I used an application recently that was awe-inspiring. All the forms inherited from their own classes and a 'form' came with an amazing amount of functionality and looked the nuts.
What I'm interested in is this 'feature' of the form. This was a C# WinForms project and blew me away.
The forms were bound to objects that the group had written to support two-way data binding (to an extent). The way they behaved was pretty simple:
The data input forms all had controls that inherited from textbox and these were bound to properties of an object, Entering data immediately validated it and the box was a light pink if validation failed and a light green if it passed. If the box ever turns blue this actually means the value in the database the form is bound to has changed and your changes to the controls were immediately saved when valid values entered. It was the case that sometimes a section of controls had to be filled before a save occured. But it was all automatic. You could stop at any point and come back later and continue without actually saving yourself.
And like I say if someone else is editing the same record values they change caused your textboxes to become blue and you knew you needed to reload the screen to see up to date information.
All of this came from using their own form class they had written and their own textbox controls bound to an objects property.
I'm mainly wondering how the hell did the object figure out the value had been changed by someone else. It surely isn't polling the database. This system was amazing. The brilliance didn't stop there.
For simplicity. How might I create an object or collection of objects to mimic the bahaviour. I'm not going to but I can't even see how.
Thanks
I'm pretty sure that anything involving other people's changes would have needed to hit the database. For two-way binding, all you really need is change notification - i.e. INotifyPropertyChanged (or a FooChanged event for every Foo property). This is all abstracted into TypeDescriptor - i.e. any binding that uses the regular PropertyDescriptor implementation (which it should) will know about notifications via SupportsChangeEvents, AddValueChanged and RemoveValueChanged.
For validation - IDataErrorInfo is your friend; by implementing this, you can volunteer validation information (which is used and displayed by several controls, such as DataGridView). i.e.
IDataErrorInfo dei = obj as IDataErrorInfo;
if(dei != null) { // supports validation
string err = dei["PropName"]; // or .Error for overall status
bool clean = string.IsNullOrEmpty(err);
}
Note that an alternative approach would be to have a Color property on the data aobject, and bind that directly to the textbox etc.
Related
I have a C1DataGrid with one column and I want to be able to validate the data when I commit a new row in the grid.
So far I tried to throw an exception in the setter of this property.
This validates the data while I am typing it in the text box correctly (throws an exception), but I am still able to commit the new row.
Furthermore I would like to only do the validation when I commit my new row and not after every new character I write.
Could someone show me how to do it? Many thanks!
You should implement inotifydataerrorinfo
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifydataerrorinfo?view=net-7.0
The easy way to do that is use the community mvvm toolkit
Inherit the viewmodel you're using for each item from Observablevalidator
Add your validation attribute(s), or custom validation
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/observablevalidator
You could then check IsValid or HasErrors in the CommittingEdit event and stop it committing
https://www.grapecity.com/componentone/docs/wpf/online-datagrid/C1.WPF.DataGrid.4.5.2~C1.WPF.DataGrid.C1DataGrid~CommittingEdit_EV.html
The datagrid itself might automatically check HasErrors, I'm not familiar with C1Datagrid
Bear in mind that what inotifydataerrorinfo is doing is telling you your viewmodel has bad data in it. You then have to do something about it. Revert the change from a cached version or something.
It is because of this that I would usually keep any "original" OK data.
Have the user edit a copy of any item separately from that and then only replace that original item ( or add a new one to a collection ) if it is definitely valid.
With a datagrids where the user can just edit like it's excel, you're better stopping them actually entering any bad data.
I only use this for quite simplistic scenarios like only entering integers or some such. The way I handle that is with an attached behavior which will essentially just not let the user type or paste invalid data in. There are obvious inherent limitations to this approach.
Another thing to consider is a binding ValidationRule.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-binding-validation?view=netframeworkdesktop-4.8
These work as the user enters and will stop invalid data transferring to the viewmodel property. You still have bad data in the datagrid though. So the user types bad stuff, the cell should respond and turn red or whatever. But you still have your bad stuff there in your view.
I am new to MVVM (and a bit of WPF) and I've read many Code Project articles, blog posts and Stackoverflow questions in the last few days. I have the impression that data binding works really nice to display data once loaded from a data source (database) and to keep the model in sync with the view, also updating all other appearances of the model data in the view(s).
But I still have no real clue how saving, and before that validating, is supposed to work right. I have a strong Windows Forms and ADO.NET background. I am well familiar with coding both the database access layer and the view updating. Back then, you had temporary, scratch data, being edited, only in the view, and the last saved version of the data in the model classes and the database. The model classes were usually in sync with the database. It was the view that contained the data that was not saved yet.
You also had a Save button that would read all data from the controls, validate it in code and either accept and save it to the model and database, or not update the model and display an error message instead. In case of an error, the user input remained in the UI for the user to correct it. But no other part of the application could see it. And you had a Cancel button that would just throw away that part of the view that contains the editing controls - the model was still valid and unchanged.
Now with data binding and ViewModels just exposing data from the Model classes, what is entered in a TextBox immediately goes into the model, be it correct or not. IDataErrorInfo is nothing more than that - informational. You can regard it or ignore it. The only hard validation that is enforced is type conversion: you can never update a non-numeric string into a numeric model field. But that's about it. I'm going to solve that by letting the ViewModel do all validation and throw an exception from the property setter on invalid data. This is the only way to implement the known behaviour.
But where does saving and discarding of data go? When will I actually write data back to the database? Does every leaving of a TextBox cause a database write so that I won't need an explicit Save command anymore (and only get reverting through Undo)? When will I validate a whole record of data? How will I deal with the model and database being out of sync, with invalid input immediately propagating through the whole application and all views thanks to data binding? When and how can I discard any user input with a Cancel button, leaving the model unchanged - or reverting it to a state before that editor dialog was opened?
I feel like MVVM does not provide solutions to these elementary problems. Did I just miss them or do they really not exist? If MVVM is not a solution to this, then what is? Or should MVVM better not be used for data editing applications in WPF?
MVVM doesn't answer these problems for you - you have the flexibility (power? burden?) to resolve database writing in any fashion you choose. If you need to collect all the data before you save back to the database, you can do that - just add a Save button bound to a SaveCommand on the ViewModel, which executes your data access stored procedure/entity framework update method/whatever. If you want to record each bit of data individually, then you'll need to call the data access procedures somewhere else (probably the property setters on the view model?)
Essentially, MVVM is not a complete end-to-end software pattern. It is only looking at the communication between what the user sees (listboxes, textboxes and buttons) and the application itself. Your data access code, serialisation, storage, persistance, whatever you are using and however you are using it is all held behind the MVVM side of the application, in your application code (the model). You can write this however you like.
I'm currently writing an application where a user fills out a form and hits Save or Cancel. Save and Cancel are both buttons bound to commands on the ViewModel. In the ViewModel constructor, the properties of a model object are copied to the properties of the ViewModel. On Save, the properties of the ViewModel are copied back to the model's properties and the data access code is initiated. On Cancel, the model's properties are copied back over the ViewModel's properties.
class MyViewModel
{
public MyViewModel(MyModel model)
{
this.Name = model.Name;
this.Colour = model.Colour;
}
public string Name {get;set;}
public string Colour {get;set;}
// commands here that connect to the following methods
public void Save()
{
model.Name = this.Name;
model.Colour = this.Colour;
model.SaveToDatabase();
}
public void Cancel()
{
this.Name = model.Name;
this.Colour = model.Colour;
}
}
That's a simple way to do it - of course, you need to throw in INotifyPropertyChanged and the rest of it, and there are other options. But I find this method easy to understand what is going on and a good base to add anything else that you may need to chuck in.
With MVVM, you bind controls in your view to properties and commands in your ViewModel. The Model represents your database.
Validation of user input can be done in several ways. You can restrict textboxes to only accept certain data, you can validate data in property setters etc etc.
I'm no database expert, but I would collect the information in my VM and add a save button somewhere in the view which validates data and writes it to the database. A cancel button could overwrite the VM properties with the (unchanged) values from the model (database).
(edit: what Pete says :)
I think you are confusing ViewModel with database, your view is binded to an object in the ViewModel, when leaving a TextBox, whatever in the TextBox will be assigned to the object of ViewModel, which is still in memory. You will need a save button, MVVM is just a pattern to separate traditional view and code behind for decoupling and easy unit testing. You still need to perform data validation after save button is clicked. For discard user input and revert to previous state, you could either save your ViewModel data before editing, or use Entity Framework to track and manage data changes.
How is data binding in C# WinForms supposed to work when you have a Save button? I don't want the data updated until I press Save!
I have two forms (list and detail) backed by a BindingList<T> collection and my custom object from that collection, respectively. I can bind each form to the list or object appropriately. However, any changes made in the detail form are immediately reflected in the list form - I don't want to save the changes and update the details shown in the list until the Save button is pressed.
Is data binding designed to support this? Is there a common pattern for doing so?
Whichever way I look at it, binding doesn't seem to be able to support this scenario. I've considered the following:
Pass a clone of the object to the detail form, but then I have to reconcile the changes on Save - changes may have been made to the copy in the list in the meantime.
Implementing IEditableObject and calling EndEdit on save almost works as I can prevent the list being notified of the changes made until Save is pressed, but if something else causes a refresh the list is updated with the interim data.
I'm currently left with dispensing with data binding in my detail view, and doing it all manually. Which is rather annoying.
Data binding really wasn't designed for this kind of thing, unfortunately.
The best solution we've found here is to edit object clones, then reconcile the changes, as you mentioned. You may want to come up with a custom collection and/or object interface that helps you manage this type of thing.
Even if the cloning method is slightly more work up front, it's going to be wayyyy less complicated and frustrating than using IEditableObject trying to catch all the possible events that update the data. Not only that, it's a more straightforward approach, and you won't end up with spaghetti code.
If you are set on using a binding list, your best bet would be to implement IBindingList to create the functionality that you desire. It may also be possible to pull this off by simply inheriting from BindingList and overriding the appropriate methods to change the binding list's behavior.
http://msdn.microsoft.com/en-us/library/system.componentmodel.ibindinglist.aspx
If you are not set on using a binding list, it is probably best to do the data manipulations manually based off of the control's events.
Best of luck.
I have application full of various controls databound to my classes. I would like to ask user "You are closing application and you made some changes. Do you want to save your changes?". For this I need to recognize that user made any changes.
How to catch user made changes in databound controls? Is textBoxXXX_TextChanged the only way to do this?
Thanks in advance for all your answers.
It depends on the datasource; for example DataTable and DataSet sources contain the GetChanges() methods which allow you to easily see if rows have been added/removed/modified. Other data sources will have their own implementations, if any. If there is no implementation then it's up to you to determine how to check for those changes.
In any event this is something you should do at the data-level, not the UI (by watching for "changed" events). Watching events doesn't scale beyond a couple controls and maintenance can be iffy.
Update: Not sure why I didn't think of it, but a second option is to add a BindingSource to your UI object and use it as a databinding proxy (your UI controls databind to the BindingSource and the BindingSource binds to the real datasource). It provides a better approach than handling all your individual "Control_Changed" events, and requiring rework of your other layers (esp. if they aren't custom data-types).
You need to provide custom logic for that, there's not really an automatic way of doing this. As I see it there are several options:
At the start of the editing, save a copy of the original data object, and when you need to check, compare the current object with the saved one. The comparison can be custom (field by field) or semi-automatic by use of serialization (compare the serialized forms) - if it is serializable.
In each of your data object's property set accessors, test for a change in value and mark the object as 'dirty'.
As been discussed, there are many ways to do this depending on how granular you want to get.
A relatively easy way using client side javascript would be to do something like the following:
Hook into the onchange events of the form elements. You could do this dynamically on page load using javascript/DOM.
When the onchange error handler is called, you could set a page level variable: pageHasChanged = true;
Hook into the page's beforeonunload event (occurs when the user tries to navigate away from the page) and check the pageHasChanged variable to see if any changes were made. If changes were made you could alert the user.
This doesn't give you the detail of what changed, but would be fairly easy to modify to track which form elements changed.
I am pretty new to C# and .NET and I'm strugling a little with the whole concept of databinding. What I am asking for is a quick rundown of the concept, or even better, point me towards sources on the net (preferred) or in print that can help me get my head around the idea.
EDIT:
I do my development in vs2008 and we are using winforms
Well, what architecture are you using? winforms? asp.net? wpf?
The high level is that if you have objects such as:
public class Person {
public string Name {get;set;}
public DateTime DateOfBirth {get;set;}
}
Then you can get the binding framework to do all the hard work, and you just say what you want bound - for example (winforms):
txtName.DataBindings.Add("Text", person, "Name");
This sets the textbox's Text property based on the person's Name, and can update the person's Name when the user changes the text.
Multi-record binding is more complex, and is based on IList in winforms/wpf, and IEunmerable in ASP.NET; this allows you to bind multiple records (for example into a grid). If the list offers extra features (sorting, filtering etc, via IBindingList, IBindingListView, etc), then more functionality might be available.
Binding also allows "observer" usage - i.e. change notification: if you indirectly change the person's Name, then the textbox gets automatically updated. This relies on events - either of the form public event EventHandler NameChanged;, or (more commonly now) via the INotifyPropertyChanged event (allowing one event to notify for multiple properties).
Some lists (such as BindingList<T>, DataView) have similar notification loops.
The concept of databinding is quite simple;
It allows you to 'bind' the data that is contained in an object to a visual control.
That control 'displays' your data. When the user changes the value that is displayed by the control, the changes are automatically persisted to the underlying object.
Vice versa, when someone changes the data in the object, the control can display the newest value.
http://msdn.microsoft.com/en-us/library/ms752347.aspx
http://www.akadia.com/services/dotnet_databinding.html