WPF PropertyChange не работает каскадно - анимация одного настраиваемого свойства зависимостей класса изменяет другие свойства - PropertyChange запускается только для первого - PullRequest
0 голосов
/ 21 сентября 2009

Очень упрощенно, это моя проблема: (решение внизу)

У меня есть собственный класс, наследующий Animatable и INotifyPropertyChange, содержащий один DependencyProperty (который я анимирую, скажем, от 1 до 10) и одно дополнительное свойство (которое я хочу использовать из XAML с привязкой):

MyClass : Animatable, INotifyPropertyChanged {

    public static DependencyProperty AnimateValueProperty = DependencyProperty.Register(
      "AnimateValue",
      typeof(double),
      typeof(MyClass)
     );

    public double AnimateValue {
        get { return (double)GetValue(AnimateValueProperty); }
        set {
                SetValue(AnimateValueProperty, value);
                OnPropertyChanged(new PropertyChangedEventArgs("AnimateValue"));
                OnPropertyChanged(new PropertyChangedEventArgs("GuiValue")); //Added in edit
            }
        }
    }

    public double GuiValue {
        get { 
            return AnimateValue + 10; 
            //More fancy stuff happening in the real class...
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
        if (PropertyChanged != null) {
            PropertyChanged(this, e);
        }
    }

    protected override Freezable CreateInstanceCore() {
        return new MyClass();
    }
}

Когда я связываю элемент GUI со свойством, которое я фактически анимирую (AnimateValue), все работает как положено - когда я связываю элемент GUI со связанным свойством (GuiValue), ничего не происходит. Я предполагаю, что измененное событие не достигает этого свойства, но я действительно не знаю, как решить проблему. Есть указатели?

Любая помощь очень ценится!

Edit: Спасибо за два ответа от Adam Sills и Kai Wang, глупо с моей стороны пропустить второй вызов OnPropertyChange, однако я пробовал это раньше, и это не сработало. Для отладки я поместил писатель в функцию AnimateValue.set и вот - она ​​вообще не выполняется!

Я также пытался изменить геттеры, чтобы они всегда возвращали 20 - без изменений! : -o Похоже, что анимация полностью пропускает свойство get, set и анимирует и считывает базовое свойство зависимости ... Или я неправильно понял? Код анимации, который я использую:

DoubleAnimation da = new DoubleAnimation (1, 10, new Duration(TimeSpan.FromSeconds(3)));
myClassInstance.BeginAnimation(MyClass.AnimateValueProperty, da);

Хмм ... После написания этого кода здесь кажется очень разумным, что установщик не используется (хотя мне бы очень хотелось, чтобы это было!) - но все же основная проблема остается, и я до сих пор не понимаю, почему получатель не используется!

Редактировать2: Решение!

Как предлагается ниже, я не мог использовать INotifyPropertyChange для свойств, соединяющихся с DependencyProperty - анимация и XAML внутренне используют DepencencyProperty и вообще не используют методы получения и установки.

Следовательно - PropertyChangedCallback для уведомления других об изменении, CoerceValueCallback для «исправления» данных. Также возможно проверять данные, все это делается в конструкторе DependencyProperty. Спасибо всем!

Спасибо!

/ Victor

Ответы [ 4 ]

2 голосов
/ 21 сентября 2009

Устанавливая значение AnimateValue, вы сообщаете WPF, что оно изменилось. Но вы никогда не говорите WPF, что GuiValue изменилось. Как инфраструктура привязки данных WPF должна знать, что GuiValue изменилось, когда вы этого не говорите?

Когда вы обновляете AnimateValue и генерируете уведомление об изменении (что само по себе не является обязательным, поскольку это свойство зависимости), вам также необходимо генерировать уведомление о GuiValue, поскольку оно зависит от AnimateValue, но вычисляется вручную внутри свойство.

В основном просто добавьте

OnPropertyChanged(new PropertyChangedEventArgs("GuiValue"));

в блок набора AnimateValue.

UPDATE:

Исходя из ваших новых правок, если при настройке свойства через XAML никогда не вызывается метод получения / установки свойства зависимости, все это делается напрямую через GetValue / SetValue.

Если вам необходимо локально реагировать на изменения, переопределите OnPropertyChanged или предоставьте обратный вызов изменения внутри DependencyProperty.Register

2 голосов
/ 21 сентября 2009

вот свойство, изменившее шаблон уведомления для использования со свойствами зависимости

  public Boolean PasswordBoxVisible {
     get { return (Boolean)GetValue(PasswordBoxVisibleProperty); }
     set { SetValue(PasswordBoxVisibleProperty, value); }
  }

  public static readonly DependencyProperty PasswordBoxVisibleProperty =
      DependencyProperty.Register("PasswordBoxVisible", typeof(Boolean), typeof(UserControl), new UIPropertyMetadata(false, new PropertyChangedCallback(PropChanged)));

  //this method is called when the dp is changed
  static void PropChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
       UserControl userControl = o as UserControl;
       //now you can use this to call some methods, raise other value changed etc, in this case OtherProperty.
       userControl.NotifyPropertyChanged("OtherProperty");
  }

У меня есть чувство, но я не совсем уверен, что вам может понадобиться принуждать другие ценности. взгляните на раздел обратного вызова принудительного значения в этой статье

1 голос
/ 21 сентября 2009

Если ваш объект является производным от объекта Dependency Object, вы не можете использовать INotifyPropertyChanged, потому что архитектура Binding будет использовать только Resinistration свойства Dependency для использования привязки.

Для этого вам нужно Свойство зависимостей только для чтения , которое я объяснил ниже.

public static DependencyProperty AnimateValueProperty = 
   DependencyProperty.Register(
      "AnimateValue",
      typeof(double),
      typeof(MyClass)
   );

 // This is for the Readonly GuiValueKey property
 public static DependencyPropertyKey GuiValueKey = 
     DependencyProperty.RegisterReadOnly (
     "GuiValue",
      typeof(double),
      typeof(MyClass),
      new UIPropertyMetadata(0.0)
);

public double AnimateValue {
    get { return (double)GetValue(AnimateValueProperty); }
    set {
            SetValue(AnimateValueProperty, value);
            // Following statement update GuiValue wherever it
            // is binded
            this.SetValue(GuiValueKey,value + 10);
        }
    }
}

// you dont even need following line...
// but its good to have it..
public double GuiValue {
    get { 
        return (double)this.GetValue(GuiValueKey); 
    }
}
1 голос
/ 21 сентября 2009

Я не уверен, понимаю ли я, что вы имеете в виду. Но посмотрите, поможет ли это.

    public double AnimateValue {
    get { return (double)GetValue(AnimateValueProperty); }
    set {
            SetValue(AnimateValueProperty, value);
            OnPropertyChanged(new PropertyChangedEventArgs("AnimateValue"));
            OnPropertyChanged(new PropertyChangedEventArgs("GuiValue "));
        }
    }
}
...