I'm making a test application to handle a CardReader, I have an enum with the states of the CardReader and a XAML window with a TextBlock, i want that when the state change the onPropertyChanged change the TextBlock with the name of the state.
Here is part of my code:
public class CardControler : INotifyPropertyChanged
{
private CardState state;
public CardState State
{
get { return state; }
set
{
if (state != value)
{
state = value;
OnPropertyChanged(state);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(CardState state)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(state.ToString()));
}
}
......................................................................
public partial class CardReader : Window
{
public CardControler control { get; set; }
public CardReader(int port)
{
this.DataContext = control;
this.port = port;
InitializeComponent();
ScreenWrite(CardState.Initializing);
Thread thread = new Thread(new ThreadStart(asincControlCreate));
thread.Start();
}
And in the xaml
<TextBlock Name="Screen" Text="{Binding Path=control.state}></TextBlock>
I hope i explained my self correctly and somebody can help me.
Thanks in advance
The following line is incorrect as you should pass propertyName as a parameter instead of state.ToString():
PropertyChanged(this, new PropertyChangedEventArgs(state.ToString()));
So your code should look something like:
public CardState State
{
get { return state; }
set
{
if (state != value)
{
state = value;
OnPropertyChanged("State");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In addition to this keep in mind that xaml is case sensitive so {Binding State} is not the same as {Binding state}.
I think the problem is that you are raising the OnPropertyChanged with the value that is changing, rather than the actual property name (i.e. "State" in this case).
protected void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I suspect you also need to change your XAML to bind to the appropriate property (note, the property name is State not state - the XAML will be case sensitive):
<TextBlock Name="Screen" Text="{Binding Path=control.State}></TextBlock>
You should pass the name of the property that changed, not its value:
PropertyChanged(this, new PropertyChangedEventArgs("State"));
The case of the property in your binding needs to match the public property (State, not state):
<TextBlock Name="Screen" Text="{Binding Path=control.State}></TextBlock>
You have alread set the datacontext of the page to control so your binding
<TextBlock Name="Screen" Text="{Binding Path=control.state}></TextBlock>
will be wrong.
your binding should be
<TextBlock Name="Screen" Text="{Binding Path=State}></TextBlock>
Instead of
public CardControler control { get; set; }
try this:
public CardControler control = new CardControler();
your OnPopertyChanged Event Call is wrong , you have to pass Property name as the argumeny. you can add the code i added below. that way you can avoid passing the parameter name altogether.
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
Related
I have some code which uses a form. The form is bound to my class, FormData. I have binding working well and updating my formData (local instance), but when I try to change the value of one of the variables in formData on button click/LostFocus trigger, it doesn't update.
Here's my relevant XAML:
<TextBox x:Name="friendly_name_textBox"
Style="{StaticResource TextErrorStyle}"
Text="{Binding
PrimaryUserName,
Mode=TwoWay,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged,
NotifyOnValidationError=True}"
HorizontalAlignment="Left"
Margin="0,75,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"/>`
The button trigger (which does get run):
private void Button_Click(object sender, RoutedEventArgs e)
{
formData.PrimaryUserName = "TEST";
}
And my FormData code:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
}
}
}
You need to implement the INotifyPropertyChanged interface and raise the PropertyChanged event in your formData class:
public class formData : INotifyPropertyChanged
{
private string primaryUserNameValue;
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if (primaryUserNameValue != value)
{
primaryUserNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Class needs to implement INotifyPropertyChanged, so that the target knows if the source property changes:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
It's really easy, please have a look at the documentation and adjust your code accordingly. Your Property would have to look like this:
public string PrimaryUserName
{
get
{
return primaryUserNameValue;
}
set
{
if(primaryUserNameValue != value)
{
primaryUserNameValue = value;
OnPropertyChanged("PrimaryUserName");
}
}
}
But you also need the event and onPropertyChanged function to make it work.
Happy Coding!
I recently started learning Xamarin and I stumbled across the following problem. I have a single label in my XAML file which is bound to a ViewModel property. I am using the ICommand interface to bind a tap gesture to a method in my ViewModel which is supposed to update the label's text. However, it is not updating the "Please touch me once!". I am just wondering what I am doing wrong here?
MainPage xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App1"
x:Class="App1.MainPage">
<Label Text="{Binding MessageContent, Mode=TwoWay}"
VerticalOptions="Center"
HorizontalOptions="Center">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding OnLabelTouchedCmd}" />
</Label.GestureRecognizers>
</Label>
</ContentPage>
Code-behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
}
ViewModel:
class MainPageViewModel : INotifyPropertyChanged
{
private string _messageContent;
public MainPageViewModel()
{
MessageContent = "Please touch me once!";
OnLabelTouchedCmd = new Command(() => { MessageContent = "Hey, stop toutching me!"; });
}
public ICommand OnLabelTouchedCmd { get; private set; }
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged(value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You're calling OnPropertyChanged with a wrong argument as seen here:
protected virtual Void OnPropertyChanged ([System.Runtime.CompilerServices.CallerMemberName] String propertyName)
It expects the name of the property instead of the value you're passing now. Try this instead:
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged("MessageContent");
}
}
Explaination
The current code isn't working because it is passing the value of the property into OnPropertyChanged.
Instead, we need to pass the name of the property as a string into OnPropertyChanged.
Answer
We can take advantage of the CallerMemberName attribute to make the code more concise and to avoid hard-coding strings when calling OnPropertyChanged.
Adding [CallerMemberName] to the parameter of OnPropertyChanged allows you to call OnPropertyChanged() from the setter of the property, and the property name is automatically passed into the argument.
Updated Method
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Updated ViewModel
class MainPageViewModel : INotifyPropertyChanged
{
private string _messageContent;
...
public string MessageContent
{
get => _messageContent;
set
{
_messageContent = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also look at the ViewModelBase located here, have all your ViewModels inherit from it. You can call just OnPropertyChanged, in either of the two ways below. The first of which will just take the name of the calling member, in this case your public property.
OnPropertyChanged();
OnPropertyChanged("MyProperty");
Edit- this is in extension to Brandon's correct answer
I am new to WPF. I have binded the source class properties to target controls successfully. But whenever the properties value changes the UI controls not reflecting the updated data. Please help
WPF Code:
<Label Name="Panel_ch1Mode" Content="{Binding Path=Mode, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" FontFamily="Roboto Regular" FontSize="16" Foreground="#FFFFFF"/>
My Class:
public class ClassName: INotifyPropertyChanged
{
//Auto Refresh
private string mode;
public string Mode
{
get
{
return this.mode;
}
set
{
this.mode = value;
NotifyPropertyChanged("Mode");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Try this: Just remove from xaml the UpdateSourceTrigger=PropertyChanged, Mode=TwoWay. )
Improve your implementation of INotifyPropertyChanged linke this:
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
In code behind please check if the ViewModel is "wired" to the View:
public YourView()
{
InitializeComponent();
DataContext = new ClassName();
}
You have to keep in mind that a binding is always related to DataContext.
I have a ViewModel Called HaemogramViewModel
Here is code:
public class HaemogramViewModel : INotifyPropertyChanged
{
public HaemogramViewModel()
{
}
public Haemogram CurrentHaemogramReport
{
get
{
return MainWindowViewModel.cHaemogram;
}
set
{
MainWindowViewModel.cHaemogram = value;
OnPropertyChanged("CurrentHaemogramReport");
}
}
protected virtual void OnPropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
In my MainWindowViewModel Calss:
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
cHaemogram = new Haemogram();
}
public static Haemogram cHaemogram { get; set; }
private void SaveChanges(object obj)
{
using (Lab_Lite_Entities db = new Lab_Lite_Entities())
{
//db.Patients.Add(CurrentPatient);
if (cHaemogram != null)
{
if (cHaemogram.Haemoglobin != null)
{
db.Haemograms.Add(cHaemogram);
}
}
}
}
}
My textbox is bound to the field Haemoglobin of CurrentHaemogram Property.
When I enter some value in the textbox and then click save button then everything works fine.
Now the problem is :
When I enter some value in the textbox then I press tab and then again click on textbox and then clear the value in the textbox. Now if I click on save button then I don't get the textbox's value = null, instead I get the textbox's value = the value that I entered previously.
Try this it works
<TextBox Text="{Binding B, UpdateSourceTrigger=PropertyChanged, TargetNullValue=''}"/>
and in you view model property should be declared as below
private double? b;
public double? B
{
get
{
return b;
}
set
{
b = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("B"));
}
}
}
In your xmal you have to set the property UpdateSourceTrigger=PropertyChanged as below
<TextBox Text="{Binding Path=Property, UpdateSourceTrigger=PropertyChanged}"/>
By defalut UpdateSourceTrigger=LostFocus, that means the property bound to the textBox will get updated once you press tab or it's focus is lost. If you set to PropertyChanged it will update the property for every char change in the textBox
In my program I would like to disable a contentPresenter when my other contentPresenter gets focus. Each presenter is represented by a property located in my MainWindowViewModel. This is also where the IsEnabled property is located for both presenters.
Both contentPresenters are created with the following structure: UserControl -> ViewModel -> Data Model.
Right now I am trying to disable the necessary contentPresenter by changing the IsEnabled property in the main window's ViewModel from the code-behind of the contentPresenter that gets focus.
contentPresenter User Control code-behind:
public partial class EditBlockUC : UserControl
{
public EditBlockViewModel ViewModel { get { return DataContext as EditBlockViewModel; } }
public EditBlockUC()
{
InitializeComponent();
}
//Runs when the user control gets focus
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
{
//This UserControl has access to MainWindowViewModel through
//it's own ViewModel, EditBlockViewModel
ViewModel.MainViewModel.LeftWidgetEnabled = false;
}
}
The line: ViewModel.MainViewModel.LeftWidgetEnabled = false; successfully changes the property in the Main window's view model, but the view is not affected. Can I fix this by finding a way to call NotifyPropertyChange()? If so, how would I do that?
If this is the completely wrong solution please let me know, and help me fix it.
Thank you
Update 1:
My complete base class:
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void NotifyPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
var lambda = (LambdaExpression)property;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = (UnaryExpression)lambda.Body;
memberExpression = (MemberExpression)unaryExpression.Operand;
}
else
memberExpression = (MemberExpression)lambda.Body;
OnPropertyChanged(memberExpression.Member.Name);
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Update 2:
My LeftWidgetEnabled property:
public bool LeftWidgetEnabled
{
get { return _leftWidgetEnabled; }
set { SetField(ref _leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
The LeftWidgetEnabled of your ViewModel.MainViewModel class must be like this:
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get { return leftWidgetEnabled; }
set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
Also, your MainViewModel must implement INotifyPropertyChanged.
You're better off letting the MainViewModel inherit from a ViewModelBase and let ViewModelBase implement INotifyPropertyChanged.
public class MainViewModel : ViewModelBase
{
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get { return leftWidgetEnabled; }
set { SetField(ref leftWidgetEnabled, value, "LeftWidgetEnabled"); }
}
}
public class ViewModelBase : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Update 1
Your ContentPresenter should then be bound like:
<ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />
while the DataContext of your UserControl (where the ContentPresenter is on) should be an instance of MainViewModel.
For instance:
<UserControl
x:Class="MyApplication.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="**PATH TO YOUR VIEWMODELS-ASSEMBLY**"
mc:Ignorable="d">
<UserControl.DataContext>
<viewModels:MainViewModel />
</UserControl.DataContext>
<ContentPresenter IsEnabled="{Binding Path=LeftWidgetEnabled}" />
</UserControl>
You implement INotifyPropertyChanged as below
class ViewModel : INotifyPropertyChanged
{
private bool leftWidgetEnabled;
public bool LeftWidgetEnabled
{
get
{
return leftWidgetEnabled;
}
set
{
leftWidgetEnabled=value
OnPropertyChanged("LeftWidgetEnabled");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}