I have a problem with two way binding a TextBox content to a property in another class. Searching stackoverflow gave a lot of tips/solutions but none seem to work.
In my XAML code I have:
< TextBox ... Width="336" IsReadOnly="True"
Text="{Binding Path=AssignedClearProgram, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
(... I removed all non important items)
In the accompanies cs code I have:
public CombiWindow(Combi combi)
{
ViewModel = new CombiViewModel(combi);
DataContext = ViewModel;
}
In the CombiViewModel:
[UsedImplicitly]
public string AssignedClearProgram { get; set; }
It seems that the first time I assign AssignedClearProgram, the textbox is filled with the text that I set, however after the window is displayed and AssignedClearProgram gets updated from the code (i.e. the set method is called), the data is not updated in the screen.
Does anybody have a solution to update the textbox when this variable is changed?
Kind regards,
Michel
Your viewmodel class needs to implement INotifyPropertyChanged and you need to raise that interface's event whenever you change the property. Then the binding will spot changes and update the textbox.
Your view model class should implement the INotifyPropertyChanged interface.
Your property would then look like the following:
private string assignedClearProgram;
public string AssignedClearProgram
{
get { return assignedClearProgram; }
set
{
if (assignedClearProgram != value)
{
assignedClearProgram = value;
// Notify property has changed here using PropertyChanged event from INotifyPropertyChanged.
}
}
}
Read this article for an example of how to implement the INotifyPropertyChanged interface and utilize its PropertyChanged event.
Related
Can someone explain me why need to use implementation of INotifyPropertyChanged when using binding in wpf?
I can bind properties without implementation of this interface?
For example i have code
public class StudentData : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
string _firstName = null;
public string StudentFirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}
And binding in .xaml
<TextBox Text="{Binding Path=StudentFirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center" />
this code from .xaml.cs
StudentData _studentData = new StudentData { StudentFirstName = "John", StudentGradePointAverage = 3.5};
public MainWindow()
{
InitializeComponent();
this.DataContext = _studentData;
}
why we need to use INotifyPropertyChanged in this case?
It is not my code.
You need INotifyPropertyChanged if you want a wpf form to be automatically updated when a property changes through code. Also some controllers might want to know if edits have been made in order to enable/disable a save-button, for instance. You also might be displaying the same property on different views; in this case INotifyPropertyChanged helps to immediately update the other view when you edit a property.
If you think that your form behaves well without INotifyPropertyChanged, then you can drop it.
Note that binding works even without INotifyPropertyChanged. See: Why does the binding update without implementing INotifyPropertyChanged?
I would implement the properties like this. In some rare cases it can help to avoid endless circular updates. And it is more efficient by the way.
private string _firstName;
public string StudentFirstName
{
get { return _firstName; }
set
{
if (value != _firstName) {
_firstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}
Starting with C#6.0 (VS 2015), you can implement OnPropertyChanged like this:
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When you bind to a property of StudentData such as the StudentFirstName then the binding class tests to see if the StudentData instance provides the INotifyPropertyChanged interface. If so then it will hook into the PropertyChanged event. When the event fires and it fires because of the StudentFirstName property then it knows it needs to recover the source value again because it has changed. This is how the binding is able to monitor changes in the source and reflect them in the user interface.
If you do not provide the INotifyPropertyChanged interface then the binding has no idea when the source value changes. In which case the user interface will not update when the property is changed. You will only see the initial value that was defined when the binding was first used.
It does need to be implemented in order for binding to work but that doesn't mean you always have to do it yourself. There are other options like Castle Dynamic Proxy (which wraps your classes in a proxy and injects INPC into all virtual properties) and Fody (which adds it to the IL in a post-processing step). It's also possible to implement yourself while at the same time reducing code bloat, as demonstrated in my answer to this question.
I'm using x:Bind and INotifyPropertyChanged to update UI in UWP application. But it behaves like OneTime binding even though I set it to OneWay.
Bindings.Update() works, but I want to know why INotifyPropertyChanged fails.
XAML
<TextBlock Text="{x:Bind staffVM.Name, Mode=OneWay}"/>
Code-behind:
private StaffViewModel staffVM;
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// I want to change staffVM according to ListView's selection.
staffVM = staffListView.SelectedItem as StaffViewModel;
staffVM.Update(); // If change this to Bindings.Update(), It works.
}
ViewModel:
public class StaffViewModel: INotifyPropertyChanged
{
private Character character;
public string Name => character.name == string.Empty ? null : character.name;
public void Update()
{
RaisePropertyChanged(string.Empty);
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName]string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
Firstly, you need to specify the name of the variable that you want to update:
public void Update()
{
RaisePropertyChanged(nameof(Name));
}
Documentation and sample: https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged
Secondly, by default x:Bind is OneTime
To fix it, add Mode="OneWay"
Mode Specifies the binding mode, as one of these strings: "OneTime", "OneWay", or "TwoWay". The default is "OneTime". Note that this differs from the default for {Binding}, which is "OneWay" in most cases.
Please read documentation
https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
The problem here is not on the level of the StaffViewModel class, but on the level of the page. When you do:
staffVM = staffListView.SelectedItem as StaffViewModel;
The UI has no notification about the fact that the staffVM field has changed. So the binding is still pointing to the old instance of StaffViewModel. Hence when you do staffVM.Update(), it does notify about changes, but the UI is not listening to that instance - it is still listening to notifications on the first selected item. Bindings.Update() fixes this because it completely re-evaluates all bindings so it will "get" the new value of staffVM field.
Solution would be to implement INotifyPropertyChanged on the Page and encapsulate the staffVM in a property which raises PropertyChanged event.
Ideally I would however suggest creating a "root" view model, which you will set only once and will not change and which will contain the selected item as its property. This way you don't have to implement INotifyPropertyChanged in the Page and its code-behind will be simpler. As a result you will have something like the following in the code-behind:
public RootViewModel VM {get;} = new RootViewModel();
And in XAML:
<TextBlock Text="{x:Bind VM.SelectedStaff.Name, Mode=OneWay}"/>
I'm working on a MVVM WPF application where i bind several values from ViewModel to View.
Now i have created a new ViewModel where i have to bind a value to a TextBox after a Button click. When i tried this simple binding it didn't work for me. To my surprise the binding works when the value is assigned in the constructor.
I'm confused.
ViewModel:
public ABCViewModel{
txtItems = "Hello world"; //this works
}
private string m_stxtItem = "";
public string txtItems
{
get { return this.m_stxtItem; }
set
{
if (this.m_stxtItem != value)
{
this.m_stxtItem = value;
}
}
}
public ICommand BindTextValue { get { return new RelayCommand(SeriesBinding); } }
private void SeriesBinding()
{
txtItems = "Hi"; //does not work
}
XAML:
<TextBox Text="{Binding txtItems,Source={StaticResource ABCViewModel}}" />
<Button Command="{Binding BindTextValue,Source={StaticResource ABCViewModel}}">Click</Button>
Why this didn't work and where am i wrong?
Simply answer: you are missing the INotifyPropertyChanged-implementation required for automatic data binding.
Extended answer to why it works when setting the value in the constructor:
the initial binding (reading of the value) from the view happens AFTER your ViewModel-constructor was called and your value was set
You need to implement the INotifyPropertyChanged interface on your ViewModel.
Here's some MSDN links on what the interface is and how to implement it:
INotifyPropertyChanged Interface
How to: Implement the INotifyPropertyChanged Interface
Basically because you've set the ViewModel as the DataSource for the View, upon construction, the View will look to the ViewModel for its values. From this point onwards the ViewModel needs some mechanism for notifying the View of changes. The View is not periodically updated or anything like that. This is where the INotifyPropertyChanged interface steps in. The WPF framework looks out for these events being fired and makes the View refresh its value.
My label only seems to get the data from the property it is bound to once. I have the Property raising the Property Changed event in the setter, but when the value of the property gets changed, it raises the event properly (I know this because of the break point I set), but the text in the Label on the window doesn't change. I should maybe also note that the window with the label isn't the main window, but a new one that pops up.
ViewModel:
public class PurchaseVerificationViewModel : ViewModelBase
{
private WindowService.WindowService windowService = new WindowService.WindowService();
private string _verificationQuestion = "Question"; //default so i can check if it changed in the window
public string VerificationQuestion
{
get { return _verificationQuestion; }
set
{
if (_verificationQuestion != value)
{
_verificationQuestion = value;
OnPropertyChanged(nameof(VerificationQuestion));
}
}
}
}
Window:
<Window>
<Window.DataContext>
<viewmodels:PurchaseVerificationViewModel/>
</Window.DataContext>
<Grid>
<Label Content="{Binding VerificationQuestion, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
There's no problem with your implementation of the INotifyPropertyChanged, since you are correctly invoking the notification that your property has been modified.
So if the problem is not with the one who's raising the notification, might it rather be with what is actively listening to it?
And the problem is that you're defining the DataContext of your Window to the class itself, rather than to the instance which you are utilizing and modifying in the code-behind of your application.
What is actually happening under the hoods, due to the way you defined your DataContext in xaml, is that a new PurchaseVerificationViewModel class is being constructed (is the not the one who are modifying on your logic) and therefore your VerificationQuestion will return it's default value (or rather the private backing field default value, "Question").
In reality the problem is that you have induced your listener to listen to the wrong thing.
Since you want the content of the Label (target) to be update based on a source change, what you have to do, is to set as the DataContextof the Window the specific instance which you are modifying on the logic of your application, and make sure you define it as a property!
public PurchaseVerificationViewModel myViewModel {get;set;}
For instance after InitializeComponent(), on your page constructor, you could initialize the property and set it as the DataContext, like this:
myViewModel = new PurchaseVerificationViewModel();
this.DataContext = myViewModel;
I am trying to change multiple buttons visibility from another class. This is supposed to be easy, but I just don't understand it.
The xaml part is straight forward:
<button x:Name="whatever" Visibility="{Binding whateverName}"
The view-model could be something like this?
private Visibility vis;
public Visibility Vis
{
get { return vis; }
set { vis = value; }
}
But if that is the case, how do I pass my button name?
To go a bit further, a services file is trying to modify the visibility value..
Thanks in advance.
Since you're using Bindings, you don't need the button name identifier.
The connection is made in the Binding part of the XAML:
<Button x:Name="whatever" Visibility="{Binding whateverName}"/>
What is happening there is that you are saying the Visibility property of the whatever button will be bound to the whateverName property value in your view model.
So your View model needs to look like this:
private Visibility vis;
public Visibility whateverName
{
get { return vis; }
set { vis = value; }
}
To change the visibility of your button you need to change the value of whateverName in your view model.
However, if you try, you'll notice that that won't work. The reason is that in order for the change to take effect on the button, the View model must notify the view that its property has changed. This is done with the INotifyPropertyChanged interface.
So your view model will need to look something like this:
public class Viewmodel : INotifyPropertyChanged
{
private Visibility vis;
public Visibility whateverName
{
get { return vis; }
set
{
vis = value;
OnPropertyChanged("whateverName");
}
}
public void OnPropertyChanged(string pName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
In the PropertyChanged event you must pass the property name that you want to notify. In my example I just used a string value that matches the property name but there are various techniques to eliminate that "magic string".
Here's one SO question that has good answers.