0

I am receiving the error "Bindingsource cannot be its own data source" sporadically when I update my datagriview from a BindingList. There is no rhyme or reason as far as I can tell when it comes to throwing the error. If anyone has some insight, it is greatly appreciated.

enter image description here

        dgv_Items.DataSource = null;
        dgv_Items.DataSource = new BindingSource(Item.ObjectsItem.OrderBy(x => x.Quality), null);

        foreach (DataGridViewRow row in dgv_Items.Rows)
        {
            try
            {
                if (row.Cells[5].Value != null)
                {
                    var cell = row.Cells[5].Value;
                    if (Uri.IsWellFormedUriString(cell.ToString(), UriKind.Absolute))
                    {
                        DataGridViewLinkCell linkCell = new DataGridViewLinkCell
                        {
                            LinkColor = Color.Blue,
                            Value = cell
                        };
                        row.Cells[5] = linkCell;
                    }
                }
            }
            catch (Exception ex)
            {
                Main.Log("Error: " + ex);
            }
        }

1 Answers1

1

The bindingsource is meant to bind the data to the displayed data. In your case the displayed data is the data grid view.

If you decide to use a binding source as the data source for your DataGridView you shouldn't handle the DataGridView programmatically anymore. Because you told the DataGridView that its DataSource would be your BindingSource, your program should only change the data by changing the data in the DataSource. Once changed, the DataGridview and the BindingSource will take care that the corresponding displayed item is updated.

Similarly if an operator changes the displayed item, the DataGridView and the BindingSource will take care that the corresponding object is updated.

Once the DataSource of a DataGridView is set, you should not change the DataGridView programmatically, but change the data in the DataSource.

You want to display URI strings, the problem is that if it changes, no one gets notified about these changes, so no one can react on the changes. The reason for this is because class string doesn't implement interface INotifyPropertyChanged

So we have to create a class of URI strings to display.

public class MyDisplayableUriString
{
   public string UriString {get; set;}
   ... // if desired other properties
}

We want a form to display the list of MyDisplayableUriString:

  • Create a new Windows Forms application
  • Add a BindingSource using the Toolbox
  • BindingSource properties DataSource = MyDisplayableUriString
  • Add a DataGridView using the Toolbox
  • DataGridView properties: DataSource = BindingSource1

You should see the fields of MyDisplayableUriString displayed in the DataGridView.

  • Use the properties windows to ass an event handler for when the form loads

In this event handler fill the BindingSource with several Displayable URi strings:

private void Form1_Load(object sender, EventArgs e)
{
    for (int i = 0; i < 10; ++i)
    {
        MyDisplayableUriString uriItem = new MyDisplayableUriString()
        {
            Uri = "URI " + i.ToString()
        };
        this.bindingSource1.Add(uriItem);
    }
}

If you run the program you should see that the table is filled with the created URI items.

Now let's change one of the items:

  • Add a button and create a handler for button click:

    private void button1_Click(object sender, EventArgs e) { MyDisplayableUriString uriItem = (MyDisplayableUriString)this.bindingSource1[0]; uriItem.UriString = "John"; }

Now if you run the program and click the button the name is not updated.

The reason for this is that your class doesn't implement INotifyPropertyChanged. Therefore bindingsource doesn't get notified when an uri item is changed. And thus the DataGridView is not updated.

Luckily StackOverflow has an article about a proper implementation: Implementing INotifyPropertyChanged

The code is as follows:

public event PropertyChangedEventHandler PropertyChanged;

private string uriName = null;
public string UriName
{
    get { return this.uriName; }
    set { this.SetField(ref this.uriName, value); }
}

protected void SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
        field = value;
        RaiseEventPropertyChanged(propertyName);
    }
}

private void RaiseEventPropertyChanged(string propertyName)
{
    var tmpEvent = this.PropertyChanged;
    if (tmpEvent != null)
    {
        tmpEvent(this, new PropertyChangedEventArgs(propertyName));
    }
}

Now whenever your program changes the object in the bindingsource the display is automatically updated, and if the operator changes the display the items in the bindingsource are updated, and your program get notified about the changes via events

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116