我有一个控件,依赖属性“IsLightOnVal”女巫定义如下:
// 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)和一个bool(Value)
我的控件的样式在ControlTemplate中定义。
我想添加一个触发器,例如,当我的集合中的第一个元素为true时,然后执行某些操作。
我试过了:
<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,但它似乎不支持绑定......
有人可以帮助我吗?
答案 0 :(得分:4)
现在你的触发器永远不会被触发,因为ObservableCollection
不支持属性更改所包含元素的通知。
您可以尝试实现支持ChangeNotification的ObservableCollection
专门化,例如Extending ObservableCollection
但是,将ObservableCollection
的第一个值存储在ViewModel / Code中可能更容易,并将其设置为触发器的目标。
答案 1 :(得分:4)
那里有很多问题。首先,您使用 TemplatedParent 的 RelativeSource ,但这不是应用于模板中元素的绑定,因此您应该使用 Self 。这样可以相对容易地修复:
<DataTrigger Value="True" Binding="{Binding Path=IsLightOnVal[0].Value, RelativeSource={RelativeSource Self}}">
其次,您已将此属性定义为CLR属性(具有自己的后备存储)和DependencyProperty。如果该属性被定义为DP,那么框架将期望您使用DP来存储该值。在您的代码中,您永远不会使用SetValue方法将集合实例存储在DependencyObject的后备存储中。所以有很多方法可以解决这个问题:
1)删除DP:
//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以及本地变量中:
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" );
}
}
}
我个人不喜欢这个选项。或者更具体地说,我认为懒惰地在getter中分配你自己的属性是不好的做法。这将在对象上设置一个本地值,如果某人实际上设置了一个较低的优先级(例如,在模板中定义了一个实例并且在那里设置/绑定了属性),则可以覆盖实际值。如果你计划设计时间的支持,这可能会让设计师陷入困境。如果你选择这条路线,那么你应该在你的DP定义中添加一个PropertyChangedHandler,并确保在那里设置你的m_IsLightOnVal成员变量,否则如果通过DP设置了值,你就会失去同步(例如某人 - 包括WPF框架 - 使用SetValue设置属性的值。)
3)仅使用GetValue / SetValue
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中定义集合类的实例,尽管如果你希望这只是绑定到那么这个可能不是问题。从对其他回复的评论虽然听起来可能是在xaml中设置。
答案 2 :(得分:1)
找到它:
<DataTrigger Binding="{Binding MyCollection[index].Value, RelativeSource={RelativeSource Self}}" Value="True">
<Setter .../>
</DataTrigger>