Property trigger VS DataTrigger

Original: http://blog.sina.com.cn/s/blog_4cc3c0380100j9ra.html

Consider the following screenshot. There is a ListBox which simply hold several string items. We want the items get red foreground when the mouse hover the item. How can we achieve this simply requirement?


#1 Generally, we may use the DataTrigger in this case. We bind the IsMouseOver property of the current ListBoxItem using the find ancestor syntax. Something like the following:

<DataTrigger Binding="{Binding Path=IsMouseOver,
RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True" >
<
Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</
DataTrigger>

【PCH】:沿着VisualTree找到ListBoxItem,查询他的IsMouseOver属性,这当然是可行的。注意ListBox显示集合的方法是对集合中的每一项,都用一个ListBoxItem包起来,就算是实现了DataTemplate也是一样的,ListBoxItem里面有一个ContentPresenter用来显示内容的。



#2

If necessary, we can utilize a value converter to detect the details in the binding. In additional to this approach, are there other ways doable?

Of course, we can use property trigger to achieve this goal. Now let’s try the explorer.

<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</Trigger> 

【PCH】:这个也是可行的,这个是Property Trigger,其实所有的属性都是查询或者应用于ContentPresenter的,因为ContentPresenter具有IsMouseOver属性,这当然是可行的。



Run the code above, we surprisedly find that it works. So who is the element that the IsMouseOver applied on? In order to research it more, we can use RelativeSource binding syntax like below:



#3.

<DataTrigger Binding="{Binding Converter={StaticResource converter}, RelativeSource={RelativeSource Self}}" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</DataTrigger> 


【PCH
】:注意这里RelativeSource Self,找到的是ContentPresenter所以,这个也是可行的。

TestConverter: (PCH: 仅仅用于测试用的)



public class TestConverter : IValueConverter
 {
 #region IValueConverter Members
 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
 return value; // add break point here 
 }
 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
 throw new NotImplementedException();
}
 #endregion
 } 


We add a break point at the Convert method in the code behind to see the value type. Wow, the value is of type ContentPresenter.No wonder the IsMouseOver property trigger works in this way. Actually, if we have a look at the default template of the ListBoxItem, we could figure that the DataTemplate is the ContentTemplate of the ContentPresenter.



<ListBoxItem.Template>
 <!--other code-->
 <ContentPresenter
 Content="{TemplateBinding ContentControl.Content}"
 ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
 ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
 HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
 VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
 SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ListBoxItem.Template> 



Yep, armed with this knowledge, we can also do it like this, just add the Path in the binding:



#3:



<DataTrigger Binding="{Binding Path=IsMouseOver, Converter={StaticResource converter}, RelativeSource={RelativeSource Self}}" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</DataTrigger> 


Here is the full version of the code:



<Window x:Class="WpfDataTriggerTest.Window1"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:WpfDataTriggerTest"
 xmlns:s="clr-namespace:System;assembly=mscorlib"
 Title="Window1" Height="300" Width="300">
<Window.Resources>
<x:Array Type="{x:Type s:String}" x:Key="data">
<s:String>Item1</s:String>
<s:String>Item2</s:String>
<s:String>Item3</s:String>
<s:String>Item5</s:String>
<s:String>Item6</s:String>
</x:Array>
<local:TestConverter x:Key="converter" />
</Window.Resources>
<StackPanel>
 <!--<local:UserControl1>
</local:UserControl1>-->
 <ListBox DataContext="{Binding Source={StaticResource data}}"
 ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Name="textBlock" />
<DataTemplate.Triggers>
 <!--# use property trigger-->
 <Trigger Property="ContentPresenter.IsMouseOver" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</Trigger>
 <!--#2 the Self value is ContentPresenter-->
 <DataTrigger Binding="{Binding Path=IsMouseOver, Converter={StaticResource converter}, 
 RelativeSource={RelativeSource Self}}" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</DataTrigger>
 <!--#3 the AncestorType is ListBox -->
 <DataTrigger Binding="{Binding Path=IsMouseOver, Converter={StaticResource converter}, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</DataTrigger>
 <!--#4 it's the items object, not the UI element ListBoxItem-->
 <DataTrigger Binding="{Binding}" Value="True" >
<Setter Property="Foreground" Value="Red" TargetName="textBlock" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
 



In the code behind:



using System;
 using System.Windows;
 using System.Windows.Data;
 namespace WpfDataTriggerTest
{
 public partial class Window1 : Window
 {
 public Window1()
{
InitializeComponent();
}
}
 public class TestConverter : IValueConverter
 {
 #region IValueConverter Members
 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
 return value; // add break point here
 }
 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
 throw new NotImplementedException();
}
 #endregion
 }
} 



The key point is this scenario is having a clear idea of what is the Self object, and what is the {Binding} section stands for.



Just as a kind reminder, when we use the RelativeSource={RelativeSource Self} syntax here, the Self is of ContentPresent type. While using the



Binding="{Binding}" syntax, we get the object item here. Namely the item: item1, item2, item3, etc.

原文地址:https://www.cnblogs.com/puncha/p/3877036.html