简体   繁体   中英

What is a good way to allow two-way binding to a IQueryable with a DataGridView?

I've tried to search for a solution to this issue, but haven't been able to find anything that has helped.

I'm trying to bind the results of a LINQ-to-SQL query to a DataGridView in such a way that I can add new rows and edit existing rows which will then update the backing database accordingly. Simply doing the following creates a read-only table:

myDataGridView.DataSource = from item in myDatabase.Parts
                 select new
                 {
                    item.Name,
                    item.Description
                 };

In actuality, the query is a slight bit more complex, with joins and such.

What I'm thinking of doing is creating a customer class which represents the anonymous type being returned by the select, convert the query to a list using the ToList() method, then wrapping it into a class which will handle the appropriate events of the DataGridView when put into VirtualMode , handling the appropriate database queries as needed.

I'm just hoping that there is a better option.

As you've seen, when you do the new you create an anonymous type and lose the ability to have two way databinding, since it is immutable.

According to this MSDN article you can get two way databinding out of the box with Linq-to-SQL when linq can "find the underlying Table" I'm not sure if that rules out basic joins but I suspect that it does.

In this question Linq to SQL and concurrency with Rob Conery repository pattern I discusses a pattern presented by Rob Conery that I was using, which gave two way binding over objects and allow joins and such, but introducing a domain object that sat on to of the DataContext objects - though as I point out in the question, doing it this way kills concurrency tracking, and you need mapping code to get from the domain objects back to the DataContext objects.

That sounds similar to what you care considering, and at least saves you needing to go the the extent of Virtual Mode.

I haven't had a chance to work much with Entity Framework 4 but it is possible that the story there is better for this situation. When I was looking at this in Linq-to-SQL a year or two back I never did find a great solution (which of course doesn't mean that there isn't one!)

After spending some more time on the issue, I began looking more at the BindingList and BindingSource classes. A fairly nice solution to my problem was to do something similar to what I was proposing in my question, but using a custom BindingList object.

public class PartListBindingList : BindingList<PartListBindingItem>
{
    private readonly MyDatabase _Database;
    private bool _IsInitialized = false;

    public PartListBindingSource(MyDatabase database)
    {
        _Database = database;

        foreach (var part in _Database.Parts)
        {
            var item = new PartListBindingItem()
            {
                PartID = part.PartID
                Name = part.Name,
                Description = part.Description
            };

            Add(item);
            BindItem(item);
        }

        _IsInitialized = true;
    }

    protected override void OnListChanged(ListChangedEventArgs e)
    {
        base.OnListChanged(e);

        if (!_IsInitialized) return;

        switch (e.ListChangedType)
        {
            case ListChangedType.ItemAdded:
                // Add new item to database
                break;

            case ListChangedType.ItemDeleted:
                // Add new item to database
                break;
        }
    }

    private void BindItem(PartListBindingItem item)
    {
        item.Changed += (s, e) =>
        {
            // Update the entry in the database.
        };
    }
}

public class PartListBindingItem
{
    public event EventHandler Changed;

    private int _PartID;
    private string _Name;
    private string _Description;

    public int PartID
    {
        get { return _PartID; }
        set
        {
            _PartID= value;
            OnChanged();
        }
    }

    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            OnChanged();
        }
    }

    public string Description
    {
        get { return _Description; }
        set
        {
            _Description= value;
            OnChanged();
        }
    }

    private OnChanged()
    {
        var changed = Changed;
        if (changed != null)
        {
            changed(this, EventArgs.Empty);
        }
    }
}

This skeleton design can be adapted to work for more complicated database bindings.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM