Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Notifying change of a calculated databound property in WPF

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!

like image 205
heltonbiker Avatar asked Jan 29 '13 19:01

heltonbiker


People also ask

How do I notify property change in WPF?

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.

What is DataContext in WPF?

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.

What is two way binding in WPF?

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.

What is path binding WPF?

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#.


1 Answers

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")

like image 128
Blachshma Avatar answered Nov 10 '22 03:11

Blachshma