wpf binding with different source and target - c#

Is is possible to make a binding in WPF whereas the source and target are different properties.
A la something like
Binding="{Binding Source=MySourceProperty, Target=MyTargetProperty}"
As requested an explanation of what I need to do:
The program among other things allows editing of properties that are part of a primary key in the database. If the property just gets changed, then this will either not update the DB value or create a duplicate, depending on how I handle saving the object. A different target would allow this to work (by explicitly specifying what to update by using the 'old' value).

A Binding defined in XAML is always targeting the object and property on which it's defined.
If you define the Binding in code, you can/must specify the source and target explicitly. This is, essentially, how the Binding class works:
Binding binding = new Binding("SourceProperty"); // Sets up the source property
myBinding.Source = mySourceObject; // sets up the source object
targetProperty.SetBinding(TargetType.TargetDepProperty, binding); // This sets the target object/binding
The XAML markup extension for a binding takes care of setting up the target side of the equation automatically, so it's always the object on which you define the binding.

I'll try to answer WHAT you need instead of asked incorrect HOW
"If the property just gets changed, then this will either not update
the DB value or create a duplicate"
In your property setter you should check set { if (this.someMember != value) if the typed in value is has changed:
public event PropertyChangedEventHandler PropertyChanged;
private string someMember;
public int SomeProperty
{
get
{ return this.someMember; }
set
{
if (this.someMember != value)
{
someMember = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SomeProperty"));
}
}
}
As aside-note (or off-topic),
you might find useful the codeproject DataContext in WPF article in its last Download the source code has a sample when updates of one VisualModel's property is reflected (synchronized with updates in the other VM's property)
Immediately after launch:
The text typed in the 1st textbox is reflected in the 2nd textbox and vice versa, the text typed in the 2nd textbox is reflected in the 1st.
The text typed in the 3d textbox is reflected in the 4th textbox (and in textblock content at bottom) and vice versa, the text typed in the 4th textbox is reflected in the 3d (and in textblock content at bottom) .
Note that the download is DataCotext Inner Objects.zip which is unzipped into directory and solution with name Bindingtoclassesstate

In the set of the public property you have access to the old value
key is the old value
value is the proposed value from the binding
you can reject the value that comes from the binding
private string key;
public string Key
{
get { return key; }
set
{
if (key == value) return;
// try data update
bool success = updateDB();
if (success) key = value; // only update if success
}
}
I would combine the above with validation to notify the user if a value was invalid.
Validation Class

Related

Text Box values chabged but the binding see it as null [duplicate]

I am working on some data collection forms in WinForms/C#. When the form loads, I am looping through a configuration and adding a new Binding to each of the TextBox controls; mapping the Text property of each TextBox control to specific string property on my POCO object.
public void BindTextBoxControls(dynamic entity, List<TextBoxConfig> textBoxConfig)
{
foreach (var config in textBoxConfig)
config.Control.DataBindings.Add(new Binding("Text", entity, config.PropertyName));
}
Everything has been working as expected, new records properly saving new values entered into the corresponding TextBox controls, TextBoxes populating with the correct values when reopened a previously entered records with the form, and updates to values in TextBoxes of previously entered records are getting the updated values set on the underlying POCO.
However, I started to layer in some business rules onto the form specifically to gray out/disable and clear out previously entered values in the TextBox based on other user input/activity on the form - things are not working as expected.
In a contrived example; a rule like if a Checkbox_1 is checked then TextBox #5 should not be valued (clear out any previously entered value and disable it from input). On my Checkbox_1 event handler for CheckedChanged, I specifically check if the Checkbox_1 is checked and if so, set TextBox_1.Text == null and TextBox_1.Enabled = false. This works as expected and on the form, I see any previously entered value cleared from the TextBox_1 and it becomes enabled.
private void chkCheckBox1_CheckedChanged(object sender, EventArgs e)
{
if(!chkCheckBox1.Checked)
{
txtBox5.Text = string.Empty;
}
}
However, when I debug and break on the save and inspect the underlying POCO's property that the underlying control is bound to after the method is called; the old value still persists on the object's property which the text box is bound to, despite the textbox having not value appearing on the form. When I reopen the form for that record, the old cleared out value is re-populated in the disabled TextBox. However, manually clearing out the value in the same TextBox or updating a value and inspecting the object shows the updated value after those operations are performed.
It seems like changing the Text value of a TextBox control (e.g. the Text property of a TextBox) in code maybe somehow be "bypassing" the DataBinding? I'm actually seeing the same/similar behavior when applying similar rules to "uncheck" TextBoxes programmatically within event handler methods - the CheckBox controls are also using DataBinding to boolean properties on the POCO.
When you setup databinding by this overload: Binding(String, Object, String), then the value of DataSourceUpdateMode will be OnValidation, which means when you modify the value of control's property using code or through UI, the binding will push the new value to data source only after Validating event happens for the control.
To fix the problem, use either of the following options:
Use another overload and set the DataSourceUpdateMode to OnProperetyChanged
OR, after setting the Value of the TextBox.Text call ValidateChildren method of the form.
Example - Set the DataSourceUpdateMode to OnProperetyChanged
public class Person
{
public string Name { get; set; }
public string LegalCode { get; set; }
public bool IsRealPerson { get; set; }
}
Person person;
private void Form1_Load(object sender, EventArgs e)
{
person = new Person() {
Name = "My Company", LegalCode = "1234567890", IsRealPerson = false };
NameTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.Name), true, DataSourceUpdateMode.OnPropertyChanged);
LegalCodeTextBox.DataBindings.Add(nameof(TextBox.Text), person,
nameof(Person.LegalCode), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.DataBindings.Add(nameof(CheckBox.Checked), person,
nameof(Person.IsRealPerson), true, DataSourceUpdateMode.OnPropertyChanged);
IsRealPersonCheckBox.CheckedChanged += (obj, args) =>
{
if (IsRealPersonCheckBox.Checked)
{
LegalCodeTextBox.Text = null;
LegalCodeTextBox.Enabled = false;
}
};
}
Note - You can put the logic inside the model
Another solution (Which needs more effort and more changes in your code) is implementing INotifyPropertyChanged in your model class. Then when PropertyChanged event raises for your boolean property, you can check if it's false then you can set the string property to null.
In this approach you don't need to handle UI events. Also right after updating the model property, the UI will be updated; in fact implementing INotifyPropertyChanged enables two-way databinding for your model class.

Drop-down list in properties window to start at certain index

Background information:
We have a UserControl called Sensor.
Sensor has a property called SlaveSensor.
The type of the property SlaveSensor is Sensor.
public Sensor SlaveSensor;
{
get
{
return _slaveSensor;
}
set
{
//Some more code for checking various stuff...
_slaveSensor; = value;
}
}
As you can see, the type of the property is the same as the UserControl itself.
The property SlaveSensor is normally set via the properties window during design time.
Visual Studio automatically provides the editor as a drop-down list, from which one can select from all available Sensors on the form.
My question is:
How can I make the drop-down list start at a specified instance in the list,
to make it quicker to find the right Sensor to set for the property?
The name of the Sensor to set as the property is always nearly the same as the name of the Sensor for which the property is being set.
So if e.g. the drop-down list would simply auto scroll to the index in the list that has the name of the Sensor for which the property is being set,
I have achieved my goal.
What do I have so far:
I assume that I need to implement a custom property editor.
I might actually be able to create one with a drop-down list, and fill this with strings,
but the existing is OK as it is, I just need to tell it to drop-down to a certain index when clicked.
Thanks for any help in advance!
I'd try this.
string text = "SomeText";
var item = dropdown.Items.FindByText(text);
if(item!= null)
item.Selected = true;
Or by value:
string value = "SomeValue";
var item = dropdown.Items.FindByValue(value);
if (item != null)
item.Selected = true;
Taken from top answer here

Wpf Data annotation validation required, when the textbox is first created then validation does not work until something is typed in

I am using validation in my wpf xaml project with ValidationContext, which then looks up data annotation. It all works great, I get a red box around a texblock and a tooltip from a style.
However there are two things I cannot work out, when a user opens a view, which has a field that is required I want that texblock to have a red box around immediately. Instead of getting it, after I typed in and then remove some text in to texbox that is bound to a required. How do I make it validate on start?
Here is some code:
protected void ValidateProperty(object value, [CallerMemberName] string propertyName = "")
{
var context = new ValidationContext(this, null, null) {MemberName = propertyName};
Validator.ValidateProperty(value, context);
}
[Required(ErrorMessage = ErrorMessages.DescriptionRequired)]
[StringLength(60, ErrorMessage = ErrorMessages.DescriptionLength60)]
public string Description
{
get { return description; }
set
{
description = value;
ValidateProperty(value);
OnPropertyChanged();
}
}
<TextBox x:Name="DescriptionTextBox"
Text="{Binding SelectedEntity.Description,
ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}"/>
So I want the DescriptionTextBox to be red by default because when the user creates new description the textbox is empty.
My Second question is about the data annotation can I set the length of DescriptionTextBox to be whatever the length of the string is in the data annotation?
Kind Regards
Daniel
You cannot validate the initial source value with exception-based validation, as that requires calling the property setter. You can, however, use a different validation mechanism like IDataErrorInfo (implement the interface and set ValidatesOnDataErrors=True on your bindings). This mechanism allows for validation to occur on the initial source value.
If your app requires .NET 4.5, then you can alternatively use INotifyDataErrorInfo (w/ ValidatesOnNotifyDataErrors=True on your bindings).

How to block assign value in setter in WPF

I have for example property like this:
private string foobar;
public string Foobar
{
get
{
return this.foobar;
}
set
{
if (value != this.foobar)
{
// here I want to check if value is correct
if(value != something)
{
this.foobar = value;
this.NotifyPropertyChanged("Foobar");
}
else
{
value = null;
this.foobar = null;
this.NotifyPropertyChanged("Foobar");
}
}
}
}
Property is binded (MVVM) to Listview :
SelectedItem="{Binding Foobar, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}
.
And when user changes value in list, selecteditem is changed and value is set in setter. I code is ok, when user selected incorrect value, to value and foobar null is assigned. But in WPF still selected value is displayed. When I set breakpoint in getter I can see that it return null too. How to refresh WPF to clear selected value in listview ? It shoud be empty like at the begin.
Thanks
The problem with your code is that you want to override value assigned by binding in your setter method. This will not work because control will not update on next property change for simple reason that it has invoked it by setting property. To implement validation on your values try this build in mechanism.
i would prefer using IDataErrorInfo and dont use property setter logic. the main advantage is that your property value in your viewmodel and your view are always the same and your viewmodel has the information wether the value is ok or not.

determine a textbox's previous value in its lost focused event? WPF

I have a textbox and have an onlostfocus event on it.
Inside the lostfocus method, is there a way I can determine if the user has actually changed the value in it?
i.e how do i get hold of any previous value in it?
Thanks
As with just about everything else in WPF, this is easier if you use data binding.
Bind the text box to a class property. By default, bindings update the source when the bound control loses focus, so you don't have to muck around with the LostFocus event. You then have access to both the new value and the value that the user entered in the property setter.
In the XAML it looks like this:
<TextBox Text="{Binding MyProperty, Mode=TwoWay}"/>
In the class it looks like this:
private string _MyProperty;
public string MyProperty
{
get { return _MyProperty; }
set
{
// at this point, value contains what the user just typed, and
// _MyProperty contains the property's previous value.
if (value != _MyProperty)
{
_MyProperty = value;
// assuming you've implemented INotifyPropertyChanged in the usual way...
OnPropertyChanged("MyProperty");
}
}
What comes to mind for me is a two stage approach. Handle the TextChanged event on the textbox and flag it. Then when the textbox OnLostFocus occurs you can simply check your flag to see if the text has been changed.
Here is a code snippet on how you could handle the tracking.
public class MyView
{
private bool _textChanged = false;
private String _oldValue = String.Empty;
TextChanged( ... )
{
// The user modifed the text, set our flag
_textChanged = true;
}
OnLostFocus( ... )
{
// Has the text changed?
if( _textChanged )
{
// Do work with _oldValue and the
// current value of the textbox
// Finished work save the new value as old
_oldValue = myTextBox.Text;
// Reset changed flag
_textChanged = false;
}
}
}
Store the original value somewhere. You could write a common component to store the value when it gets focus and compare the value when it loses focus. I've done this in ASP.NET and it works quite well.
Another way to solve this by databinding:
Bind the TextBox.Text to the property, that holds the inital value, but use a binding with
UpdateSourceTrigger=Explicit
Then, when the textbox loses focus, you can check the binding if source and target values differ, using this code snippet and evaluating the resulting BindingExpression:
BindingExpression be = tb.GetBindingExpression(TextBox.TextProperty);
Some more code can be found here:
http://bea.stollnitz.com/blog/?p=41

Categories