Home > OS >  Is there an easier way to update related properties in WPF?
Is there an easier way to update related properties in WPF?

Time:09-16

I have a UserControl that displays a rating as number of stars. It does this by binding a TextBlock’s Text property to a regular code-behind property which in turn uses an integer DependencyProperty Value.

In order to update the TextBlock when Value changes, I need to manually trigger the INotifyPropertyChanged.PropertyChanged event from the DependencyProperty’s PropertyChangedCallback.

This feels exceedingly excessive. Is there an easier to way to accomplish this?

<TextBlock Text="{Binding RatingDisplay, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Rating}}}" />
public partial class Rating : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(int), typeof(Rating), new PropertyMetadata(0, (sender, e) =>
        {
            ((Rating)sender).RaisePropertyChanged(nameof(RatingDisplay));
        }));

    public Rating()
    {
        InitializeComponent();
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public int Value
    {
        get => (int)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    public string RatingDisplay => new string('*', Value);

    protected void RaisePropertyChanged(string? propertyName = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

CodePudding user response:

You do not need to implement INotifyPropertyChanged in a class that exposes dependency properties. Dependency properties provide their own change notification mechanism.

In order to update a read-only dependency property, something like this should work:

public partial class Rating : UserControl
{
    private static readonly DependencyPropertyKey RatingDisplayPropertyKey =
        DependencyProperty.RegisterReadOnly(
            nameof(RatingDisplay), typeof(string), typeof(Rating), null);

    public static readonly DependencyProperty RatingDisplayProperty =
        RatingDisplayPropertyKey.DependencyProperty;

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(
            nameof(Value), typeof(int), typeof(Rating),
            new PropertyMetadata(0, (o, e) => o.SetValue(
                RatingDisplayPropertyKey, new string('*', (int)e.NewValue))));

    public Rating()
    {
        InitializeComponent();
    }

    public int Value
    {
        get => (int)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    public string RatingDisplay => (string)GetValue(RatingDisplayProperty);
}

Alternatively, bind the Value property with a Binding Converter that creates a string from the Rating Value.


Or, probably most simple, directly access the UI element that should updated:

<TextBlock x:Name="ratingDisplay"/>

with this code behind:

public partial class Rating : UserControl
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register(
            nameof(Value), typeof(int), typeof(Rating),
            new PropertyMetadata(0, (o, e) =>
                ((Rating)o).ratingDisplay.Text = new string('*', (int)e.NewValue)));

    public Rating()
    {
        InitializeComponent();
    }

    public int Value
    {
        get => (int)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }
}
  • Related