Here is the scenario:
I have an Edit Dialog form with a BindingSource and some data bound text boxes on it:
I pass an entity to the form constructor and it gets loaded into BindingSource.DataSource which causes the data bound controls to show the values of properties.
The problem is as the user edits the values in TextBox controls and Validating events get passed, the data source gets changed though it is not applying to DB but it still can confuses the user as he sees the edited values on the List Form, till next application restart.
so the question is: How to prevent binding source from reflecting changes instantly or how to roll them back?
I inherited the binding source and created a new binding source like this:
public class SuperBindingSource:BindingSource
{
#region Properties
public object DataSourceBeforeChange { get; private set; }
#endregion
#region Methods
public void ResetChanges()
{
this.DataSource = this.DataSourceBeforeChange;
}
#endregion
protected override void OnDataSourceChanged(EventArgs e)
{
base.OnDataSourceChanged(e);
DataSourceBeforeChange=this.DataSource.DeepClone();
}
}
Though I am not sure if it is a good approach.
As an option, when setting up data-bindings, you can set them to update data source never.
Then at the point that you want to apply changes, for example when pressing OK button, you can set data-bindings to update data source on property change and then call end edit method of the binding source.
For Cancel button, you don't need to do anything, because the data source is not updated.
Example
In form load event:
this.BindingContext[bindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b=>b.DataSourceUpdateMode= DataSourceUpdateMode.Never);
When pressing OK:
this.BindingContext[productBindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b => b.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged);
productBindingSource.EndEdit();
You can download/clone the full source code:
r-aghaei/SuspendDataBindingExample
You can use the SuspendBinding method after the values are loaded.
After that the values should not update the source until you call ResumeBinding:
SuspendBinding and ResumeBinding are two methods that allow the temporary suspension and resumption of data binding in a simple-binding scenario. You would typically suspend data binding if the user must be allowed to make several edits to data fields before validation occurs. For example, if one field must be changed in accordance with a second, but where validating the first field would cause the second field to be in error.
According to the documentation you should be able to use this with your textboxes. If the user clicks Ok to save the values you resume the binding and if he cancels you don't.
Related
I am new to the .net and I am working on the some functionality in office that is as below
Jobs.aspx.cs
protected void gvActionItems_RowEditing(object sender, GridViewEditEventArgs e)
{
//setting the value of the user control property
}
JobUserControl.ascx.cs
public int _usrcontrolproperty
{
get{return _usrcontrolproperty;}
set{
//depending on the value of the property fetch the data from the database and binding those data on the user controls FormView
}
}
protected void fvJob_DataBound(object sender, EventArgs e)
{
//Making the dynamic UI changes that is setting properties of controls depending upon the values of binding data
}
This is how i did the required UI changes in databound event of form view, but one of senior says 'It's bad architectural code design it has extra memory issue and make the UI changes in the _usrcontrolproperty set method after the data bind.'. So i want to know
1) Is this really bad architectural code ? If bad then why ?
2) And if my seniors way is bad then also Why ?
Because i thought the UI changes should done at the the time of the binding the data
If your senior cannot backup his/her claims.. then he/she isn't really someone you should try learning from. I'm not sure what the "memory issue" is that he/she is referring to, however it's hard to tell with your stripped down code.
That being said, I would reconsider databinding in a property set purely because you open yourself up to "gotcha's" later down the track when people start setting this property.
Instead, I would have a Refresh() method. Therefore, the calling code would be:
UserControl.Property = value;
UserControl.RefreshData();
This gives the calling API the option of refreshing at that point or deferring the decision.
I am with the #Simon on this one but I would have a RefreshData(value) method. Therefore, the calling code would be:
UserControl.RefreshData(value);
This gives the calling API the option of refreshing at that point or deferring the decision.
An in this method you can use as ,
Public static RefreshData(<datatype>data)
{
//Assign the value to the property
//Get the data from database
//Bind the data
}
And you can also make your property private or protected if you don't want to expose the property to other classes.
In my app (GroupItemsPage) I have several groups with items (showing some news). If a user scrolls to the right and chooses an item, he is redirected to a details page. If he returns, the same group should still be displayed. I could achieve this behaviour by adding NavigationCacheMode="Enabled" to the XAML (see here). This works like a charm.
But unfortunately, after this, the page is not updated anymore. On this GroupItemsPage I display the number of unread items (with databinding). Unfortunately, this is not updated anymore since the page is cached.
How can I have both behaviors (update gui with databinding and persisting the chosen page)?
Edit: The GroupItemsPage looks the following. I marked the things I need to update (they are databound).
Events Loaded and Unloaded will always be called when navigating to and away from page regardless of cache settings.
If you want to load/update data in cached page, use Loaded event.
EDIT: There is another solution for your problem.
Define a private field _isLoaded which will tell you if data is loaded.
private bool _isLoaded;
Then in constructor set Loaded event:
Loaded += OnLoaded;
And then use this code in OnLoaded method to load or update your UI:
if (_isLoaded)
{
// Update your UI
}
else
{
_isLoaded = true;
// Initialize your UI (data loading, data binding, etc.)
}
I have a Class named Testing and a Form called TitleScreen. In TitleScreen I have a textBox1 who's text I would like to be passed to a Class and then pass it back to my Form into a textBox2.
I know how to do only the basics in C# so if you try and make it simple as possible.
In your Class:
public class Class1
{
public static string SeparateName(string fullName)
{
string[] wordsInText = fullName.Split(' ');
return wordsInText[0];
}
}
In your Form:
private void button1_Click(object sender, System.EventArgs e)
{
textBox2.Text = Class1.SeparateName(textBox1.Text);
}
"I highly recommend that you read a book or tutorial that targets new users, otherwise there will be holes in your understanding of the language and the frameworks."
It sounds like you want to perform an operation on the textbox's value and then print the result in another textbox.
You can write a method (function) that accepts an argument of type String and perform the operation in that method. The method can then set the Text property of the textbox to the result.
If you're asking how to input code in a winforms project, you can double-click the background of the form to reach its code. (At least in Visual Studio)
If you don't know how to do the above suggestions, I highly recommend that you read a book or tutorial that targets new users, otherwise there will be holes in your understanding of the language and the frameworks.
I would suggest you want to look at the concept of data binding, whereby you bind the controls on your forms to the properties of the underlying objects (instances of your classes).
Binding removes the need to write code to cross-load the data from the class into the form and back again, instead you can then say "text box 1 is bound to this property of my class". Then, when you update the value of the textbox the data is automatically placed into the chosen property of your class instance. Typically you then have a save button that calls a save method on your class to persist the data to your data store (database or whatever).
It is perfectly reasonable to bind more than one control on your form to the same property on your underlying class, so in your example you can bind both textBox1 and textBox2 to the same property on your class. Then, once you've implemented databinding, when you change the value in textBox1, the value will automatically be reflected in textBox2, either on each keystroke or when the field is validated (typically when you move focus to another control).
This is the microsoft documentation on Winforms binding which covers everything you need: https://msdn.microsoft.com/en-us/library/ef2xyb33(v=vs.110).aspx
My form looks something like a three-pane email client. Left side is a grid with a list of people. Top right is the current person's detail record. Bottom right is a custom control with many checkboxes displaying the current person's areas of expertise:
[x] cooking [x] window cleaning [x] brain surgery
[x] massage-therapy [x] singing [ ] random acts of vandalism
When the form is opened, the focus goes to the first person listed in the grid on the left-side of the form,, and the grid's focused_row_changed event fires. In the handler for this event I get the current person's id, then fetch detail data for that person from the database and populate the detail record, and also fetch the person's areas-of-expertise rows and set the checkboxes. All of this is working fine except when the form is first opened, because then the custom control with its many checkboxes is not yet initialized. At that point MyCustomControl is null.
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
What is the best practice design-pattern for handling this situation? What do I do here when my control isn't fully initialized yet?
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
else
{
// ?? Wait around for a bit and keep trying every 100ms?
}
The way I have solved this in my controls when they have had this problem is to implement ISupportInitialize.
In your control, you would put something like:
public class MyCustomControl: ISupportInitialize
{
private bool _initializing = false;
private void BeginInit()
{
_initializing = true;
}
private void EndInit()
{
_initializing = false;
}
private void SomeMethodThatWouldRaiseAnEventDuringInit()
{
if (_initializing) return;
//...
}
}
The windows forms designer checks for your control implementing the interface, and produces this code in the .Designer.cs file:
((System.ComponentModel.ISupportInitialize)(this.customControl1)).BeginInit();
///
/// customControl1
///
this.customControl1.SelectedIndex = 0; //this would normally raise the event
((System.ComponentModel.ISupportInitialize)(this.customControl1)).EndInit();
From what I understand, you are setting MyCustomControl.SetCheckedValues( datasource); when focused_row_changed event fires.
This also tends to happen when the form is just loading, which is generally not desired, because you end up with events telling things to load when, for example, a selected index is still -1.
The way I have been working around this is I have a global boolean in the form called doneLoading. It starts off false and becomes true when the Form_Shown() event gets called.
From there I just put an if(doneLoading) around any piece of code that needs to wait until the form is actually done loading before it is allowed to execute. In your case, I would do:
if(doneLoading)
{
MyCustomControl.SetCheckedValues( datasource);
}
Do your UI initialization functions in a subroutine that isn't called until after all the other UI elements are initialized, or base your calculations on the back-end values instead of the UI.
in response to comments and other posts, if you can't get anything else to work, you can add a 'refresh' button to the UI
I did some googling and didn't find an answer to this puzzle.
Provided you have the following:
MySuperView
MySuperViewModel
MySuperView has two textboxes both bound to string properties on the ViewModel
and your using a DelegateCommand to bind your 'Save' button to the ViewModel using syntax such as:
ViewModel:
this.SaveOrderCommand = new DelegateCommand<object>(this.Save, this.CanSave);
View:
Command="{Binding SaveOrderCommand}"
How do you deal with UI Elements to make the User Interaction more pleasing. For example, lets say some lower level failure occurring during the save action of the DelegateCommand and you would like to trigger the tooltip of one of the TextBoxs. How would this typically occur?
I'd like to stick with as clean code-behind as possible but I am not adverse to putting UI specific code in there.
I would recommend that your ViewModel implement IDataErrorInfo so you can take advantage of validation stuff in WPF. You don't need to wait until someone clicks the save button, once the textbox gets updated it will be validated.
public string this[ColumnName]
{
if (Column == "TextProperty")
{
if(!ValidateTextProperty())
return "TextProperty is invalid";
}
}
void Save(object param)
{
if (CanSave)
{
if (string.IsNullOrEmpty(this["TextProperty"])
{
//Add Save code here
}
}
}
In your View:
<TextBox Text={Binding TextProperty, ValidateOnDataErrors="true",
UpdateSourceTrigger=PropertyChanged}/>
This will put a red box around the textbox and you can add a validation error template to the textbox style to add a tooltip see here
To show exceptions in a tooltip, I would add a property to the ViewModel that exposes the error message as a string, and bind that to your TextBox's ToolTip. Then in your Save method, you would start by setting that property to the empty string, and then doing all the real work inside a try..catch that, if an exception occurs, pushes the exception message into that property, so it automatically shows up in the ToolTip.
You would need to provide change notification for your property, either by making it a DependencyProperty or by using INotifyPropertyChanged.
Basically, you would want a create properties for your view to observe (usually through triggers) that would update your UI depending on what is happening in your code execution.