PropertyChangedEventHandler does not work - c#

I have a following label and a slider in my UserControl xaml
<Label x:Name="labelValX" Content="{Binding Path=xValue}" HorizontalAlignment="Left" Width="88" Height="44"/>
<Slider x:Name="sliderSpeed" Value="{Binding slideValue, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top" Width="173" Height="53" Minimum="10" Maximum="100" />
and a specific SetGetAccValues.cs class:
public class SetGetAccValues : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _xval;
public string xValue
{
get { return _xval; }
set
{
if (value != _xval)
{
_xval = value;
OnPropertyChanged("xValue");
}
}
}
public byte _slideValue;
public byte slideValue {
get
{
return _slideValue;
}
set
{
if (value != _slideValue)
{
_slideValue = value;
OnPropertyChanged("slideValue");
}
}
}
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
if (propName.Equals("slideValue"))
{
speedAccMeter(slideValue);
}
}
}
An in my other GetAccNotifications.cs class, I have following part, where I'm defining my xValue string to a specific value:
Y = ((double)(sbyte)value) / 64.0;
Y = Math.Round(Y, 2);
SetGetAccValues set = new SetGetAccValues();
set.xValue = Y.ToString();
The problem occurs when the OnPropertyChanged is triggered with "xValue" as the propName, the PropertyChangedEventHandler remains always null, but when it is triggered with "slideValue" as propName it is actually not null. Why does it remain null in the xValue case ?.

I believe the PropertyChanged event is firing before the datacontext is finished loading.
You can listen to the DataContextChanged event in your usercontrol, so that when the new datacontext is available, you can set your properties.
public AccView()
{
InitializeComponent();
this.DataContextChanged += OnDataContextChanged;
this.DataContext = new SetGetAccValues();
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Y = ((double)(sbyte)value) / 64.0;
Y = Math.Round(Y, 2);
(dependencyPropertyChangedEventArgs.NewValue as SetGetAccValues).xValue = Y.ToString();
}

I think you didn't bind DataContext. You should set DataContext with code behind in your case.
In SetGetAccValues.xaml.cs
public SetGetAccValues()
{
DataContext = this;
}

Related

Binding a string variable to a label

I am trying to move from WinForms to WPF, and am stuck on binding.
I have a label:
<Label Name="labelState" Content="{Binding state}" HorizontalAlignment="Right" Margin="10,10,10,10" FontSize="12" />
In the cs of the same userControl (named FormInput), I have :
public string state { get; set; }
public FormInput()
{
state = "ok";
InitializeComponent();
}
Why doesn't this work?
Thank you.
When you are binding something in WPF you need to use INotifyPropertyChanged
Implement a class follows,
class TestObject : INotifyPropertyChanged
{
private string _state;
public string State
{
get
{
return _state;
}
set
{
if (_state == value) return;
_state = value;
OnPropertyChanged("State");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
and in your FormInput
public FormInput()
{
InitializeComponent();
TestObject t = new TestObject();
labelState.DataContext = t;
t.State = "ok";
}
and XAML as follows,
<Label Name="labelState" Content="{Binding State}" HorizontalAlignment="Right" >

XAML Binding not updated

I have an usercontrol in a UWP who I place in other user controls who have two text TextBlocks who are bound to the VM.
Here is the XAML Code:
DataContext
DataContext="{Binding BalanceView, Source={StaticResource CoreModule}}"
<TextBlock Text="{Binding TotalBalance, Mode=TwoWay, Converter={StaticResource AmountFormatConverter}, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
<TextBlock Text=" / "
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
<TextBlock Text="{Binding EndOfMonthBalance, Mode=TwoWay, Converter={StaticResource AmountFormatConverter}, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource DeemphasizedBodyTextBlockStyle}"
Margin="0,0,5,0" />
And the VM Properties there bound to:
public double TotalBalance
{
get { return totalBalance; }
set
{
if (Math.Abs(totalBalance - value) < 0.01) return;
totalBalance = value;
RaisePropertyChanged();
}
}
public double EndOfMonthBalance
{
get { return endOfMonthBalance; }
set
{
if (Math.Abs(endOfMonthBalance - value) < 0.01) return;
endOfMonthBalance = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
I can see that the value returned is correct. But on the UI it's permanently on 0. If I set the value staticly to a value it's shown properly.
What is wrong?
I would suspect you have these double values as private properties which are not shown here, but are referenced such as "totalBalance" vs PUBLIC "TotalBalance" and similar with "endOfMonthBalance" vs "EndOfMonthBalance", otherwise it would not compile.
Also, shouldn't your RaisePropertyChanged() call
RaisePropertyChanged("TotalBalance") and RaisePropertyChanged("EndOfMonthBalance") vs a blanket call via no parameter.
Call OnPropertyChanged passing the name of the CLR property as the paramter.
private double totalBalance=0;
public double TotalBalance
{
get { return totalBalance; }
set
{
if (Math.Abs(totalBalance - value) < 0.01) return;
totalBalance = value;
OnPropertyChanged("TotalBalance");
}
}
private double endOfMonthBalance=0;
public double EndOfMonthBalance
{
get { return endOfMonthBalance; }
set
{
if (Math.Abs(endOfMonthBalance - value) < 0.01) return;
endOfMonthBalance = value;
OnPropertyChanged("EndOfMonthBalance");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}

Binding on Indexed properties UserControl

I created UserControl.
public partial class Line : UserControl, INotifyPropertyChanged
{
ObservableCollection < Point > points = new ObservableCollection< Point >();
public static readonly DependencyProperty SpeciesPropertyPoints = DependencyProperty.Register("Points", typeof(ObservableCollection<Point>),
typeof(Line), null);
public ObservableCollection<PointPath> Points
{
get { return (ObservableCollection<Point>)GetValue(SpeciesPropertyPoints); }
set
{
SetValue(SpeciesPropertyPoints, (ObservableCollection<Point>)value);
NotifyPropertyChanged("Points");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var point = new Point(100, 50);
points.Add(point);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Point: INotifyPropertyChanged
{
private double _x;
public double X
{
get { return _x; }
set { _x = value; }
}
private double _y;
public double Y
{
get { return _y; }
set { _y = value; }
}
public Point()
{
X = 0;
Y = 0;
}
public Point(double x, double y)
{
X = x;
Y = y;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
Button Click="Button_Click"
TextBox x:Name="x" Width="20" Text="{Binding Points[0].X}"
TextBox x:Name="y" Width="20" Text="{Binding Points[0].Y}"
I want after clicking on button in TexBox with name "x" display Points[0].X (ie 100) and in TexBox with name "y" display Points[0].Y (ie 50). Please help me to understand.
You should change Text binding:
TextBox x:Name="x" Width="20" Text="{Binding Path=Points[0].X}"
TextBox x:Name="y" Width="20" Text="{Binding Path=Points[0].Y}"
And your DP implementation is wrong,
should be like this
public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(
"Points", typeof (ObservableCollection<Point>), typeof (Line), new PropertyMetadata(default(ObservableCollection<Point>)));
public ObservableCollection<Point> Points
{
get { return (ObservableCollection<Point>) GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
}
You should not write logic in DP setter, because its just CLR Property wrapper and it's not guaranteed that setter will be called in some cases!
EDIT:

Two way Binding

I have some code and I'm having problems trying to change the value of the class 'Player'
My XAML
<Rectangle x:Name="oChar" Fill="Orange" Height="32" Width="32" Canvas.Left="32" Canvas.Top="{Binding Mode=TwoWay}"/>
Code behind:
private Player myPlayer = new Player { left = 32, top = 128 };
public MainPage()
{
this.InitializeComponent();
this.DataContext = myPlayer.top;
myPlayer.top += 32;
}
Class INotiftyProperty
private int _top;
public int top
{
get { return _top; }
set { _top = value; OnPropertyChanged("top"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
First time doing stuff like WPF, any help on why the value wont change or any information would be great!
You should use this.Datacontext =myPlayer;

WPF Binding to a property in second ViewModel

How do I get the text bound to txtMessage from the second view model? When I had only one view model, the text was working fine. It does not work anymore when I moved the actual download code to second view model. Am I missing something? Any help appreciated.
Xaml:
<DockPanel DockPanel.Dock="Top">
<TextBlock x:Name="txtMessage" DockPanel.Dock="Top" Margin="5" Text="{Binding viewModel1.Message}" />
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5,5">
<ProgressBar Width="300" Visibility="{Binding IsDownloading, Converter={converter:VisibilityConverter}}" IsIndeterminate="True" />
<Button Content="Cancel" />
</StackPanel>
</DockPanel>
<Button Content="Download" Width="120" Margin="0,0,5,0" Name="btnSubmit" Click="btnSubmit_Click" />
CodeBehind:
public partial class DownloadWindow: Window
{
DownloadWindowViewModel viewModel = new DownloadWindowViewModel();
public DownloadWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
viewModel.IsDownloading = true;
viewModel.Download();
}
}
viewModel:
public class DownloadWindowViewModel: INotifyPropertyChanged
{
Thread downloadThread;
public DownloadViewModel viewModel1;
public DownloadWindowViewModel()
{
viewModel1 = new DownloadViewModel();
}
private bool _isDownloading; = false;
public bool IsDownloading
{
get
{
return _isDownloading;
}
set
{
_isDownloading; = value;
OnPropertyChanged("IsDownloading");
}
}
public void Download()
{
viewModel1.Download();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
viewModel1:
public class DownloadViewModel: INotifyPropertyChanged
{
Thread _thread;
public void Download()
{
ThreadStart threadStart = delegate()
{
StartDownload();
};
_thread = new Thread(threadStart);
_thread.IsBackground = true;
_thread.Start();
}
private void StartDownload()
{
for (int i = 10; i < 1500; i++)
{
Thread.Sleep(5000);
Message = "Downloading " + i.ToString();
}
}
private string _message = "";
public string Message
{
get
{
return _message;
}
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Your viewModel1 has to be a property, and it's a field at the moment. Change it to:
public DownloadViewModel viewModel1 { get; set; }
Explanation why such restriction exists, can be found here (primarily due to notification/verifications mechanisms simply not working for fields):
Why does WPF support binding to properties of an object, but not fields?

Categories