如何让属性更改事件在其子属性更改时触发?

时间:2013-06-22 10:05:03

标签: wpf data-binding

我有一个类AdvanceBasicEffect,它有一个属性SpecularColor,它是类AdvanceVector3类的对象,所以当我绑定specularColor.X属性时,属性更改事件触发但仅在AdvanceVector3类中不在AdvanceBasicEffect中。

查看您将要弄清楚的代码:

public partial class Lights : UserControl
{
    public Lights()
    {
        InitializeComponent();

        this.DataContext = this;
        basicEffect = new AdvanceBasicEffect();
    }

    public AdvanceBasicEffect basicEffect { get; set; }
}

public class AdvanceBasicEffect : INotifyPropertyChanged
{
    public AdvanceBasicEffect()
    {
        SpecularColor = new AdvanceVector3();
        basicEffect = ((bathroom)CurrentWindowHandle.currentGame.Components.First()).basicEffect;
    }

    BasicEffect basicEffect;

    AdvanceVector3 _SpecularColor;
    public AdvanceVector3 SpecularColor 
    { 
        get
        {
            return _SpecularColor;
        }
        set
        {
            //Line 1 : event not occuring 

            _SpecularColor = value;
            if(basicEffect!=null)
             basicEffect.DirectionalLight0.Direction = new Vector3(_SpecularColor.X, _SpecularColor.Y, _SpecularColor.Z);
            valueChanged("SpecularColor");

        }
    }

    private void valueChanged(string p)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(p));
            CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_80", basicEffect, false);
            CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_111", basicEffect, false);
            CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_112", basicEffect, false);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}



public class AdvanceVector3 : INotifyPropertyChanged
{
    float _X;
    public float X
    {
        get
        {
            return _X;
        }
        set
        {
            _X = value;
            valueChanged("X");
        }
    }

    float _Y;
    public float Y
    {
        get
        {
            return _Y;
        }
        set
        {
            _Y = value;
            valueChanged("Y");
        }
    }

    float _Z;
    public float Z
    {
        get
        {
            return _Z;
        }
        set
        {
            _Z = value;
            valueChanged("Z");
        }
    }

    private void valueChanged(string p)
    {
        //line 2 :Event Occuring 
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(p));
            //CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_80", basicEffect, false);
            //CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_111", basicEffect, false);
            //CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_112", basicEffect, false);
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

在评论“第1行”事件发生时,在“第2行”事件发生时,所以我的问题是当子属性发生变化时,为什么父属性没有发生变化。我不能在同一个类中定义X,Y,Z,因为我有许多属性需要AdvanceVector3类

绑定代码如下:

  <Slider ToolTip="SpecularColorX" Minimum="-1" Maximum="1" Value="{Binding basicEffect.SpecularColor.X}" />
        <Slider ToolTip="SpecularColorY" Minimum="-1" Maximum="1" Value="{Binding basicEffect.SpecularColor.Y}" />
        <Slider ToolTip="SpecularColorZ" Minimum="-1" Maximum="1" Value="{Binding basicEffect.SpecularColor.Z}" />

2 个答案:

答案 0 :(得分:4)

如果我正确地回答了您的问题,我认为您误解了INPC的含义以及何时触发。

如果你说有

Class ParentClass : INotifyPropertyChanged {

  public ChildClass SomeObject {
    get { ... }
    set { ... valueChanged("SomeObject"); }
  }

  ...

}

Class ChildClass : INotifyPropertyChanged {

  public string SomeString {
    get { ... }
    set { ... valueChanged("SomeString"); }
  }

  ...

}

现在,如果您在SomeString

的对象中使用xaml或代码隐藏更改ParentClass
var parentObject = new ParentClass {SomeObject = new ChildClass {SomeString = "Hi"}};

// will trigger property changed in ChildClass for SomeString property
// however it will not trigger a PropertyChanged in ParentClass for SomeObject
parentObject.SomeObject.SomeString = "New String"

// will trigger property changed in ParentClass for SomeObject property
parentObject.SomeObject = new ChildClass();

您的xaml绑定工作正常,因为您直接绑定到属性,并且当它们更改时,View会相应地更新。

如果您想要“观察”SomeStringParentClass的任何更改,那么您需要订阅PropertyChanged ChildClass.SomeString事件

所以说ParentClass可以更新为

  public ChildClass SomeObject {
    get { ... }
    set { 
       if (value == _someObject)
         return;

       _someObject.PropertyChanged -= ChildObjectValueChanged;
       _someObject = value;
       _someObject.PropertyChanged += ChildObjectValueChanged;

       valueChanged("SomeObject"); }
  }

  private void ChildObjectValueChanged(object sender, PropertyChangedEventArgs args) {
    if (args.PropertyName == "SomeString")
      // Child Object property "SomeString" has changed. Do what you need here
  }

答案 1 :(得分:0)

你的二传手永远不会被召唤。 Wpf本身在PropertyChagned imlplementations上调用INotifyPropertyChanged。你应该做的是在AdvancedBasicEffect类构造函数中自己改变事件,而不是在valueChanged方法中设置网格效果:

public AdvanceBasicEffect()
{
    SpecularColor = new AdvanceVector3();
    basicEffect = ((bathroom)CurrentWindowHandle.currentGame.Components.First()).basicEffect;

    SpecularColor.PropertyChanged += (o,e) => 
    {
        CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_80", basicEffect, false);
        CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_111", basicEffect, false);
        CurrentWindowHandle.currentGame.models[0].SetMeshEffect("Archinteriors7_10_112", basicEffect, false);
    }
}