Databound textbox updates do not persist to db - winforms

I have a winforms app with a group of text boxes for an entity, let's call the entity Product
One of my textboxes is hidden because it holds a foreign key to another database object Business Unit. I populate the hidden textbox using a combobox that looks up values on the parent table. When the selection changes, so does the value in the hidden textbox.
private void businessUnitComboBox_SelectionChangeCommitted(object sender, EventArgs e)
{
this.businessUnitIdTextBox.Text = this.businessUnitComboBox.SelectedValue.ToString();
this.businessUnitComboBox.Focus();
}
Problem is after calling SaveChanges on my context, this change on the hidden textbox is not persisted. Oddly, if I update any of the other Product textboxes, they save just fine.
The textboxes were added to the project with standard drag and drag drop from Visual Studio's GUI, and a bindingsource was created when adding automatically.
My entities implement INotifyPropertyChanged using fody-propertychanged.
I am struggling to find the issue here. Creating new records works fine, but updating that one Foreign Key value never has.
Is this because it is a navigation property and needs to be handled differently, or are there other possibilities as to why the changes are not persisted? Any help is much appreciated.

May be it is because of lacking of this.Validate() in your Save method.
Put this code in save method of your form.
this.Validate();
this.yourBindingSource.EndEdit();
yourDbContext.SaveChanges();
The default behavior of data binding is OnValidation.
To change this behavior you can go to your textbox properties, open databinding part, open advanced, Change Data Source Update Mode to OnPropertyChanged.
So the code of your databinding in designer generated codes will be like this:
this.nameTextBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.productBindingSource, "Category.Name", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
In this case you don't need to use this.Validate before saving changes and you can simply use
this.yourBindingSource.EndEdit();
yourDbContext.SaveChanges();

Related

Detecting a BindingSource SortChanged Event in order to implement 2 way binding in winforms

I am trying to write a 2 way binding application using C# winforms
I have a BindingNavigator and a DataGridView bound to the same data source
So that I can have 2 way binding ( yes in winforms ) I want to be able to detect when the bindingSource sort order has changed.
When I click the column header of the DataGridView the sort order of the grid does change
and the BindingSource.ListChanged event does fire
However the bindingSource.Sort remains null
When the grid column header is clicked the BindingSource.ListChanged event does fire
private void bindingSource_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType == ListChangedType.Reset)
{
if (bindingSource.Sort != null)
{
controller.Sort = bindingSource.Sort; // never gets here. Why?
}
}
}
Why isn't clicking the DataGridView Column header setting the bindingSource.Sort property?
What event should I be listening to in order to detect that the bindingSource sort has changed?
Having come across this problem (for a different reason as in multi column sorting) I found it amazing that the Bindingsource doesn't have a sort changed event nor that the Datagridview's own Sorted event doesn't fire with it (it just seems to fire when it does the sorting).
The datagridview "knows" and even changes its sort glyph position.
The way I solved this won't suit most because it is specific to the task I am coding. Within my multi column sort routine I keep track of the bindingsource.sort string and then on a regular event within the Datagridview (currently trying SelectionChanged) I compare my sort string with the current Bindingsource one and switch off my multi-column sort indicators when it is different. This is ok for me because all my Datagrids are based on a common custom control.
The only other way I thought of would be to create a custom control based upon the Bindingsource and add a sort method (that calls the bindingsource sort method) and then trigger my own event. This would be fine if starting out on a new project but changing a large program becomes a pain.
It is sad given that I made a custom control from the start for every UI control (Winforms) but never thought to make a general one for Data Access.
Hope this helps someone facing the same issues.

Building a data entry form in wpf

I'm trying to build a data entry form in wpf. To perform validation I apparently need to have an object attached in the datacontext of my grid. But how can I have one when I didn't create one yet?
How does it work?
For example, I have a screen with a datagrid. The datagrid contains users that were obtained from membership. Above the grid is a button: add user. When clicked a new window appears and the following can be entered: user name, password, email. To perform validation on the textboxes to see if they aren't empty. Now, it is my understanding that the way this works is by having an object attached to the window (datagrid datacontext). But how can I have it attached when it doesn't exist yet?
This is a case where MVVM design patterns are very useful.
Every WPF view has a corresponding view model object that the properties in the view are bound to. So your window with the data grid has a view model - its DataContext - and the view model has properties that are bound to properties in the view - e.g. the ItemsSource in the data grid is bound to a collection (see note 1).
The "add user" command (which is implemented as a RelayCommand in the window's view model) creates a new view (the new window) and its corresponding view model object (the new user), sets the view's DataContext to the view model, and calls ShowDialog to show the window. (See note 2.) If the user accepts the new object, ShowDialog returns true, and the logic in the command takes the view model object (which now contains whatever changes the user made) and uses the information in it to create a new model object and add it to the model. If the user cancels, ShowDialog returns false, and the command discards the view model object without creating a new model object.
Note 1: The collection here may be a collection of model objects, or it may be a collection of view model objects. It depends on whether or not you need anything that's not in the model for displaying the model objects in a data grid. It's common, in this kind of scenario, for the objects in the grid to be view models for the dialog - that is, the view model objects have properties implemented for both display in the grid and modification in the dialog window. On the other hand, if all the grid is doing is displaying data from the model, there may be no need for an intermediary object.
Note 2: Having the command create a WPF window violates a central MVVM design principle, which is that view models shouldn't create WPF objects. The reason for this principle is pretty simple: you can't build an automated unit test for this command, since it's just going to throw up a dialog and wait. There are all kinds of different approaches to this - see, for instance, this question, and Josh Smith's blog post on the Mediator pattern - and all of them involve delegating the creation and display of the actual dialog window to a separate service that can be mocked out for unit testing. If you don't want to choose one of those approaches up front, you can retrofit one into your application once you get this thing working.
The idea here is that you should attach an object which is slightly different from your business models. In your case it won't UserInfo (or whatever you have for users in grid). It will be some other class, more suitable for editing. In MVVM this class will be a ViewModel. This class will have some differences comparing to your regular user class, for example it may have some properties nullable (when you haven't set them yet). Also this class will handle validation. You should instantiate this class at the same time you're creating an editor window and put instance of this class into Window.DataContext.
Hmm, there is a lot in this question but I just created a screen with three data grids (I am using Telerik in this case) and under each datagrid is a button to add to the grid. No the window with the three datagrids has it's own view model. and each of the "pop up's" has it's own viewmodel, in this case all of these are user controls and I just create a new window and set window.content and call show dialog.
Communication is facilitated via "events" - not the standard events you are used to in .NET but in this case I am using Prism and it's CompositePresentationEvent class. When the user is done creating their new object they click add and I fire off this event with the "payload" being the object they created. The main window with the three grids listens for that event and has a method to handle it, in this case adds it to the ObservableCollection which is what I bind the grids to.
If I were you I would look into the various frameworks that are out there, Prism, MVVM light etc... Again, your question seemed rather broad, I tried to give an overview but I didn't go into detail, if you look into some sort of framework I think it will clear up a lot of these details for you.
When the users hit Add New, create a new blank copy of your object, and set the datacontext to that new object.
Set some kind of flag to identify that it is a New object. This can be the Id being NULL, 0, -1, etc or an ObjectState property set to New. That way all your validation rules apply, and once the user hits save you know to INSERT instead of UPDATE

Silverlight Twoway Binding and rolling back changes - how to?

I have simple SL user control. A listbox which shows all customers and on the right a number of textboxes and comboboxes that are bound to the SelectedItem (Customer) in the listbox. The SelectedItem bound to SelectedCustomer property.
I am looking for a pattern/methodology to deal with canceling changes made to the customer (in the bound textboxes and combo boxes).
The edit controls (textboxes and combo's) can be one or two way bound to the selecteditem of the listbox.
If they are two way bound, immediate changes in the textboxes are reflected in the listbox. If they are oneway bound the changes in the textboxes are not reflected in the SelectedCustomer object.
At the bottom of the edit form i have typical Save, Cancel, Delete buttons. The save button for instance would take the SelectedCustomer object (if twoway bound and I would send through service for saving on server).
If the textboxes are one way bound i have to capture somehow the textbox values and insert into some object for sending to the server for saving.
If I use twoway binding , and say the save operation fails...i have to set the SelectedCustomer values back to original values otherwise the client now continues to see data that has not been saved.
There must be an easy way of dealing with this type of scenario....
RIA Services with Entity Framework already provides this functionality, basically how RIA services works and you can do it too as follow.
Each class implements interface called IEditableObject, which provides methods BeginEdit/EndEdit and CancelEdit. And it also stores (copies) instance of same class with name "OriginalEntity" with same values that were loaded from server.
After the form shows up for user to modify, BeginEdit is called, which probably caches every property using reflection, in some sort of dictionary. If you call CancelEdit, then values from OriginalEntity are loaded back in object.
Upon some errors while saving changes, you can either refresh the entities from server (best way) or you can try loading properties back from OringalEntity property.
I wouldn't discard user changes, as that easily leads to user frustration. IMHO, the user should not be informed about connection problems by uncontrolled data rollbacks.

How do I open a WPF window in AddNew mode before any data has been loaded?

I want to open a WPF4/EF4 form in AddNew mode so the user can start entering data in bound controls before any data has been selected from the database. I already have an "Add New Record" button but it only works with a populated DataContext (my CollectionViewSource). Here is the code so far:
private void btnAddNewRecord_Click(object sender, RoutedEventArgs e)
{
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesListCollectionView.AddNewItem(newEntity);
}
Background: This is a basic WPF app with bound controls. I started with an Entity Framework model that appears in my DataSources window. I dragged my LabSample entity from the DataSources window and let it create my CollectionViewSource (labSamplesViewSource) in the XAML's Windows.Resources section. The DataContext for all my controls is the labSamplesViewSource. I create a new LabEntities object called _labEntitiesContext as the window is instantiated. I use _labEntitiesContext to build my filtered ObjectQuery(of LabSample) and to SaveChanges, but I'm a little confused as to how this _labEntitiesContext is hooked up to my CollectionViewSource. If you could clarify this along with answering my question that would be helpful. Note: I'm not ready to use MVVM.
When the window loads I use this.FindResource to grab a reference to the CollectionViewSource in a class level variable named _labSamplesCollectionViewSource. I allow the user to enter search fields to populate the screen with data. My LoadData routine looks something like this:
System.Data.Objects.ObjectQuery<LabSample> labSamplesObjectQuery = this.GetLabSamplesFiltered_Query(_labEntitiesContext, sampleID_LIKE, xxx_LIKE, yyy_LIKE);
System.Data.Objects.ObjectResult<LabSample> labSamplesObjectResult = labSamplesObjectQuery.Execute(System.Data.Objects.MergeOption.AppendOnly);
_labSamplesCollectionViewSource.Source = new System.Collections.ObjectModel.ObservableCollection<LabSample>(labSamplesObjectResult);
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
The _labSamplesListCollectionView class level variable set above is used in my btnAddNewRecord_Click code. Before LoadData is called the _labSamplesListCollectionView is null causing my AddNew code to fail with "Object reference not set to an instance of an object".
How can I make this work? I'm wondering if I should be making use of _labSamplesListCollectionView.AddNew instead of my current technique but I couldn't get that work either. Your help will be greatly appreciated.
I am writing an app that does something similar. I am however using MVVM pattern, which allows me to do some neat things. In mine, I am working with Shipments. On the ShipmentsView, I can click an "Add New" button which fires off a bound command property which is housed in the associated ViewModel class. That command methods looks like the following: Note: Views in this context are not CollectionView but refer to MVVM View classes.
Dim NewShipment = New Shipment()
_Context.AddToShipments(NewShipment)
Dim ShipVM = New ShipmentViewModel(NewShipment)
ShipmentVMCollection.Add(ShipVM)
Dim NewShipmentView as ShipmentView(ShipVM)
My ShipmentView handles it's placement and visiblility, and my Shipment object has it's property values initialized so that it does not immediately present errors via it's validation handlers. This way the user can create a new shipment and if they get sidetracked they can save it and come back to it without having a bunch of mandatory fields.
When I use a CollectionViewSource, I populate it with an ObservableCollection of my entities, and then add the entities to that observable collection when I create them. ObservableCollection implements INotifyPropertyChanged and INotifyCollectionChanged events and notifies the UI when something happens, and it all works through the CollectionViewSource.
You might take a look at the MVVM pattern which is really good for moving data and keeping it in the proper scope, and there are some good MVVM frameworks out there that will help you make a nice application with MVVM.
MVVM may be a bit of overkill for your app if it is small. But if it gets over more than just a few Views it is going to get unwieldy and hard to move data back and forth and keep it current, and maintainable.
Wiki Article for MVVM - a pretty good place to start and get links
This is my Constructor for one of my ViewModels. I realize you don't want to implement in MVVM right now, but a code behind would be similar. In this instance, I am using a background worker to get my entity records, (the constructor call be for that and the View setting immediately afterward can be disregarded), then I link up my CVS, Populate it with my ObservableCollection, and set it's View to a field so I can filter on it later.
Public Sub New(ByRef MyView As NTMsView)
Me.New(ViewManagerService.CreateInstance, ViewModelUIService.CreateInstance)
NTMsBackgroundWorker.RunWorkerAsync()
_View = MyView
_NTMCollectionViewSource = _View.FindResource("NTMCollectionViewSource")
_NTMCollectionViewSource.Source = NTMs
_NTMCollectionView = _NTMCollectionViewSource.View
End Sub
This is an example of my AddRecord method. Then I instance a new object, add it to the appropriate collection in the Context, Save it, execute a stored procedure, then refresh the context since the stored procedure did a few things to the record. Then I add the object to my Observable.
Private Sub AddNTM()
'Create an NTM Object.
Dim NewNTM As New NTMShipment()
'Add it to the context
_context.AddToNTMShipments(NewNTM)
_context.SaveChanges()
_context.MakeNewSecurityID(NewNTM.NTMShipID)
_context.Refresh(RefreshMode.StoreWins, NewNTM)
'Wrap it in a ViewModel and Add it to the NTMs collection
NTMs.Add(New NTMViewModel(NewNTM))
End Sub
As for creating a new entity before your CollectionViewSource is created, a couple of questions. Is your edit forms datacontext related to the CVS? In my forms, the CVS is only used in conjunction with ItemsControls since it is displaying a collection of items. If your edit forms controls are dissociated from the CVS, you should not have much trouble populating them with a new entity and when it comes time to save, check to see if CVS is null and if so, create it then populate it.
If that is not a good answer, could you expand on how your application is structured?
Instead of opening the Window in AddNew mode I disable all data entry controls when the window loads or the when a search returns no records. When the "Add New Record" button is clicked I ALWAYS start over with a new data context that contains just one new entity. This means I have to prompt to save changes if any dirty (modified) records exist. The prompt allows the user to save changes, discard changes or continue editing (never entering AddNew mode). Here is the AddNew code:
MessageBoxResult response = PromptToSaveChanges(ReasonForPromptToSave.LoadingData);
if (response == MessageBoxResult.Cancel) return;
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesCollectionViewSource.Source = new ObservableCollection<LabSample>();
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
_labSamplesListCollectionView.AddNewItem(newEntity);
_labSamplesListCollectionView.CommitNew();
_labSamplesListCollectionView.Refresh();
Here are my steps to put the window in AddNew mode:
1) Prompt to save changes.
2) Create a new entity and add it to my data context.
3) Create a new ObservableCollection of my entity type and assign it to the .Source of my CollectionViewSource. Note the _labSamplesCollectionViewSource is a reference to the XAML's CollectionViewSource that was auto-generated by dragging a table from the data sources window.
4) Assign the .View of the CollectionViewSource to a class level ListCollectionView variable.
5) Add the new entity to the ListCollectionView that was just created.
6) Call CommitNew and Refresh on the ListCollectionView
I have answered my own question here, but keep in mind that the answer is the result of trial and error and may not be ideal. Regarding my confusion as to how the _labEntitiesContext is hooked up to the CollectionViewSource I believe the answer is in the line that reads _labSamplesListCollectionView.AddNewItem(newEntity), but I'd like to see an explanation of how all of the objects reference each other.
My final comment/question is that I'm disappointed at how hard it is to find a standard reference application or document that teaches non-MVVM WPF/Entity Framework databinding in detail. Microsoft promotes drag-and-drop binding but leaves us without a reference on how to build a complete application. I'll move on to MVVM soon, meanwhile if anyone can direct me to a GREAT resource or feature complete application that is WPF, non-MVVM and Entity Framework I would greatly appreciate it.

Moving to new record on Collection

I'm just getting started with collections (ObservableCollections) and I've hit a wall that I assumed would be easy. I'm sure it is easy but I'm just not finding the answer.
I have a WPF screen with a DataGrid to the left and TextBoxes to the right of the screen. The DataGrid is bound to the ObservableCollection (Activities) and I can click up and down the DataGrid and see my TextBoxes refresh with the correct info. I can then alter the info in the TextBoxes and save it back to the DB. All works perfectly!
However, when it comes to Adding a record to the collection I'm lost as to the correct approach. I'm using the Add method as shown below, but how do I move to this newly created record so it can be edited? I've tried a dozen approaches but I've yet to find a correct approach. The TextBoxes just remain focussed on the last edited record. Any ideas?
Private Activities As ObservableCollection(Of ActivityRecord)
Private Sub AddMode()
Dim _ActivityRecord As New ActivityRecord(0, DateTime.Now, Nothing, "", gWorkerID, "")
Activities.Add(_ActivityRecord)
'Code to move to the newly created record should go here
In WPF, every ItemsControl derived control (list your DataGrid) secretly uses a derivative of CollectionView to facilitate the navigation between records/items - in effect it is the class which provides the currency mechanism that help other controls (like the textbox) determine which data bound item is current.
If you create a new object and add it to your ObservableCollection, you can use one of the CollectionView's MoveXXX methods to move to make that item current. You can (depending on what sort of CollectionView you get) also call the Add method on the CollectionView, and it will automatically call the Add method on your underlying ObservableCollection.
Either way, retriving a reference to the CollectionView is the secret. You can either use the CollectionView's static method (I forget it's name) to retrieve the view being used for your DataGrid, or (and this is my preferred method) you can explicitely create a ListCollectionView and bind your DataGrid to it, instead of the ObservableCollection.

Resources