How to move by code the BindingSource to a specific record
Asked Answered
P

3

7

Using datagridview bound to BindingSource control bound to a LINQ to SQL class, I wonder how to position the bindingSource to a specific record, that is, when I type a Product name in a textbox, the bindingsource should move to that specific product. Here is my code:

In my form FrmFind:

    NorthwindDataContext dc;
    private void FrmFind_Load(object sender, EventArgs e)
    {
        dc = new NorthwindDataContext();

        var qry = (from p in dc.Products
                   select p).ToList();

        FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);

        productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
    }

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        TextBox tb = sender as TextBox;

        int index = productBindingSource.Find("ProductName", tb.Text);

        if (index >= 0)
        {
            productBindingSource.Position = index;
        }
    }

In the program class:

    public class FindAbleBindingList<T> : BindingList<T>
    {

        public FindAbleBindingList()
            : base()
        {
        }

        public FindAbleBindingList(List<T> list)
            : base(list)
        {
        }

        protected override int FindCore(PropertyDescriptor property, object key)
        {
            for (int i = 0; i < Count; i++)
            {
                T item = this[i];
                //if (property.GetValue(item).Equals(key))
                if (property.GetValue(item).ToString().StartsWith(key.ToString()))
                {
                    return i;
                }
            }
            return -1; // Not found
        }
    }

How can I implement the find method to make it work?

Pavis answered 30/7, 2012 at 0:10 Comment(4)
Set the Position property. msdn.microsoft.com/en-us/library/…Lapoint
I have already checked that link, it doesn't answer my question.Pavis
Maybe this will be more helpful msdn.microsoft.com/en-us/library/ms158165.aspxLapoint
Also checked it before, this implement dataset which is not my datasource type.Pavis
I
18

You can combine the BindingSource.Find() method with the Position property.

For example, if you have something like this in your TextBox changed event handler:

private void textBox1_TextChanged(object sender, EventArgs e)
{
    TextBox tb = sender as TextBox;
    int index = bs.Find("Product", tb.Text);

    if (index >= 0)
    {
        bs.Position = index;
    }
}

This of course will depend on a lot of things like the particular implementation of the Find method the data source for the binding source has.

In a question you asked a little while ago I gave you an implementation for Find which worked with full matches. Below is a slightly different implementation that will look at the start of the property being inspected:

protected override int FindCore(PropertyDescriptor property, object key)
{
    // Simple iteration:
    for (int i = 0; i < Count; i++)
    {
        T item = this[i];
        if (property.GetValue(item).ToString().StartsWith(key.ToString()))
        {
            return i;
        }
    }
    return -1; // Not found
}

Do note that the above method is case sensitive - you can change StartsWith to be case insensitive if you need.


One key thing to note about the way .Net works is that the actual type of an object is not sufficient all the time - the declared type is what consuming code knows about.

This is the reason why you get a NotSupported exception when calling the Find method, even though your BindingList implementation has a Find method - the code that receives this binding list doesn't know about the Find.

The reason for that is in these lines of code:

dc = new NorthwindDataContext();

var qry = (from p in dc.Products
           select p).ToList();

FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);

productBindingSource.DataSource = list.OrderBy(o => o.ProductName);

When you set the data source for the binding source you include the extension method OrderBy - Checking this shows that it returns IOrderedEnumerable, an interface described here on MSDN. Note that this interface has no Find method, so even though the underlying FindableBindingList<T> supports Find the binding source doesn't know about it.

There are several solutions (the best is in my opinion to extend your FindableBindingList to also support sorting and sort the list) but the quickest for your current code is to sort earlier like so:

dc = new NorthwindDataContext();

var qry = (from p in dc.Products
           select p).OrderBy(p => p.ProductName).ToList();

FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);

productBindingSource.DataSource = list;

In WinForms there are no entirely out of the box solutions for the things you are trying to do - they all need a little bit of custom code that you need to put together to match just your own requirements.

Indistinguishable answered 30/7, 2012 at 20:36 Comment(9)
I have implemented BindingList in my project as advised before, now I pass the list to the bindingsource control before query the find. When I run your code above, I get again the exception: System.NotSupportedException was unhandled Source=System StackTrace: at System.ComponentModel.BindingList1.FindCore(PropertyDescriptor prop, Object key) at System.ComponentModel.BindingList1.System.ComponentModel.IBindingList.Find(PropertyDescriptor prop, Object key), any thought please?Pavis
So you get the same exception - you have not implemented the find method. At this point it is getting quite hard to help you, you seem to be doing lots of things that you don't tell us about then posting questions that aren't really related to the error you are getting. It you have a bindinglist that supports find then you can find and index and set the position from that index.Indistinguishable
I will review my implementation of BindingList. David, may be as you said there is something I have not mentioned here that makes this error appears. The first time I posted a question on the Filter method, and the second time I posted a question on the Find method, I see no harm in that. And I do not hide my joy to find you in this thread, because thanks to you I learned interesting things. As you are sure of what you said, then I ask you please to be patient for a moment. From my side, I am sure that with your assistance we can clarify this issue.Pavis
@AlphaBird If you still have problems getting this all to work maybe it would be better to go to chat? If you read this in the next short while I will be on chat (click the link at the top of this page) in the c# room.Indistinguishable
Sorry I came here 1 hour after you left chat, I added the used code to the initial question, so please how to implement the find method. By the way have you an idea on the BindingListView. it seems to be interesting too.Pavis
Thanks @AlphaBird - I will look at this tomorrow when I have time. I have it working on my end so we just need to find the difference and you will be going! :)Indistinguishable
@AlphaBird - I have found your problem. You use the OrderBy extension method which has a return type of IOrderedEnumerable which has no member find so your BindingSource raises the NotSupported exception. I've added a solution.Indistinguishable
let us continue this discussion in chatIndistinguishable
Thanks @David - I have integrated the OrderBy method inside the Find implementation, and now I have no more Exception. But now I get index = -1 every time I change the textbox content and the datagrid position remain the same, what means that till now Find method doesn't work.Pavis
F
2

I took a different approach. I figured, programmatically, every record must be checked until a match is found, so I just iterated using the MoveNext method until I found a match. Unsure if the starting position would be the First record or not, so I used the MoveFirst method to ensure that is was.

There is one assumption, and that is that what you are searching for is unique in that column. In my case, I was looking to match an Identity integer.

int seekID;        
this.EntityTableBindingSource.MoveFirst();
if (seekID > 0)
{
     foreach (EntityTable sd in EntityTableBindingSource)
     {
         if (sd.ID != seekID)
         {
             this.t_EntityTableBindingSource.MoveNext();
         }
         else
         {
             break;
         }
      }
 } 
Furfuraceous answered 6/10, 2016 at 20:5 Comment(0)
I
1

I didn't really care for either answer provided. Here is what I came up with for my problem:

// Create a list of items in the BindingSource and use labda to find your row:
var QuickAccessCode = customerListBindingSource.List.OfType<CustomerList>()
    .ToList().Find(f => f.QuickAccessCode == txtQAC.Text);

// Then use indexOf to find the object in your bindingSource:
var pos = customerListBindingSource.IndexOf(QuickAccessCode);
if (pos < 0)
{
    MessageBox.Show("Could not find " + txtQAC.Text);
}
else
{
    mainFrm.customerListBindingSource.Position = pos;
}
Inverter answered 14/10, 2020 at 4:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.