#c# #wpf #data-binding #dependency-properties
Вопрос:
У меня есть пользовательский элемент управления, который отображает рейтинг в виде количества звезд. Это достигается путем привязки текстового свойства TextBlock к обычному свойству кода, которое, в свою очередь, использует целочисленное значение DependencyProperty.
Чтобы обновить текстовый блок при изменении значения, мне нужно вручную запустить INotifyPropertyChanged.Событие PropertyChanged из обратного вызова PropertyChanged объекта DependencyProperty.
Это кажется чрезвычайно чрезмерным. Есть ли более простой способ сделать это?
<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));
}
Комментарии:
1. Почему вы не используете ViewModel , подключенный к вашему пользовательскому управлению? Таким образом, вам не понадобится свойство зависимости, которое облегчило бы чтение вашего кода.
2. Это плохой совет. Пользовательский элемент управления — как и любой другой элемент управления — не должен иметь собственной частной модели представления, которая была бы отключена от структуры модели представления приложения.
Ответ №1:
Вам не нужно реализовывать INotifyPropertyChanged в классе, который предоставляет свойства зависимостей. Свойства зависимостей предоставляют свой собственный механизм уведомления об изменениях.
Чтобы обновить свойство зависимости только для чтения, должно сработать что-то вроде этого:
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);
}
В качестве альтернативы, свяжите свойство Value с помощью преобразователя привязки, который создает строку из значения рейтинга.
Или, возможно, самое простое, прямой доступ к элементу пользовательского интерфейса, который должен быть обновлен:
<TextBlock x:Name="ratingDisplay"/>
с этим кодом за:
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);
}
}