I have a property in my view model which returns a constant under some conditions.
which is implemented similiar to this:
class Tmp : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public String Text
{
get { return "testing"; }
set
{
PropertyChanged(this,new PropertyChangedEventArgs("Text")); }
}
}
so the property Text alwasys returns "testing".
I bound this to a text box like :
<TextBox Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
When the application starts, the textbox correclty says testing.
now when I type something in the text box the setter gets called, which calls PropertyChanged.
After this something ( probably the GUI ) calls the getter and gets the value "testing".
However the text in the textbox is not changed back to testing.
So I can type "abc" into the text box, and text box displays "abc" even though the model is just storing "testing".
why isnt the text in the text box not reset to "testing" at each keystroke?
Why should the textbox get the text again? It just wrote it into your source property it "knows" that it must be the same, because he is in the middle of telling the property the new value. Otherwise you would create circular references. What you are doing is going completely against guidelines and behavior expected from a property.
Remove the setter, make the binding one way, raise the text property when your constant is changed, and make the textbox readonly. Remember, its not necessary to call Propertychanged in the setter.
To make your initial approach work, you need to break out of the "Textbox changes property but won't listen for incoming raises" state
set
{
sUIDispatcher.BeginInvoke((Action)(() => Raise("Name")));
}
i would like to add, that this is a REALLY bad idea and strongly discourage you to do it like that.
Related
I have a Winforms app where a particular textbox field ("Phone Number") gets updated entirely programmatically, as the result of a user search on another form or as queries to a database for the overall main form's (saved) data.
We'd like the textbox to display with a red background whenever the data fits certain situations (blank is one of them, but there's also another string that can show up that we need to treat as "blank"). So I rigged this up on a TextChanged event handler.
However, sometimes the user will press a "Clear" button to blank out the Person data/fields on this form, including this Phone Number textbox. And in that case, we don't want a blank to show up red. So I adjusted the TextChanged event handler to account for this. OK, so far, so good.
Yet if they have done a Clear and now another search takes place, dumping its results back into the field, if the updated data is empty string or null... well... the TextChanged event won't fire because the VALUE is not changing. It already was Null/Empty. Yet in this situation, we'd WANT what I've got in the TextChanged event handler to fire.
I can't use the Validating Event Handler, because that only engages when a USER provides the input (I think?)
So far, my work-around has been to FORCE the event handler to fire after we're basically updating that field at the end of a search (possibly updating Null/Empty with Null/Empty). And this works. But it seems like there ought to be a better way. ??
I didn't see another event handler on that control that seemed to do what I'm looking for, but I thought I'd ask the crowd.
Thanks!
Apparently you want a special kind of TextBox, one that fires the event whenever property Text is set, even if this doesn't lead to a change.
A special kind of TextBox?Sounds like a derived class:
class MyTextBox : TextBox
{
public override string Text
{
get => base.Text;
set
{
// if no change, only call OnTextChanged, otherwise call base.Text
if (this.Text.Equals(value))
{
base.OnTextChanged(EventArgs.Empty);
}
else
{
base.Text = value;
}
}
}
}
If desired, use a stringComparer, like OrdinalIgnoreCase.
public IEqualityComparer<string> TextComparer {get; set;} = StringComparer.OrdinalIgnoreCase;
public override string Text
{
get => base.Text;
set
{
if (this.TextComparer.Equals(this.Text, value))
...
The disadvantage of method is that base.Text = value will check for equality again. If you don't wan't this, look at the source code of TextBox
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
If you click on SetValue, you will jump deep inside windows. I'm not sure if you ever want to get there, given the fact that you won't update the Text property several times per second.
I am developing an UWP app leveraging the MVVM paradigm. My view contains a simple TextBox that has its Text property bound to a respective ViewModel property:
<TextBox Text="{Binding Path=Radius, Mode=TwoWay}"/>
Naturally, I've assigned my ViewModel to the page's DataContext:
public sealed partial class ExamplePage : Page
{
private ExamplePageVM viewModel;
public ExamplePage()
{
this.InitializeComponent();
viewModel = new ExamplePageVM();
DataContext = viewModel;
}
}
In the ViewModel I perform some kind of input validation, i. e. if the user inserts an invalid float value into the TextBox I want to reset the TextBox to a default value (zero, for instance):
class ExamplePageVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private float radius;
public string Radius
{
get => radius.ToString();
set
{
if (radius.ToString() != value)
{
if (!float.TryParse(value, out radius)) radius = 0;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Radius)));
}
}
}
}
Changing the value in the TextBox causes the setter to be called as intended. Also, the PropertyChanged event is invoked accordingly. However, the TextBox still contains invalid data after the setter execution has finished, which means that the view isn't updated correctly.
According to the first comment on this post, the solution to this issue is using <TextBox Text="{x:Bind viewModel.Radius, Mode=TwoWay}"/> instead of the Binding approach shown above. Why is that so? What's the difference between Binding and x:Bind in this very situation?
You may want to set the UpdateTrigger yourself since TextBox normally updates the source when focus lost gets called.
You can change the behaviour UpdateSourceTrigger=PropertyChanged.
<TextBox Text="{x:Bind AnswerText, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding AnswerText, UpdateSourceTrigger=PropertyChanged}"/>
If this is not working you may want to prevent inputs different then numbers with the keydown event. Which you could outsource in a user control for reuse.
Hope this helps.
Binding to TextBox.Text is a rather special case, because Microsoft made a decision where the most common scenario is that the binding should be updated when control loses focus, as opposed to each and every input text change. This allows 2 things:
somewhat more efficient handling of larger texts
safeguarding user input in progress from being changed by application
In the absence of publicly available UWP source code it's possible that MS developers may provide you with more reliable insight, but even comparing the changes to a bound source with direct tracking of the EditBox.TextProperty via DependencyObject.RegisterPropertyChangedCallback makes you expect that instead of the usual direct binding to the dependency property changes there is actually an additional man-in-the-middle kind of implementation in TextBox that handles how and when TextProperty updates affect and being affected by the DataContext or underlying class properties bound with {Binding} or {x:Bind}.
Note that {x:Bind} and {Binding} are very different mechanisms, especially with the first being a compile-time and the 2nd is run-time, meaning that internally they require different implementation and it's up to the framework developers to make sure they exhibit the identical behavior.
Now in your test scenario you are trying to validate and possibly change a property value in the bound data source expecting that the TextBox will display the value you want, which it does with {x:Bind}, but not with {Binding}.
Apparently you've found a scenario where {x:Bind} and {Binding} implementations behave differently. I did the same tests and totally confirm your findings.
I have a dialog which has its DataContext assigned in the code-behind. When a button (somewhere in the UX) is clicked, an asynchronous command is executed. In that command, the dialog is initialized and then gets opened.
The problem now is, that sometimes the ComboBox is empty. Does anyone see, how this could go wrong?
I can print the values from the asynchronous database-access before opening the dialog, and the values are always there.
There are other fields in the dialog, which are bound to the ActiveUser object. That object is not being set from data originating from an async call. Those values are always present, unlike the ComboBox ItemsSource. So I assume, it has something to do with the async call.
Creation and opening of the dialog in a command:
EditUser = AsyncCommand.Create(async (choosenUser) =>
{
// create a dialog, which has its DataContext assigned in the constructor in the code-behind
EditUserDialogView dialog = new EditUserDialogView();
// assign a property (non-async)
((EditUserDialogViewModel)dialog.DataContext).ActiveUser = (DbUser)choosenUser;
// get the list of UserTypes async
List<UserType> userTypeList = await DataAccessService.GetUserTypesAsync();
// Debug output -> usertypes are always printed correctly, so are available at this point
foreach (UserType ut in userTypeList)
{
Log.Info("UT: "+ ut.UserTypeName);
}
// assign UserTypes to property bound to combobox ItemsSource
((EditUserDialogViewModel)dialog.DataContext).UserTypeComboBoxList = userTypeList;
// open the dialog
if (dialog.ShowDialog() == true)
{
}
else
{
}
});
Binding in dialog:
<ComboBox IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding UserTypeComboBoxList, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="UserTypeName"/>
The AsyncCommand is implemented as suggested here:
https://msdn.microsoft.com/en-us/magazine/dn630647.aspx?f=255&MSPPError=-2147217396
(The only difference is, that the parameter for the lambda is the CommandParameter (here choosenUser), not a CancellationToken.)
Here is the property definition in the ViewModel for the dialog:
public List<UserType> UserTypeComboBoxList { get; set;}
I figured out, how I can fix this behavior. When I change the property to raise a NotifyPropertyChanged event, it will always show the ComboBox correctly, containing the values.
public List<UserType> UserTypeComboBoxList
{
get { return userTypeComboBoxList; }
set
{
userTypeComboBoxList = value;
NotifyPropertyChanged(nameof(UserTypeComboBoxList));
}
}
As all values should be initialized by the time I open the dialog, I do not see why raising this event changes anything.
It's the race condition. Since, the database call (to fill comboBox) is invoked asynchronously so if before the dialog initializes, asynchronous call is completed then the comboBox is displayed properly; If not, then you see that you are assigning the new object to property UserTypeComboBoxList (without NotifyingPropertyChanged) which would change the reference of property and in this case, XAML binding loose tracks of it property reference until not notify explicitly which is being done by the later property definition.
So the later code of Property definition is correct. The other way of achieving the same behavior is using following (if the bound data can be changed during the program execution):
Use the ObservableCollection which provides the notification
when object is added/deleted into collection
Instead of initializing a new object for property
UserTypeComboBoxList in your action, initialize at Constructor,
and Clears & Add the updated values everytime.
The problem seems to be, that once I create the dialog and InitializeComponent() is called, C# starts to assemble the final View class (asynchronously) and initializes the bindings. So my assignments of the properties of the ViewModel will either by luck be the initial assignment, because it was not already bound, and sometimes it will be an update of the already bound empty initial value.
This can be prevented by initializing the ViewModel before assigning it to the View.
I am trying to get my little program to do some calculations.
So far I have 15 textboxes, named TxtPP(followed by product type),so i got TxtPPproduct1, TxtPPproduct2 etc....
At the bottom of the form I have a disabled textbox which shows the total of all the above textboxes.
I don't want to use a button to do the calculations, I want this to be done every time a value is added to one of the textboxes (so on LostFocus).
Is there a clean way to do this?
To do this, you need to take advantage of set being a method, which means you can raise PropertyChanged for other properties than just the one you are in.
First you need each source textbox to be bound. To get it to update the source on losing input focus, set the UpdateSourceTrigger to LostFocus for example:
<TextBox Text="{Binding FirstSourceValue, UpdateSourceTrigger=LostFocus}"/>
Now, in the setter for the bound member, you need to raise PropertyChanged for the derived value as well, something like:
public double FirstSourceValue
{
get { return firstSourceValue; }
set
{
firstSourceValue = value;
NotifyPropertyChanged(); //Notify for this property
NotifyPropertyChanged("DerivedValue"); //Notify for the other one
}
}
And the derived value property just returns the result of the calculation:
public DerivedValue
{
get { return FirstSourceValue + SecondSourceValue; }
}
Now you can bind your disabled text box to it, and it will update whenever the other text boxes do:
<TextBox IsEnabled="False" Text="{Binding DerivedValue, Mode=OneWay}"/>
Lets assume property Name is bind to TextBox in view like this.
private string name
public string Name
{
get {return name;}
set {
name=value;
OnPropertyChanged("Name");
}
}
View
<TextBox Text="{Binding Name, Mode=TwoWay"/>
When we update the text in text box, it will call the setter in Name property which in turn raise PropertyChanged which suppose to update UI again. I am curious how WPF avoid recursion of update and raise event. is it done by considering the sender of that event?
A standard implementation of a property should look like this:
private string name;
public string Name
{
get { return name; }
set
{
if( name != value )
{
name = value;
OnPropertyChanged("Name");
}
}
}
Note the additional if to make sure the event is only raised if the value of the property actually changed.
There is no recursion as far I understand.
1) TextBox updates the value using viewmodel property.
2) Viewmodel raises update, letting UI know that something changed
3) TextBox now updates himself to match the viewmodel value.
may the answer from here help you out.
if you set a property from ui to viewmodel it goes like this.
setter call started
value set
INotifyPropertyChanged started
INotifyPropertyChanged done
setter done
getter called and done
IDataErrorInfo called and done
but if you set the property in your viewmodel it goes like this
setter call started
value set
INotifyPropertyChanged started
getter called and done
IDataErrorInfo called and done
INotifyPropertyChanged done
setter done
Changing property from UI to ViewModel may lead to deadlock kind of
situation which might run into end less recursive calls in two way
scenarios. In order to block this from happening, when WPF is making
change to model, it will continue to track changes via
INotifyPropertyChanged ,but this change will be queued in dispatcher
queue, and it will be executed after its current update is finished.
Since the change in viewmodel is not initiated by WPF, WPF will not
queue the operation, it will immediately execute the change.