In order to get data binding in WinForms (to a DataGridView, for instance) to work anything like you'd hope and add/delete rows as the collection changes, you have to use a BindingList (or DataTable) instead of a generic List. The problem is, almost nobody has the first instinct to code with a BindingList instead of a List in their libraries.
The BindingList implements two events that the List doesn't have and these must be the difference in data binding action (also, a property to suppress the second event):
AddingNew
ListChanged
RaiseListChangedEvents
Similarly, the DataTable has two events which probably enable similar functionality:
RowDeleted
TableNewRow
EDIT: As the helpful SO community pointed out here and in another article, a List can be converted (maybe more accurately encapsulated?) by calling the correct BindingList constructor:
BindingList<MyType> MyBL = new BindingList<MyType>();
MyList.ForEach(x => MyBL.Add(x));
My situation is a little more complicated as illustrated by the code below.
EDIT Added INotifyPropertyChanged stuff that must exist in the real library.
public class RealString : INotifyPropertyChanged
{
private int _KnotCount = 0;
private List<KnotSpace> _KnotSpacings = new List<KnotSpace>();
public RealString()
{
KnotSpacings.Add(new KnotSpace());
}
public int KnotCount
{
get { return _KnotCount; }
set
{
int requiredSpacings = 0;
_KnotCount = value;
// Always one more space than knots
requiredSpacings = _KnotCount + 1;
if (requiredSpacings < KnotSpacings.Count)
{
while (requiredSpacings < KnotSpacings.Count)
{
KnotSpacings.Add(new KnotSpace());
}
}
else if (requiredSpacings > KnotSpacings.Count)
{
while (requiredSpacings > KnotSpacings.Count)
{
KnotSpacings.Remove(KnotSpacings.Last());
}
}
this.OnPropertyChanged(this, "KnotCount");
}
}
public List<KnotSpace> KnotSpacings { get => _KnotSpacings; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(object sender, string PropertyName)
{
if (this.PropertyChanged == null) return;
this.PropertyChanged(sender, new PropertyChangedEventArgs(PropertyName));
}
}
public class KnotSpace
{
private double _Spacing = 10;
public double Spacing { get => _Spacing; set => _Spacing = value; }
}
The things in the list are displayed in the UI, and the properties of the things in the list are modified in the UI, but the UI doesn't directly add/remove things from the list except by changing the KnotCount property. Wrapping the KnotSpacings property in a BindingList doesn't result in the BindingList updating when KnotSpacings is updated by changing the KnotCount property.
EDIT OK, more clarification...
BindingList BL = new BindingList<KnotSpace>(MyRealString.KnotSpacings);
DataGridView1.AutoGenerateColumns = true;
DataGridView1.DataSource = BL;
NumericUpDown1.DataBindings.Add("Value", MyRealString, "KnotCount", false, DataSourceUpdateMode.OnPropertyChanged);
The BindingList has no more success tracking the changes to the underlying List property (KnotSpacings) than the Windows controls. So data binding the controls to the BindingList doesn't accomplish much. BindingList works great if UI adds/removes items from the BindingList because it does the same operations in the underlying List. But then I would need to replicate the add/remove action and logic of the library in my UI and that's a breaking change in waiting.
EDIT Major changes made to my original post attempting to: (1) Clarify the problem. (2) Distinguish it as not a duplicate question (although one of the several questions was a dup). (3) Acknowledge the helpful efforts of others that would be lost if I deleted the post.
First Off, there is a better way to pass a List<T> to a BindingList<T>. BindingList<T> has a constructor that accepts a List<T> which copies the List's elements into the BindingList, like so:
List<int> myList = new List<int>();
BindingList<int> myBindingList = new BindingList<int>(myList);
But that's not your question, really. To answer your question simply - Correct, List<T> is not a good choice for two-way binding in WinForms. As List<T> does not have any events notifying for elements added, you can really only guarantee a one-way binding - data entry may work, but things break down when trying to refresh on, say, items being added to the List.
That said, you mention that these libraries are modifying a List<T> that you have access to during the modifications. I would argue that a good Library would use the Interface pattern to use, modify, and pass collections. Although List<T> and BindingList<T> are very different classes, they both implement IList<T>, ICollection<T>, and IEnumerable<T>. So any function which accepts any of those interfaces as a parameter would accept either a List<T> or a BindingList<T> (for example: public void DoSomethingWithCollection(IEnumerable<int> collection) could accept List<int>, BindingList<int>, or any other collection that implements IEnumerable<int>). The Interface pattern is a well-known standard at this point in C#'s lifespan, and though nobody's first instinct would be to use a BindingList<T> over a List<T>, their first instinct should absolutely be to use an IEnumerable<T> (or IList<T> or ICollection<T>) over a List<T>.
Where possible, it would be better for binding to pass your List to the BindingList's constructor, then never use the List again - instead, use the Add and Remove methods of the BindingList to manage it's internal collection.
If you use the BindingList<T> constructor that accepts an instance of IList<T>, then that instance is used to back the BindingList<T>, and changes in the IList<T> are reflected in the BindingList.
That's not the end of the story, however. WinForms databinding is structured in such a way that, the further away you get from simple, single-property 2-way binding, the more things you have to cover yourself.
For example, the INotifyPropertyChanged interface is implemented by classes that are used as a data source to notify of a change in a child property (like your KnotCount property).
For more complex scenarios, one would not use BindingList<T>, but would derive a class from it and override one or more of the data binding mechanisms. Ditto for the BindingSource class.
There is a lot of boilerplate behind the data binding mechanism, but almost every portion of it is open to derivation in order to customize the behavior. It is sometimes useful to draw out an object graph of the classes and interfaces used in data binding (lots of reading the documentation involved) to give yourself a good mental overview of the whole process.
Related
I've been looking at the difference between a BindingList and an observablecollection and List. From what I've read, it seems like the BindingList is the only collection type that will notify if an object in it has one of its properties changed. I cannot get this to work.
I have a property on a ViewModel called Matches, which returns a BindingList created out of a list of CarMatch objects in another class. (Cars m_Cars = new Cars();) My DataGrid on the View is bound to this Matches property in the VM.
public BindingList<CarMatch> Matches
{
get
{
Return new BindingList<CarMatch>(m_Cars.Matches);
}
}
Now, in the code I change one of the CarMatch object's properties, say.. automaticTrans = true from false. Matches[0].automaticTrans = true. I want to see that change in the DataGrid. Without implementing INotifyPropertyChanged inside of the CarMatch class, is there a way to update the datagrid from the viewmodel? Using INotifyPropertyChanged on Matches does not seem to do it. There is something about this I just don't understand, and could use an example to look at.
CarMatch (not Matches) has to implement INotifyPropertyChanged. But consider using ObservableCollection unless you really need some of the additional scenarios offered by BindingList: with ObservableCollection, INotifyPropertyChanged comes for free. And, more importantly, BindingList doesn't scale well.
try
dataGrid.Items.Refresh();
but keep in mind that is a expensive call if you have lots of data and you call it several times in a short period of time.
In WinForms controls like a TextBox have property Modified that gets value "true" after changing the control's content and may be set to "false" manually. Their WPF analogues seem not to have such property (neither IsModified in new naming style). So do I have to handle their modifying events myself or there's some more convenient way?
For example I have few textboxes and a function, which combines their contents into one document for preview. Opening the preview I want to keep an old content for the document, if none of the textboxes was changed or to call the function to produce new document's content if at least one textbox was edited.
In WPF it's easier to control everything through ViewModel/Model... This might be too much/not what you're looking for. But through experience, I feel that the pattern below pays off in easy usage.
Wrap your simple data class (with all the properties that it is using now/in your question now) in a class/Model that implements IEditableObject, INotifyPropertyChanged and possibly IEquitable. Lets call your class Data.
In a wrapper class create fields:
Data _current;
Data _proposed;
Data _previous;
IEditableObject requires you to implement BeginEdit(), EndEdit() and CancelEdit().
in them you need to control the state _current, proposed, and previous. For example,
public void CancelEdit()
{
_current = _previous;
_proposed = null;
}
public void EndEdit()
{
_previous = _proposed;
}
public void BeginEdit()
{
_proposed = _current;
}
You might need more logic in methods above, so this is just an example. The key of knowing if your object has changes is implementing a flag, lot's of people call it IsDirty:
pubic bool IsDirty { get { return _current != _previous; } }
Now the user of this class can easily check the state. Oh, and on more thing each property would have the following mechanism:
public string Example
{
get { return _current.Example;}}
set
{
if(_current.Example == value) return;
BeginEdit();
_current.Example = value;
RaisePropertyChanged (() -> Example);
}
}
What's nice about implementing IEditableObject, all controls respond to it, DataGrid is a good example and also you can easily return to the original state by cancelling edit.
Anyway, there are lots of samples that you should browse for. I just hope to can get you started onto that path...
P.S. this pattern was used before WPF came out, its super common in WinForms as well
WPF doesn't have that because UI is not Data and therefore your UI is not the right place to store information about whether your data has changed or not.
Crappy dinosaur winforms doesn't allow a clean and true separation between UI and application logic/data and therefore has all sorts of horrible hacks in order to mash together these completely separate concepts.
You must learn to develop correctly, using the MVVM pattern. Then you will realize there's no sense in placing state data on any UI elements.
What I did before was make a deep copy of the data object, then write a generic compare method that uses reflector to compares if there is difference betweens the two objects.
So say if I have a SaveButton, a TextBoxA binded with a ViewModel.PropertyA, initialy PropertyA is = "123".
When user typed "1234" in TextBoxA, the PropertyA set method will executes the compare method to find the difference. And enable the Save Button.
But when the user changed the text "1234" back to "123", the Save button will disabled again.
After 1 year, now I wonder is there a better way or easier way to do it?
i.e. Is there any framework that will do this kind of stuff? So I don't have write code for deep copy the object, write compares method myself?
The actual UI I had was not that simple only contains TextBox type, that was a UI for edit customer information, thus have DateTime, Collection etc. That's why I wrote deep copy method for cloning the whole object.
Assuming that these properties on your View Model are raising the PropertyChanged event somehow since the question is tagged with MVVM.
Here's one approach. Write an event handler for your ViewModel's PropertyChanged event. Save original values in a private Dictionary<string, string> only when a property changes. That prevents the need for copying the whole object just in case someone makes an edit. If the property already exists in the dictionary, then you could easily determine if it's been changed back to its original value.
Edit: Oh, I was thinking that the PropertyChangedEventArgs contained new and old values, but it doesn't. So, in order to do this, you'd need to add some extra method call within your View Model's property setters that can evaluate the old and new values of each property.
In order to easily set up enabling and disabling the Save button, there should be a bool property in your view model to which you would bind the Save button's enabled property.
If items are removed from the dictionary whenever the new value matches the original value, then your Save button enabled property could just return true if the dictionary contains any items.
Edit 2: For the collection types, you'd want to have your View bind to ObservableCollection properties on your View Model. The Collection changed event does give you a list of old and new items, so keeping track of the changes within that event handler should be fairly easy.
if the ViewModel is your own object and you can modify it, implement the ICloneable interface so you can make a copy of it.
Next implement the IComparable interface on it, where T is the view model. So its easy to compare
Then I suppose you'd have to make an PropertyChanged event for all properties, and when one is fired make the compare.
I guess its pretty much the same as what you already have now, but if you write the logic based on ICloneable and IComparable at least you only have to write it once
edit: and if you do simply do not want to write your own compare method, there are snippets that automatically compare all properties, such as this post. However, using something like that is a lot slower (performance wise) than writing your own compare function.
Sounds like you want state management combined with property change notifications. The state management is really up to you to with how you want to do it. The few concepts that make sense are to use either a backup copy of the object or a Dictionary<string, object> that maps the original properties (property name) to underlying fields (property values).
As for determining if there are any changes, I would utilize the INotifyPropertyChanged interface. This would keep state management and notifications internal to the class. Just implement a wrapper (good practice) called OnPropertyChanged(string propName, object propValue) that sets a boolean array/dictionary (Dict<string, bool>) that would then set whether there are any changes, with a HasChanges property returning true if any properties are changed. Example class:
public class TestClass : INotifyPropertyChanged
{
private Dictionary<string, object> BackingStore = new Dictionary<string,object>();
private Dictionary<string, bool> Changes = new Dictionary<string, bool>();
private string _testString;
public string TestString
{
get { return _testString; }
set { _testString = value; OnPropertyChanged("TestString", value); }
}
private bool HasChanges { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public TestClass(string value)
{
_testString = value;
SaveValues();
}
public void SaveValues()
{
// Expensive, to use reflection, especially if LOTS of objects are going to be used.
// You can use straight properties here if you want, this is just the lazy mans way.
this.GetType().GetProperties().ToList().ForEach(tProp => { BackingStore[tProp.Name] = tProp.GetValue(this, null); Changes[tProp.Name] = false; });
HasChanges = false;
}
public void RevertValues()
{
// Again, you can use straight properties here if you want. Since this is using Property setters, will take care of Changes dictionary.
this.GetType().GetProperties().ToList().ForEach(tProp => tProp.SetValue(this, BackingStore[tProp.Name], null));
HasChanges = false;
}
private void OnPropertyChanged(string propName, object propValue)
{
// If you have any object types, make sure Equals is properly defined to check for correct uniqueness.
Changes[propName] = BackingStore[propName].Equals(propValue);
HasChanges = Changes.Values.Any(tr => tr);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
For simplicity sake, I just use SaveValues/RevertValues to save/undo changes. However, those can easily be used to implement the IEditableObject interface (BeginEdit, CancelEdit, EndEdit). The PropertyChanged event can then be subscribed to by whatever form the objects are being bound in (or even to an underlying BindingList, that way only a single instance needs to be subscribed to), which checks for the HasChanges flag and sets the appropriate state of the form.
I am trying to understand the difference between the following 2 examples.
First, this is how I currently assign Data to a control in my WinForm App.
lkuCounty.Properties.DataSource = Person.CountyList();
lkuCounty.Properties.PopulateColumns();
lkuCounty.Properties.DisplayMember = "CountyName";
lkuCounty.Properties.ValueMember = "CountyID";
lkuCounty.Properties.Columns[0].Visible = false;
lkuCounty.Properties.Columns[2].Visible = false;
lkuCounty.Properties.Columns[3].Visible = false;
This seems to work though I'll admit that if it is slightly off I probably lack the experience to tell just by looking at the code. Also of note, Person.CountyList() actually returns a DataTable :\
Now how all of the examples I find seem to say I should do this.
memberBindingSource.DataSource = Person.CountyList();
lkuCounty.Properties.DataSource = memberBindingSource;
lkuCounty.Properties.PopulateColumns();
lkuCounty.Properties.DisplayMember = "CountyName";
lkuCounty.Properties.ValueMember = "CountyID";
lkuCounty.Properties.Columns[0].Visible = false;
lkuCounty.Properties.Columns[2].Visible = false;
lkuCounty.Properties.Columns[3].Visible = false;
Is there a benefit to using the BindingSource? Is there a negative to doing it the OTHER WAY?
For context, this is a WinForm CRUD app in C# using SQL 2005.
For standard DataSets/DataTables, the BindingSource merely provides another layer of indirection between your controls and the actual data source.
However, most data-aware controls can only be bound to certain data sources (those implementing IList, IListSource, IBindingList or IBindingListView). This presents a problem if you need to use a custom object as a data source, because said object then needs to implement at least one of those interfaces.
So you can either implement the entire IList interface in your business object - or you could inherit your object from the List class and bind it up to a BindingSource, which you then bind to your Control(s).
The long and the short: unless you're certain your data sources will always be DataTables and the like, use a BindingSource. It does add a slight performance overhead, but it can make your life a lot easier.
There is also some very nice state-management functionality built into the BindingSource, which comes in very handy if your application is stateful. Instead of you writing custom state-handling code, just let the BindingSource handle things for you!
You can bind directly to any object, as in the first example. However, that object will need to implement many of the data binding interfaces for it to respond intelligently to the events fired by the control.
For example, bind a List of Person to a DataGridView. Now, click a column header to sort a column. It does not work because List does not implement the needed interface. Try the same thing with a DataTable. The column sorting magically works. That's because DataTable implements all the needed interfaces for Data Binding.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
//does not sort...
dataGridView1.DataSource = new List<Person>
{
new Person{ Age=11, Name="Jimmy" },
new Person{ Age=12, Name="Suzie" }
};
}
You can write your own classes that implement the data binding interfaces. It's alot of work. Here's a great book on the subject:
Data Binding with Windows Forms 2.0: Programming Smart Client Data Applications with .NET
I would like to display items from a Queue in a Gridview in Windows Forms. I can set the datasource attribute of the Gridview to the Queue, but it won't be automatically updated. I know I can use the BindingList class, but then I lose my Queue functionality.
Is there any way to combine the two classes, or do I have to implement one of the behaviours in a derived class?
What I'm doing is processing a list of items, I want to show the remaining ones in a grid. The data should not be changed by the user, but I want the GridView to be updated as the contents of the Queue change.
Example:
In form:
Proccessor pro = new Processor();
gridview.DataSource = pro.Items;
In class:
class Proccessor {
Queue<DataBlock> _queue = new Queue();
public Queue<DataBlock> Items {
get {
return _queue;
}
}
public void AutoProcess() {
while (_queue.Count > 0) {
Process(_queue.Dequeue());
}
}
private void Process(DataBlock db) { ... }
}
The whole purpose of a Queue is that entries can only be added in one place. So the idea of binding this to a UI grid so it can be updated is, uh, interesting - how should the UI look?
You'll definitely have to consider your own custom collection, or as you say, derive from BindingList and handle e.g. CancelNew accordingly. See the MSDN article for details.
I would subclass Queue as QueueForDisplay. The constructor would take a view control. I would override the Enqueue and Dequeue methods. In those overrides, I would update the view control. If you don't like the tight coupling, you could simply subclass Queue as QueueWithEvents and provide OnEnqueue and OnDequeue events.