zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    搭建企业级Docker Registry -- Harbor
    搭建私服-docker registry
    CentOS 7.2修改网卡名称
    tomcat错误日志监控脚本
    Openresty+Lua+Redis灰度发布
    Jenkins权限控制-Role Strategy Plugin插件使用
    Rsyslog日志服务搭建
    awk4.0对数组value排序
    Spring-IOC 在非 web 环境下优雅关闭容器
    Spring-IOC bean 生命周期之 Lifecycle 钩子
  • 原文地址:https://www.cnblogs.com/puncha/p/3877036.html
Copyright © 2011-2022 走看看