Windows Forms - Data Binding to a DataSource properties's property - c#

I've got a data binding source, and it has an associated property. When this source property is text/integer based etc, everything works fine. I can bind that to a text box or label okay.
Some of my properties are lists. This is also fine, I can create a new listBindingSource, point it's datasource at the origional binding source, and the datamember as the list and point my, say, listview at that. I set the DisplayMember to the property of the item in the list and it works fine.
However, if the property is neither list, or text/int, but just a single object... I'm stuck. There's no way of telling it I want to bind the properties's property to the object. There's no 'DisplayMember' to help.
An example:
I have an object MeteredSpace. It has the properties
public string Name {get;set;}
public List<MeteredSpace> ChildMeteredSpaces {get;set;}
public MeteredSpace ParentMeteredSpace {get;set;}
Then I define the following met
//underlying datasource
this.meteredSpaceBindingSource.DataSource = typeof(SEMS.LinqObjects.MeteredSpace);
//data source of children
this.childMeteredSpacesBindingSource.DataMember = "ChildMeteredSpaces";
this.childMeteredSpacesBindingSource.DataSource = this.meteredSpaceBindingSource;
Now for my controls:
//nice and easy, picking directly from the underlying data source
this.nameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text",this.meteredSpaceBindingSource, "Name", true));
//our child metered space list box is pretty easy too:
this.meteredSpaceListBox.DataSource = this.childMeteredSpacesBindingSource;
this.meteredSpaceListBox.DisplayMember = "Name";
//then we come to our parent text box
this.nameTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text",this.meteredSpaceBindingSource, "ParentMeteredSpace", true));
I have no way of pointing it at ParentMeteredSpace's Name property like I do with the list box. I also can't create a nice new binding source for it.
Anyone know of a way around this? I had a look into overriding a control and adding my own DisplayMember, but I started to think there might be a better way of doing it.
Thanks

You should be able to reference through the parent property:
Binding b = new Binding("Text",
this.meteredSpaceBindingSource,
"ParentMeteredSpace.Name",
true);
this.nameTextBox.DataBindings.Add(b);

Related

How can I bind the checked property of CheckedListBox? [duplicate]

I tried to set DataSource of CheckedListBox like this:
private void Form1_Load(object sender, EventArgs e)
{
checkedListBox1.DisplayMember = "Name";
checkedListBox1.ValueMember = "Checked";
_bindingList = new BindingList<CustomBindingClass>(
new List<CustomBindingClass>
{
new CustomBindingClass {Checked = CheckState.Checked, Name = "Item1"},
new CustomBindingClass {Checked = CheckState.Checked, Name = "Item2"},
new CustomBindingClass {Checked = CheckState.Unchecked, Name = "Item3"},
});
checkedListBox1.DataSource = _bindingList;
}
And It's working but partially. I'm able to do the fallowing later
_bindingList.RemoveAt(0);
or _bindingList[0].Name = "TestTest"; and CheckedListBox updates well except items are not checked. This is not working
_bindingList[0].Checked=CheckState.Checked;
I also tested to do it when CheckedProperty from my CustomBindingClass is of type bool, but doesn't works either. Any suggestion what should be the type of ValueMember property ?
Consider these facts:
CheckedListBox does't have a built-in data-binding support for checking items. You need to handle check state of items yourself.
You set checkedListBox1.ValueMember = "Checked";. You didn't set item check state, you just said when you select the item, the value which returns by SelectedValue comes from Checked property of your object which is behind the seected item. For example you can use this code in a Click event of a Button to see the result; regardless of check-state of items, the message box, will show value of Checked property of the object behind the item:
MessageBox.Show(checkedListBox1.SelectedValue.ToString());
Selecting and checking items are completely different.
I prefer to use DataGridView for such purpose. You can simply have a CheckBox column and a readonly TextBox column and bind DataGridView to the list of your objects.
If you need to have two-way data binding, you need to implement INotifyPropertyChanged interface regardless of what control you are using to show data. If you don't implement that interface, when changing properties on your model ListChange event will not raise and you can not see changes in UI automatically.
If you take a look at CheckedListBox class, you'll notice that DataSource, DisplayMember and ValueMember are marked with
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
This a common technique used in Windows Forms controls to indicate that some public properties inherited from a base class (hence cannot be removed) are not applicable for that concrete derived class and should not be used.
There must be a reason for doing that for the aforementioned properties of the CheckedListBox. As you already saw, it's "sort of working", but the point is that it isn't guaranteed to work at all. So don't use them. If you wish, create a helper class that holds CheckedListBox and BindingList, listens to ListChanged event and synchronizes the control.

How do I pass a grid's Datasource through to the PropertyGrid?

I have a grid wrapped in a user control. I need to pass the datasource of the grid through the control.
// My Pass-Through declaration
public object DataSource
{
get { return internalGrid.DataSource; }
set ( internalGrid.DataSource = value; }
}
So far, that's the easy part. The datasource of the grid is a plain Object. The pass-through Datasource is also a plain Object.
When I drop a datagrid directly on my form, I can set datasource by selecting one of my binding source objects on my form. However, when I include my custom control, the pass-through datasource is grayed out. I can't select the binding sources.
I'm certain that I am simply missing a property attribute, but I don't know which one(s). Any help would be appreciated.

RadControl and Observable collection of dynamic object

I have an ObservableCollection of dynamic objects that is bound to a RadGridView. I want to use the RadDataFilter control with this list. For some reason when I set the Source nothing appears as options for creating filtering rules.
You can see in the picture that the DropDown box that is supposed to have the names of the columns is empty.
I'm setting the source as follows
DataFilter.Source = Data;
Data is declared as ObservableCollection<dynamic>

How to bind a List of a DataObject to a Grid with BindingSources?

In an assembly I created a class like the following:
[DataObject(true)]
public class A
{
public int Foo{get;set;}
[DataObjectMethod[DataObjectMethodType.Select)]
public static List<A> GetAllA(string ConnectionString)
{
// return filled List<A>
}
}
Now I want to display this List with a Gridcontrol under Winforms. I though of a DataGrid.
Though I'm coming from ASP.net I'd first think of
this.dataGridView1.DataSource = A.GetAllA(ConnectionString)
Works, but I'd prefer a better databinding with BindingSources. (Because I've always heard that thats the way to go)
I managed to drop a BindingSource onto the form and set the DataSource property to class A.
But where can I set the SelectMethod and its parameters? If I set DataSource property of the dataGridView to the BindingSource, it will only display an empty line.
Is this the right way to go? Will it only require some additional clicks in the wizard, or do I need to read tons of documentation to get this working?
Edit: Is there even a way to achieve automatically binding to my select method? Or does the BindingSource only supports mapping the columns, but not actually binding the data, meaning I'm required to set the DataSource property nevertheless?
You need to create a DataSource. Click "Data" menu and select "Add New DataSource..."
Connecting to Data in Visual Studio Overview
http://msdn.microsoft.com/en-us/library/wxt2cwcc(VS.80).aspx
To connect your application to data in
a database, Web service, or object,
run the Data Source Configuration
Wizard
by selecting Add New Data Source from
the Data Sources
Window.
Public Class A
Private _field As String
Public Property Field() As String
Get
Return _field
End Get
Set(ByVal value As String)
_field = value
End Set
End Property
End Class
Public Class AListing
Inherits List(Of A)
End Class
Use AListing as the object when adding a data source. Good for grid views or detail forms that provide navigation. It is up to you to populate it.
Use A as the object when adding a data source. Good for a dialog when you only need to bind to one instance. It is up to you to populate it.
A DataSource just helps the designer configure data binding. You still have to fill the objects. If you do not care about designer support, calling as you do is fine. Using a BindingSource just allows you to use an object like a "data table". Using your example, if I use a BindingSource, I could handle the CurrentChanged event for any additional processing.
this.dataGridView1.DataSource = A.GetAllA(ConnectionString);
//-or-
this.bindingSource1.DataSource = A.GetAllA(ConnectionString);
Have class A retrieve the connection string from the configuration file rather than as a parameter on the GetAllA method. Once your method has no parameters it should be possible to select it in the wizard.

How to remove selected items from ListBox when a DataSource is assigned to it in C#?

How to remove selected items from ListBox when a datasource is assigned to it in C#?
When trying to remove, got error
"Items collection cannot be modified when the DataSource property is set."
But when i try to remove item from datasource (datatable) ,
it thorws error as "datarow is not in current row collection".
Find that item in the DataSource object and remove it, then re-bind the ListBox.
EDIT:
Here's how you delete from a DataTable as your DataSource, regardless of the .NET version.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
dt.Rows.Remove(rowView.Row);
I haven't tried with anything other than WinForms DataGridViews, but I highly recommend BindingListView, which is both faster than DataTables/Views and allows you to bind generic List<T>s as your DataSource.
Alternatively, use a list that implements IBindingList or inherits from BindingList. When objects are added or removed from a Binding List, any controls bound to it are automatically notified of the change and will update themselves accordingly. If you are using BindingList and your class also implements INotifyProperty changed, Any changes to class properties will also be updated automatically in the databinding control. For example, if a column in a datagrid(view) is bound to a property, "Name", and you change "Name" in the datasource, the datagrid will automatically update. If you add a new item to the datasource, the datagrid will update automatically. Binding List also supports notification in the other direction. If a user edits the "Name" field ina datagrid, the bound object will be updated automatically. Going off topic slightly, if you go a little further and impliment "SupportsSortingCore" and the associated methods in BindingList, you can add automatic sorting to your data. Clicking on a columnm header will automatically sort the list and display the header sort direction arrow.
If the ListBox has a datasource assigned, you must remove items from the datasource and then rebind the ListBox
You need to modify the data source rather than the Items collection of the control. Depending on what kind of data source you are binding to, there are going to be different things you have to do so that your UI updates.
The best way is find a collection that fits your needs and implements IBindingList or IBindingListView. Those two interfaces implement even handlers that listen for a CollectionChanged event and update your UI accordingly.
If your collection doesn't support those interfaces, you're going to have to re-bind your data source every time somebody adds/removes an item.
when you get the message "Items collection cannot be modified when the DataSource property is set."
setting the datasource to something else, empty list or null does not help when
the code initializecomponent is not completed.
to avoid that error, one must do the change of datasource or the item list during or after form load.
I know it does not seem to make sense. Hoever, the visual studio designer will generate code in the form designer.cs or vb that will add items to the listbox if any code that changes the items is found before end of initialize components
While Chris Doggett posted a valid solution, I ran into problems while using it. By using that method it was not allowing a subsequent GetChanges(DataRowState.Deleted) to work properly.
To better solve my problem, I only had to change a single line - the last line.
DataRowView rowView = listBox.SelectedItem as DataRowView;
if (null == rowView)
{
return;
}
rowView.Row.Delete();
This allowed my GetChanges call to work properly.
This worked for me
DataTable temp = (DataTable)lstBlocks.DataSource;
temp.Rows.RemoveAt(position);
its vary simple , assign a new blank value to listbox
eg..
Dim ABC As New List(Of String)()
ListBox1.DataSource = ABC
ListBox implementation is bugged, you need to create a new data source instance for the component for it to recognize a change.
Eg:
ActivitiesList.DataSource = _activities;
_activities = new List<Activity>(_activities);
_activities.Remove((Activity)ActivitiesList.SelectedItem);
ActivitiesList.DataSource = _activities;

Categories