Bind trigger to element in parent collection
我有一个控件,它的依赖属性"IsLightOnVal"是这样定义的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // List of available states for this control private ObservableCollection<CtlStateBool> m_IsLightOnVal; [Category("Properties")] public System.Collections.ObjectModel.ObservableCollection<CtlStateBool> IsLightOnVal { get { if (m_IsLightOnVal == null) m_IsLightOnVal = new System.Collections.ObjectModel.ObservableCollection<CtlStateBool>(); return m_IsLightOnVal; } set { if (m_IsLightOnVal != value) { m_IsLightOnVal = value; OnPropertyChanged("IsLightOnVal"); } } } // IsLightOnVal dependency property. public static readonly DependencyProperty IsLightOnValProperty = DependencyProperty.Register("IsLightOnVal", typeof(System.Collections.ObjectModel.ObservableCollection<CtlStateBool>), typeof(ButtonSimple), new UIPropertyMetadata(new System.Collections.ObjectModel.ObservableCollection<CtlStateBool>())); |
在我的集合中,每个元素都包含一个字符串(State)和一个布尔值(Value)
我的控件样式在 ControlTemplate 中定义。
我想添加一个触发器,例如当我的集合中的第一个元素为真时,然后做一些事情。
我试过这个:
1 2 3 4 5 6 7 8 9 10 | <Style x:Key="Btn_RADIO_VHF" TargetType="{x:Type ButtonSimple}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ButtonSimple}"> <Canvas .../> <ControlTemplate.Triggers> <DataTrigger Value="True" Binding="{Binding IsLightOnVal[0].Value, RelativeSource={RelativeSource TemplatedParent}}"> <Setter Property="Fill" TargetName="pShowTouch" Value="{DynamicResource ShowTouch}"/> </DataTrigger> </ControlTemplate.Triggers> |
我也尝试使用简单的触发器而不是 DataTrigger,但它似乎不支持绑定...
有人可以帮我吗?
你有很多问题。首先,您使用的是 TemplatedParent 的 RelativeSource,但这不是应用于模板内元素的绑定,因此您应该使用 Self。这样可以相对容易地修复:
1 | <DataTrigger Value="True" Binding="{Binding Path=IsLightOnVal[0].Value, RelativeSource={RelativeSource Self}}"> |
其次,您已将此属性定义为 CLR 属性(具有自己的后备存储)和 DependencyProperty。如果属性被定义为 DP,那么框架将期望您使用 DP 来存储值。在您的代码中,您永远不会使用 SetValue 方法将集合实例实际存储在 DependencyObject 的后备存储中。所以有很多方法可以解决这个问题:
1) 移除 DP:
1 2 | //public static readonly DependencyProperty IsLightOnValProperty = // DependencyProperty.Register("IsLightOnVal", typeof( System.Collections.ObjectModel.ObservableCollection<CtlStateBool> ), typeof( ButtonSimple ), new UIPropertyMetadata( new System.Collections.ObjectModel.ObservableCollection<CtlStateBool>() ) ); |
由于它不是 DP,尽管您无法在 setter 中对其进行设置,将其绑定到 VM 上的某些属性等,因此这可能不是最佳选择。
2) 将值存储在 DP 以及您的局部变量中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public System.Collections.ObjectModel.ObservableCollection<CtlStateBool> IsLightOnVal { get { if ( m_IsLightOnVal == null ) this.SetValue(IsLightOnValProperty, m_IsLightOnVal = new System.Collections.ObjectModel.ObservableCollection<CtlStateBool>()); return m_IsLightOnVal; } set { if ( m_IsLightOnVal != value ) { this.SetValue( IsLightOnValProperty, m_IsLightOnVal = value ); OnPropertyChanged("IsLightOnVal" ); } } } |
我个人不喜欢这个选项。或者更具体地说,我认为在吸气剂中懒惰地分配自己的财产是不好的做法。这将在对象上设置一个本地值,如果有人实际上将其设置为较低的优先级,则该值可能会覆盖实际值(例如,在模板中定义了 this 的一个实例,并且在那里设置/绑定了属性)。如果您计划设计时支持,这可能会搞砸设计师。如果你确实走这条路,那么你真的应该在你的 DP 定义中添加一个 PropertyChangedHandler 并确保在那里设置你的 m_IsLightOnVal 成员变量,否则如果值是通过 DP 设置的(例如某人 - 包括WPF 框架 - 使用 SetValue 设置属性的值)。
3) 仅使用 GetValue/SetValue
1 2 3 4 5 | public System.Collections.ObjectModel.ObservableCollection<CtlStateBool> IsLightOnVal { get { return (System.Collections.ObjectModel.ObservableCollection<CtlStateBool>)this.GetValue(IsLightOnValProperty); } set { this.SetValue( IsLightOnValProperty, value ); } } |
我会推荐这种方法。是的,这意味着任何希望设置属性的人都必须定义集合的实例,但我认为这比设置自己的 DP 值可能遇到的问题更可取。请注意,如果您采用这条路线,那么您可能想要定义一个派生自 ObservableCollection 的非泛型集合类,以便有人可以在 xaml 中定义集合类的实例,尽管如果您希望它只是绑定到那么 this可能不是问题。从对其他回复的评论中,虽然听起来它可能是在 xaml 中设置的。
现在你的触发器永远不会被触发,因为
您可以尝试实现支持 ChangeNotification 的
但是,将
找到了:
1 2 3 | <DataTrigger Binding="{Binding MyCollection[index].Value, RelativeSource={RelativeSource Self}}" Value="True"> <Setter .../> </DataTrigger> |