As an exercise I decided to create a bicycle gear calculator in WPF. I created two private fields with setters that trigger OnPropertyChanged()
, but I have one databound property, ratio
, which behaves as "readonly", because it is dynamically calculated. When I run the program, the textboxes show, the initial values are correctly displayed, the "working" word in the property changed handler is displayed, but the ratio
TextBlock doesn't update.
I suspect this is due to the way the property is "getted", I wonder if it would be absolutely necessary to add a private field for every, I wonder if there should be any DependencyProperty in this... But actually I have reached my knowledge limit on this and cannot get this trivial program to work.
This is my model:
class SingleGearsetModel : INotifyPropertyChanged
{
public SingleGearsetModel()
{
crank = 44;
cog = 16;
}
private int _crank;
private int _cog;
public int crank {
get{return _crank;}
set{
_crank = value;
OnPropertyChanged("crank");
}
}
public int cog {
get{return _cog;}
set{
_cog = value;
OnPropertyChanged("cog");
}
}
public double ratio
{
get {
return (double)crank / (double)cog;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string arg)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(arg));
Console.Writeline("working");
}
}
} // end class
This is my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="CalculadorFixaWPF.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<DockPanel x:Name="LayoutRoot">
<TextBox Text="{Binding crank, Mode=TwoWay}"/>
<TextBox Text="{Binding cog, Mode=TwoWay}"/>
<TextBlock Text="{Binding ratio, StringFormat={}{0:0.00}}"/>
</DockPanel>
</Window>
And this is in my code-behind (MainWindow.xaml.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
this.DataContext = new SingleGearsetModel();
}
}
Thanks for reading!
To implement INotifyPropertyChanged you need to declare the PropertyChanged event and create the OnPropertyChanged method. Then for each property you want change notifications for, you call OnPropertyChanged whenever the property is updated.
The DataContext property is the default source of your bindings, unless you specifically declare another source, like we did in the previous chapter with the ElementName property. It's defined on the FrameworkElement class, which most UI controls, including the WPF Window, inherits from.
Two way binding is used when we want to update some controls property when some other related controls property change and when source property change the actual control also updates its property.
Binding path syntax. Use the Path property to specify the source value you want to bind to: In the simplest case, the Path property value is the name of the property of the source object to use for the binding, such as Path=PropertyName . Subproperties of a property can be specified by a similar syntax as in C#.
Since Ratio is a calculated field, you want to add a notification every time the value might change. This will happen if crank
or cog
are changed.
So, after you notify on them being changed, also notify thatratio
has changed:
public int crank
{
get{return _crank;}
set{
_crank = value;
OnPropertyChanged("crank");
// *** Notify that ratio has also been changed ***
OnPropertyChanged("ratio");
}
}
Same goes for cog
.
Edit: Based on your comments, here is how you can register to the PropertyChanged event from inside your class and raise a PropertyChanged for ratio:
// Constructor
public SingleGearsetModel()
{
....
PropertyChanged += SingleGearsetModel_PropertyChanged;
}
// PropertyChanged event handler
void SingleGearsetModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "cog" || e.PropertyName == "crank")
{
OnPropertyChanged("ratio");
}
}
Using this approach, you don't need to add the OnPropertyChanged("ratio")
inside the setters of the properties - whenever they are raised, you get the event(in SingleGearsetModel_PropertyChanged
) and raise the OnPropertyChanged("ratio")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With