OnPropertyChanged() not working in MVVM - c#

I searched through all other question with the same problem. But I can't find any solution from them.
OnPropertyChanged is firing but the Control is not updating. I'm using the Mahapps.Metro ProgressRing Control.
View Code
<controls:MetroWindow.Resources>
<userObj:LoginViewLogic x:Key="UserData"/>
<userObj:LoginViewLogic x:Key="LoginViewLogic"/>
</controls:MetroWindow.Resources>
<Grid>
<Canvas>
<controls:ProgressRing Name="ProgressRing" Canvas.Left="133" Canvas.Top="154" Height="50" Width="35" IsActive="{Binding Source={StaticResource UserData},Path=UserData.IsProgressRingActive}"/>
</Canvas>
</Grid>
ViewModel Code
public class LoginViewLogic {
public LoginViewLogic() {
_userData = new User(AppSettings.ReadCredentials(),(bool)loadedSettings);
}
private User _userData;
public User UserData
{
get { return _userData; }
set { _userData = value; }
}
public async void Login() {
_userData.IsProgressRingActive = true;
var loginResult = await Stuff.Login(_userData);
if (!loginResult) {
MessageBox.Show("You have entered an invalid username or password",
"Information", MessageBoxButton.OK, MessageBoxImage.Error);
_userData.IsProgressRingActive = false;
}
}
Model Code
public class User : INotifyPropertyChanged {
private bool _isProgressRingActive;
public bool IsProgressRingActive {
get { return _isProgressRingActive; }
set {
_isProgressRingActive = value;
OnPropertyChanged("IsProgressRingActive");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Have you tried this:
<controls:MetroWindow.Resources>
<userObj:LoginViewLogic x:Key="LoginViewLogic"/>
</controls:MetroWindow.Resources>
<Grid>
<Canvas>
<controls:ProgressRing Name="ProgressRing" Canvas.Left="133" Canvas.Top="154" Height="50" Width="35" IsActive="{Binding Source={StaticResource LoginViewLogic},Path=UserData.IsProgressRingActive}"/>
</Canvas>
</Grid>
So you are only creating and binding to one instance of LoginViewLogic class?

Your viewmodel should also raise a PropertyChanged event.
Try with below code.
public class LoginViewLogic : INotifyPropertyChanged
{
public LoginViewLogic()
{
_userData = new User(AppSettings.ReadCredentials(),(bool)loadedSettings);
}
private User _userData;
public User UserData
{
get { return _userData; }
set { _userData = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Related

Create a binding between properties

I have a Listbox where it's items are objects. In these objects I store two colors.
I want to bind these colors with an other object's property, but how can I achieve this?
The listbox looks like this:
Listbox1.Items.Add(new ColorAndMoreClass(Color.Red, Color.Blue));
Far away, in an other class there is a property which I'd like to bind to.
How can I do that?
Your rootclass could look like this.
In the class you have a object representing a different Class.
public class ColorAndMoreClass: INotifyPropertyChanged
{
private Color _c;
private Color _c2;
private OtherClass _example;
public ColorAndMoreClass(Color c, Color c2)
{
_c= c;
_c2 = c2;
}
public OtherClass example
{
get { return _example }
set
{
_example = value;
OnPropertyChanged("example");
}
}
public Color c
{
get { return _c; }
set
{
_c= value;
OnPropertyChanged("c");
}
}
public Color c2
{
get { return _c2; }
set
{
_c2 = value;
OnPropertyChanged("c2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Your other class could look like this. I just took a simple string.
public class OtherClass : INotifyPropertyChanged
{
private String _someOtherProperty;
public OtherClass(){}
public String someOtherProperty
{
get { return _someOtherProperty; }
set
{
_someOtherProperty= value;
OnPropertyChanged("someOtherProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
In your Code behind make a property the Listbox can bind to
public List<ColorAndMoreClass>> ListOfColorAndMore{ get; set; }
public Window1()
{
ListOfColorAndMore = GetDataThatFillsUpTheProperty();
InitializeComponent();
DataContext = this;
}
Your XAML could then look like this. The Datatemplate is used to tell XAML how to display your object.
<Grid>
<ListBox ItemsSource={Binding ListOfColorAndMore}>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=c.R}" />
<TextBlock Text="{Binding Path=c2.R}"/>
<TextBlock Text="{Binding Path=example.someOtherProperty}"/>
</StackPanel>
</DataTemplate>
</ListBox>
</Grid>
I hope it is this that you mean. But your question is not that clear.

Command run after application run

I noticed that it happends not only in the one project but on multiple too so I will provide simple example. I've got such xaml:
<Page
x:Class="TestApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Button Content="Button" Command="{Binding PressedButton}" HorizontalAlignment="Left" Margin="0,-10,0,-9" VerticalAlignment="Top" Height="659" Width="400"/>
</Grid>
</Page>
my classes to binding data:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
}
public class Command : ICommand
{
private Action<object> action;
public Command(Action<object> action)
{
this.action = action;
}
public bool CanExecute(object parameter)
{
if (action != null)
{
return true;
}
else
{
return false;
}
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (action != null)
{
action((string)parameter);
}
}
}
public class TestViewModel : ObservableObject
{
public ICommand PressedButton
{
get
{
return new Command((param) => { });
}
}
}
and main page:
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
DataContext = new TestViewModel();
}
It's weird but PressedButton runs only on application start(isn't that weird it runs on start?). After that, even after button click there is nothing triggered. I can't figure out what's wrong.
I think you may be causing binding issues by returning a new command each time the "getter" is called. Try setting the command once, in your constructor (for example).
public MainPage()
{
PressedAdd = new Command(param => SaveNote());
}
public ICommand PressedAdd { get; private set; }
In the SaveNote() method, you could test the values and either save (or not save) them:
private void SaveNote()
{
if (NoteTitle == null || NoteContent == null)
return;
// Do something with NoteTitle and NoteContent
}

Bindings on a ContentControl do not update the data

I have the code:
<TextBox Width="200" Text="{Binding Value}"></TextBox>
Which works. However the "Value" can be different types. So if I have an bool I want to show a checkbox. I rewrote it as as follow, which kinda works:
<ContentControl Content="{Binding Value}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}"></CheckBox>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Double}">
<TextBox Width="200" Text="{Binding Path=.}"></TextBox>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
But now the property isn't updated like before. I have tried setting Mode=Twoway, but it still do not work.
Edit
It worked perfectly fine when I only had the textbox, editing the text of the textbox updated the model. However when I tried doing this with the second code (ContentControl) it just doesn't work.
Code
I'm using Mvvm-light togheter with bindings. The "Value" is bound to an instance of Property
[JsonObject]
public class Property<T> : INotifyPropertyChanged
{
[JsonProperty]
public String name;
public Property(String name, T value)
{
this._value = value;
this.name = name;
}
[JsonIgnore]
public T Value {
get { return _value; }
set {
_value = value;
hot = true;
NotifyPropertyChanged("Value");
}
}
[JsonProperty(PropertyName = "value")]
private T _value;
[JsonIgnore]
public String Name { get { return name; } set { name = value; } }
[JsonProperty]
public bool hot = false;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should implement INotifyPropertyChanged interface in order to track property changes. I'm sure everything works fine then.
This works for me:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private object value;
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
DataContext = this;
}
public object Value
{
get { return value; }
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Value = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Updating a control after INotifyPropertyChanged

I've checked the existing answers on Stack and still can't get this correct:
In my View:
<TextBlock Margin="8,0,0,0"
FontSize="48"
Text="{Binding YearsToSave}"
d:LayoutOverrides="Width">
...
<SurfaceControls:SurfaceSlider x:Name="slider" Grid.Row="8"
Grid.Column="2"
VerticalAlignment="Bottom"
Maximum="{Binding YearsToSaveMaxValue}"
Minimum="{Binding YearsToSaveMinValue}"
Value="{Binding YearsToSave}"
d:LayoutOverrides="Width" />
In my view model:
class YearsToSaveViewModel : INotifyPropertyChanged
{
private int yearsToSave;
public event PropertyChangedEventHandler PropertyChanged;
public YearsToSaveViewModel()
{
Questions = new SavingsCalculatorQuestions();
YearsToSave = 5; //Binds correctly
YearsToSaveMinValue = 0;
YearsToSaveMaxValue = 30;
}
public SavingsCalculatorQuestions Questions { get; set; }
public int YearsToSaveMinValue { get; private set; }
public int YearsToSaveMaxValue { get; private set; }
public int YearsToSave
{
get { return yearsToSave; }
set
{
yearsToSave = value;
OnPropertyChanged("YearsToSave");
}
}
public void Reset()
{
YearsToSave = 0;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
switch (name)
{
case "YearsToSave":
Questions.NumberOfYears = YearsToSave;
break;
}
}
}
}
The property changed event fires correctly and gets the value, updates the Questions.NumberOfYears correctly but the change never propagates back to the view.
Your OnPropertyChanged method is not raising the PropertyChanged event...
Update your method like so:
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
switch (name)
{
case "YearsToSave":
Questions.NumberOfYears = YearsToSave;
handler(this, new PropertyChangedEventArgs(name));
break;
}
}
}
Another option is using the NotifyPropertyWeaver Project!
I like it,because it automatically does the Event call for you!
(a bit of black magic but convenient)
See
http://crosscuttingconcerns.com/NotifyPropertyWeaver

INotifyPropertyChanged Not Working

I am using INotifyPropertyChanged but it will give me null when I shaw the PropertyChanged so what i can do..
my code is like this..
public class Entities : INotifyPropertyChanged
{
public Entities(int iCount)
{
_iCounter = iCount;
}
private int _iCounter;
public int iCounter
{
get
{
return _iCounter;
}
set
{
value = _iCounter;
NotifyPropertyChanged("iCounter");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Thanks...
I tried putting your code in my program and it is working fine. I am getting the EventArg as the property:
class Program
{
static void Main(string[] args)
{
var ent = new Entities(10);
ent.PropertyChanged += new PropertyChangedEventHandler(ent_PropertyChanged);
ent.iCounter = 100;
}
static void ent_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
}
public class Entities : INotifyPropertyChanged
{
public Entities(int iCount)
{
_iCounter = iCount;
}
private int _iCounter;
public int iCounter
{
get
{
return _iCounter;
}
set
{
_iCounter = value;
NotifyPropertyChanged("iCounter");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
What is the exact erro you are getting?
This is i think a bug in INotifyPropertyChanged .
There can be 2 workaround
1st Workaround
1- Assign iCounter property to a UI control like Lable.
2- Now change the value of the property this time , PropertyChanged event will have a reference of your method and will not be null;
2nd workaround
Assign PropertyChanged delegate in the Entities class constructor
i am giving the demo code in WPF
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.Resources>
<ToolTip x:Key="#tooltip">
<TextBlock Text="{Binding CompanyName}"/>
</ToolTip>
</Grid.Resources>
<TextBlock Text="{Binding Name}" Background="LightCoral" />
<Rectangle Width="200" Height="200" Fill="LightBlue" VerticalAlignment="Center" HorizontalAlignment="Center" ToolTip="{DynamicResource #tooltip}" Grid.Row="1"/>
<Button Click="Button_Click" Grid.Row="2" Margin="20">Click Me</Button>
</Grid>
see here CompanyName is assigned to a tool tip.
// this is Window1.Cs file
public Window1()
{
DataContext = DemoCustomer.CreateNewCustomer();
InitializeComponent();
}
// Now DemoCustomer Class
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerName = String.Empty;
private string companyNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer()
{
customerName = "no data";
companyNameValue = "no data";
phoneNumberValue = "no data";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CompanyName
{
get { return this.companyNameValue; }
set
{
if (value != this.companyNameValue)
{
this.companyNameValue = value;
OnPropertyChanged("CompanyName");
}
}
}
public string PhoneNumber
{
get { return this.phoneNumberValue; }
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
OnPropertyChanged("PhoneNumber");
}
}
}
}
and finally changing the value
private void Button_Click(object sender, RoutedEventArgs e)
{
DemoCustomer dc = this.DataContext as DemoCustomer;
dc.CompanyName = "Temp";
}

Categories

Resources