As all of us know, if we want to bind a control in the WinForm to a property It has to be done through the fallowing way:
txtTitle.DataBindings.Add(new Binding("Text", object, "Name", true, DataSourceUpdateMode.OnPropertyChanged))
but in this case if "object" ( second argument of the Binding) changes to another one, in order to reflect change in the UI, we have to reset binding in this way:
txtTitle.DataBindings.Clear();
txtTitle.DataBindings.Add(new Binding("Text", object, "Name", true, DataSourceUpdateMode.OnPropertyChanged))
but I'm looking for a solution that does the above code snippet automatically, without the need to do it manually.
I think BindingSource might help me because it has ResetBinding() method but I'm not sure.
I'll appreciate your guides.
Thank you in advance.
EDIT:
I know how to update UI element after changing property value by implementing INotifyPropertyChanged interface, but my question is how to deal with the situation when my object that UI control is bound to its property changes, not the property itself.
.
.
.
consider this situation. I have a list of objects of a class and I want to bind my textbox to the property of this class, but based on some situations I want to change my object that is selected from that list so textbox has to be updated to reflect the new object's property value. at the current stage the only solution I can use is to delete previous binding and add new one. I want that somehow UI element be aware of the situation and shows new object's property value automatically or with smallest code as possible.
Unless I'm really not understanding your use-case, all you need is a BindingSource object between your TextBox and data object.
You don't bind your TextBox directly to your object, but to the BindingSource. All you have to do is use the DataSource and DataMember properties of the BindingSource to "switch" which object is used as the source of data for your controls.
See: MS Docs
Related
I have 400+ textboxes on a page. The page is meant to be an exact replica of a paper form. The user is going to edit all of the fields and then save the data to the DB. The data is being pulled down into a DataTable from a SQL DB.
I'm planning on saving the data via the same DataTable or just via a bulk update. Not 100% on that. But, I need to get access to that data. And maybe I'm not doing this next best part the best and if I'm not, I'd appreciate it if I was informed of a better way.
When the DataTable gets the data, I assign each field into the appropriate control. Below is an example of how the data is being added.
foreach (DataRow CDR in ClaimDataTable.Rows){
if (CDR["BoxNumber"].ToString() == "1.1")
this.Box1_1.Text = CDR["DataValue"].ToString();
if (CDR["BoxNumber"].ToString() == "1.2")
this.Box1_2.Text = CDR["DataValue"].ToString();
if (CDR["BoxNumber"].ToString() == "1.3")
this.Box1_3.Text = CDR["DataValue"].ToString();
I wrote some code to automatically create that code. So I didn't manually write all 400+ lines.
What I was thinking, was that I could add a LostFocus event to each TextBox. Then when the control loses focus, I would create a class with a box name and the box value. Add that to a list and when they're ready to save, just loop through the list and do the bulk update with the BoxNumber and the box data.
Would that be feasible? Or is there a better method?
Thanks!
Look into event routing.
You should not create and access individual text-boxes but let the framework create them via datatemplating a collection, your above code is ze horror.
When the DataTable gets the data, I assign each field into the
appropriate control.
WPF is data binding and one does not generally load controls as you mentioned. Why is the code not binding to each control to a property which adheres to INotifyPropertyChanged?
Have the page's DataContext (DC) point to an instantiated class which adheres to InotifyPropertyChanged interface. The setting of the DC in that fashion will allow all controls on the page to use its datacontext by default.
In the class to be instantiated for the DC create a member property which calls PropertyChanged when the value is set using the INotifyPropertyChanged system.
Bind each TextBox Text property to that property on the class.
Then each control which needs that value will display it automatically after it has been set such as in this example.
<TextBlock Name="tbHeader"
Text="{Binding CDRData}" />
Then one only has to write the value to the property named CDRData once and all textboxes bound get the value.
I have a form on which all textboxes are bound to different properties of the same dataobject that implements INotifyPropertyChanged. The Forms Autovalidate is set to 'Disable' as I want to trigger the validation explicitly by calling form.ValidateChildren().
Expected:
After calling ValidateChildren all edited values should be in my dataobject.
Problem:
Only the last focused control writes it's data to the dataobject, but all other controls lose the edited values and show the old value instead.
Question:
How can I make sure that all data is validated before the controls refresh themselves?
Using Autovalidate = EnablePreventFocusChange or EnableAllowFocusChange does work but as I want to validate all at once it is not an acceptable solution for me.
Searching the internet for soutions I found an example showing the same problem but unfortunately no solution.
EDIT
After further investigation i tried this and it works:
form.BindingContext[dataobject].SuspendBinding();
form.ValidateChildren();
form.BindingContext[dataobject].ResumeBinding();
Is Pausing the Binding the standard way or are there any better solutions to fix this?
I'm not sure if this is helpful or will it be a complete answer but I had similar problem where only one value has been saved out of whole form. Here is some background explanation I found on the MS site:
http://connect.microsoft.com/VisualStudio/feedback/details/351146/binding-writevalue-does-not-update-underlying-data-source
Even SuspendBinding didn't work for me. So following the link and the information there I connected the objects via BindingSource object with RaiseListChangeEvents property set to False
Dim MyBindingSource = New BindingSource With {.DataSource = MyDataSource, .RaiseListChangedEvents = False}
MyControl.DataSource = MyBindingSource
Once this is in place you can call refer to the binding on the control and write the value manually (assuming there is just one binding) the code will look similar to:
MyControl.Bindings(0).WriteValue()
Let me know if this helps.
I have a listbox bound to a List<object> as its DataSource. What I'm wanting to do is use the SelectedValue property of the Listbox (i.e the object corresponding to the current selection) as a DataSource for some textboxes that will display certain values of the object for editing.
I've tried
TextBox.DataBindings.Add(new Binding("Text", ListBox, "SelectedValue.name"));
and
TextBox.DataBindings.Add(new Binding("Text", ListBox.SelectedValue, "name"));
but as there is nothing selected in the ListBox (because the form hasn't been shown yet), I get an exception about "Value cannot be null".
Now I know that I can (re)bind to ListBox.SelectedValue in my form's SelectionChangeCommitted handler (that is, after a selection has been made), but if i have to do that I might as well just set the TextBox's value directly (admittedly I could just do this to resolve the issue, but I'd like to learn more about databinding).
So my question is, in short: Is it possible to bind to ListBox.SelectedValue once (initially, before the ListBox has a selection) and avoid the null value exception, and if so, how?
I'm not sure which control your projectNameCtrl is, but you'll want to bind your TextBox. Something like this:
textBox1.DataBindings.Add(new Binding("Text", listBox1, "selectedvalue"));
Where:
textBox1 is your TextBox
listBox1 is your ListView
EDIT
You should be able to data bind a ListBox even if that ListBox has no selected items so your 'value cannot be null' must be for another reason. I suggest using the debugger to determine which object specifically is null.
You can ensure you don't data bind a control more than once by first checking the control's DataBindings.Count property; if it's equal to zero you haven't yet data bound that control:
if (textBox1.DataBindings.Count == 0) {
// OK to data bind textBox1.
}
Off the top of my head, I think you'd need to do something on each selectedItemChanged event...
I know this doesn't answer your question, but I'd look at using WPF instead since this is so much more elegant to do in WPF, and let's face it, by not creating a GUI in code (using XAML instead) your sanity will be much more intact when you finish your project. I don't recall enough windows forms, but in WPF, you just implement INotifyPropertyChanged on your back-end object that you're binding to, and then when you bind to the SelectedItem property of that ListBox, you automatically get updates since the SelectedItem property is a DependencyProperty.
Is it possible to update data in the datagrid without having to bind it to a particular object and still enjoy the benifits of a "two way" data binding? In this case the type of object to be binded is decided at runtime based on user input.
Some more clarity:
Actually I am using a multi threaded application and I need to update data in UI from another thread. For this purpose I share a reference for an Observable Collection object and bind(through ItemsSource) the same to the data grid. Now, whenever the thread updates the data, it calls a specific function in the UI thread asking it to refresh the datagrid. The problem arises when I try to modify some value in the grid so that it is sent back to the thread that is running in parallel. It throws an exception "'DeferRefresh' is not allowed during an AddNew or EditItem transaction."
With a DataGridView you can change the datasource at runtime and still enjoy two-way binding... Just set the DataSource to null first.
If for some reason you can't do that, you might consider object composition: i.e. binding your grid to an intermediate object which simply holds a variable for the actual object you're 'binding' at runtime, and create wrappers for the implementation (IList, IListSource, IBindingList or IBindingListView).
Unless I've missed something?
You can dynamically create a binding though code at runtime on bases of user input
Other than that I don't think that there would be any way to achieve two way binding without data binding. Once ugly way could be to handle data change event in both the itemssource and DataGrid and on the event update the other control i.e. data grid in case of change in itemssource and itemssource in case of changed value in datagrid, manually
When you set (not bind) the ItemsSource, bindings automatically happen at row and cell levels by internal implementation of DataGrid. So I am not able to understand "how" can we not bind an object to a datagrid and enjoy it's TWO WAY UPDATES.
They will anyways happen when u set the ItemsSource... or is it that you DONT want to even set the ItemsSource?
I have a List collection where UserPropDef is a custom class with a few public properties. I want to display the contents of this list in a DataGrid. So I set the DataGrid.DataSource property of my grid to the list and it displays the contents just as I want.
Now if I add or remove items from the list I want the DataGrid to update. I can't use a BindingList because this list needs to be serialized. I've tried calling the Update() and Refresh() methods of both the DataGrid, and the form and nothing seems to cause the DataGrid to refresh based on the current contents of the collection.
The only thing that works seems to be setting the DataSource property again. Yet when debugging my code I can see that after the collection has changed the DataSource property of the DataGrid is in fact still referencing the correct and updated collection.
Is there a better way to cause the DataGrid to refresh based on it's current DataSource?
My suggestion would be take what is behind door number 3:
Create a custom Serializable List that implements IBindingList (Or something that inherits from BindingList and fixes the Serializable issues. Check out Fixing BindingList... for ideas).