только для чтения DependencyProperty, производный от другого DependencyProperty - PullRequest
0 голосов
/ 03 августа 2020

У моего объекта есть одно истинное свойство VideoDimension и одно свойство VideoRatio, которое в основном является "уменьшенной" версией первого. Я ищу способ соединить эти два наиболее элегантным способом.

public static readonly DependencyProperty VideoDimensionProperty = 
    DependencyProperty.Register(
        nameof(VideoDimension), 
        typeof(Point), 
        typeof(MyControl), 
        new PropertyMetadata(new Point(0, 0))
    );
public Point VideoDimension
{
    get { return (Point)GetValue(VideoDimensionProperty); }
    set { SetValue(VideoDimensionProperty, value); }
}


public static readonly DependencyProperty VideoRatioProperty = 
    MysteryFunction(VideoDimensionProperty, (value) =>
        {
            Point point = (Point)value;
            return point.X / point.Y;
        });
public double VideoRatio
{
    get { return (double)GetValue(VideoRatioProperty); }
}

Что может быть это MysteryFunction выше? Я хотел бы, чтобы VideoRatio вычислялся ленивым способом.

Рабочие обходные пути, которые я нашел до сих пор:

  • Сделайте VideoRatioProperty своим DependencyProperty и вручную обновите его при изменении VideoDimension. Это не идеально, потому что:
    • VideoRatioProperty можно рассчитать даром, если его никто не слушает.
    • Это создает второй источник истины. Это позволяет изменять VideoRatio, а не VideoDimension. Это можно уменьшить, используя RegisterReadOnly, чтобы защитить его от public вызывающих, но объект-владелец все еще может его изменить.
  • Используйте только VideoRatio в качестве легкого получателя, который будет вычислять значение на лету. Но тогда становится трудно заставить другие элементы связываться с ним, поскольку они должны слушать уведомитель VideoDimensionProperty, но принимать значение из VideoRatio. Это слишком много, чтобы узнать об этом от внешнего вызывающего.
  • Не используйте VideoRatio вообще, но заставьте вызывающих абонентов слушать VideoDimension с привязкой Converter, которая будет вычислять соотношение. Это тоже слишком много, чтобы узнать об этом от внешнего вызывающего абонента, и это определенно не масштабируется.

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Со свойствами это можно сделать так, используя PropertyChangedCallback и свойство VideoRatio только для чтения:

public Point VideoDimension
{
    get { return (Point)GetValue(VideoDimensionProperty); }
    set { SetValue(VideoDimensionProperty, value); }
}

public static readonly DependencyProperty VideoDimensionProperty =
    DependencyProperty.Register(
        nameof(VideoDimension), 
        typeof(Point), 
        typeof(MainWindow), 
        new PropertyMetadata(new Point(0,0), VideoDimensionChangedCallback));

private static void VideoDimensionChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Point p = (Point)e.NewValue;
    d.SetValue(VideoRatioPropertyKey, p.X / p.Y);
}

public double VideoRatio
{
    get { return (double)GetValue(VideoRatioProperty); }
}

public static readonly DependencyProperty VideoRatioProperty = VideoRatioPropertyKey.DependencyProperty;

private static readonly DependencyPropertyKey VideoRatioPropertyKey =
    DependencyProperty.RegisterReadOnly(
        nameof(VideoRatio), 
        typeof(double), 
        typeof(MainWindow), 
        new PropertyMetadata(0));
1 голос
/ 04 августа 2020

«Ничего не подсчитано, если никто его не слушает»

Исходный объект не должен заботиться о том, «слушает» ли его кто-нибудь. Он должен обновлять свое состояние в любом случае, так что это не проблема.

«Но тогда становится трудно заставить другие элементы связываться с ним ...»

Вы можете реализовать INotifyPropertyChanged в своем классе и вызывать событие PropertyChanged для VideoDimension всякий раз, когда вы хотите уведомить подписчиков.

"Но объект-владелец все еще может изменить его "

Не только может - он должен. Это его ответственность. Устанавливает ли он свойство зависимости только для чтения, явно используя ключ, или вызывает событие PropertyChanged для свойства CLR, доступного только для чтения, - это просто дело вкуса.

Вы не должны заставлять внешние вызывающие стороны использовать конвертер, чтобы получить значение. С двумя другими вариантами все в порядке, и их следует рассматривать как решения, а не как обходные пути.

...