Chapter6:Data Binding(学习)

Element to element binding:

Data binding一种情况是前台元素与后台data object,第二种是前台元素之间的binding(ElementSource:第一种拿Source的方法)

1: Xaml的binding表达用markup extension,target property一定要是DP。

<TextBox Text="{Binding Path=Value, ElementName=_slider}" Grid.Row="2"/>

 

 

2:我们一般set最基本的两个binding属性,Source property用Path属性(写DT的时候经常省略,并且不要写ElementName或者Source), Source Object 用ElementName属性.

3:只写{Binding}表示binding到DataContext

4:用后台代码定义binding:一般C#不实现binding功能(xaml中写), 但是在custom control时常见

View Code
var binding = new Binding("Value");
binding.ELementName = "_slider";
_text.SetBinding(TextBlock.FontSizeProperty, binding);

5:其他常用的binding属性:

  • BindingMode: BindingMode.Default, .OneWay, .TwoWay, .OneTime, .OneWayToSource

    TextBlock.Text property 属性binidng mode是one way binding(同TextBlock的大多数properties一样)

     TextBox.Text property  属性binding mode是two way binding

  • UpdateSourceTrigger(只作用在TwoWay和OneWayToSource): UpdateSourceTrigger.LostFocus, .PropertyChanged, .Explicit, .Default  
  • Updateing the source or target manually(一般binding都是自动完成的,但也可以手动控制)  

 

Binding to a single object:

1:Binding需要Source Object,上面元素与元素的binding我们用ElementName属性表达,但是binding to data我们需要用DataContext(如果binding里没有表达Element,则source为从当前对象向上找最近的非空DataContext) (DataContext:第二种拿Source的方法)

2:如何让Binding的property获得自动通知能力:

Binding的属性需要有change notification,有两个方法,其一是使Property变成DP(自动change notification的能力),其二是常用,data类继承INotifyPropertyChange。

 

//先建立一个Common base for InotifyPropertyChanged 

public abstract class ObsevableObject :
    InotifyPropertyChanged {
    public event PropertyChangedHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propName) {
        var pc = PropertyChanged;
        if(pc != null)
            pc(this, new PropertyChangedEventArgs(propName));
    }
}
View Code
//继承InotifyPropertyChanged 的data类

class Person : ObservableObject {
    public string Name {get; set;}

    int _age;
    public int Age {
        get{ return _age; }
        set{
            if(_age != value) {
            _age = value;
            OnPropertyChanged("Age");
            }
        }
    }
{
View Code

第一种有2个问题(p176):需要data object 继承DP,这样就组织了其继承别的类的可能。再有这样继承DP的data用在其他的APP里ASP或者WinForm里会笨重(根本不需要是继承DP)。

第二种方法:(P178)

class Person : INotifyPropertyChanged{
        //普通Property
        public string Name { get; set; }
        
        private int _age;  
        //需要PropertyChanged的property    
        public int Age { 
            get { return _age; }
            set  {
                       if(_age != null)
                       {
                          _age = value;
                          OnPropertyChanged("Age");
                   }

        }
    }
View Code

 

3:Source 属性:(Source:第三种拿Source的方法)

Source={StaticResource resourceName}
<!--直接source binding到一个object的示例没什么用,多是向上面这样binding to data from resources-->

4: RelativeSource属性:(RelativeSource:第四种拿Source的方法)

使用RelativeSource属性(用来指定与目标对象存在一定关系的其他对象),有四种mode

Self, FindAncestor, PreeviousData, TemplatedParent(p177)

 self:binding one property to another property on the same object

<Button Content={Binding ActualWidth, RelativeSource={RelativeSource Self}}"/>

FindAncestor:不一定是他直接的parent,可以是parent的parent, 可以通过AncestorType来指定到底是那层的parent。

<TextBlock Text="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>

PrviousData:当binding到一个collection的时候指定source是prvious data object

TemplatedParent:一般使用在CT里,指向使用该Tempalte的Control(比较TemplateBinding

Binding to a collection:

前面是关于binding单个对象,这里关于ItemsControl(ListBox,DataGrid这种ItemsControl)binding 一组collection对象,以及如何使用DataTemplate(可以用在ContentTamplet或者ItemTemplate)Value Converter

1:继承自ItemsControl的control都会拥有一个ItemsSource(相对于ContentControl的Source属性)属性来指定一个collection 对象(至少要是IEnumerable)。
2:默认情况下,items are display as TextBlock,使用的是ToString来显示每个item。可以用DisplayMemberPath来选择我们要显示items的某个数据(每一个Person下的Name),还有可以overwrite ToString()重写来输出格式。

3:构建data Object时不要用List<T>,因为当List<T>增加或者删除是不会有notification sent出的,所以view层的数据是得不到更新的。另外List<T>也不支持NotifyCollectionChanged接口建议用ObservableCollection<T>接口

4:另外ItemsControl的数据源也可以用DataContext:我们可以set某个object(比如很高一层的Object)的DataContex,这样这个ItemsControl的binding可以写成{Binding},代表binding到父亲object的DataContex。有了DataContex的好处是,allow us to bind the same collection to other objects. 比如ListBoxComboBox share同一个Grid对象的DataContex(其中一个control的SelectedItem不会影响另外一个control的SelectedItem

View Code
//前台view:
<ComboBox  ItemsSource="{Binding}" Grid.Row = "2"/>

//后台代码:
DataContext = _people;

5:上面第四点,如果想binding到同一个DataContext的collection sync SelectedItem,可以用IsSynchronizedWithCurrentItem属性

View Code
IsSynchronizedWithCurrentItem = "True"

 6:WPF并不关心哪里如何拿到的Data,只要binding到的是一个collection类型并且至少是IEnumerable<T>就可以完成ItemsControl的数据binding,

Using data template:

DT一般是做给ContentTemplate或者ItemTemplate用来表达数据的现实方式,DT里面没有特定写出ElementName或者Source,其Source就是目前使用DT的control所binding的数据对象,Path=也可以省去,直接写Text = "{Binding Name}".

CT里面一般有写ElementName,来连接Template里的其他元素。

1:ContentControl和ItemControl都可以赋值一个DP对象

  • 属性Content:(类型为Object),如果这个Object类型的对象不是继承自任何UIElement某认会被渲染成TextBlock。
  • 属性ContentTemplate:如果这个属性不为null,则使用DataTemplate来对Data做样式的设置。Binding expression inside template不要指定特定Source或者ElementName,因为其default Source or DataContext 是data object(程序部分的实例object:如下)
    View Code
    1 _list.ItemsSource = new ObservableCollection<Person>
    2             {
    3                 new Person{Name = "Bart", Age = 10},
    4                 new Person{Name ="Shawn", Age=32},
    5                 new Person{Name = "Rebecca", Age=25}
    6             };
<TextBlock Foreground="Red" Text= "{Binding Name}">

    如果上述Name property不存在,则binding失败。

    对于ItemsControl有同样功能的属性ItemTemplate

    对于HeaderedContentControl有同样功能的属性HeaderTemplate

 

2:使用x:key定义的<DataTemplate>,带x:key的完整代码,注意C#代码的_list和xaml部分的ItemTemplate(x:Key 选用不同DT的方法1)

 

View Code
 1 <Window
 2   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 5   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 6   xmlns:local="clr-namespace:WpfApplication2"
 7   mc:Ignorable="d" 
 8   x:Class="WpfApplication2.MainWindow"
 9   Title="MainWindow" Width="350" Height="400">
10   <Window.Resources>
11     <DataTemplate x:Key="personTemplate">
12       <Border BorderBrush="#FF09FF36" BorderThickness="2">
13         <StackPanel Margin="4">
14           <TextBlock TextWrapping="Wrap" Foreground="Red" FontSize="20" Text="{Binding Name}"><Run Text="TextBlock"/></TextBlock>
15           <TextBlock TextWrapping="Wrap" Text="{Binding Age}" FontSize="16" TextAlignment="Right"/>
16         </StackPanel>
17       </Border>
18     </DataTemplate>
19   </Window.Resources>
20   <Grid>
21     <Grid.RowDefinitions>
22       <RowDefinition Height="Auto"/>
23       <RowDefinition/>
24     </Grid.RowDefinitions>
25     <Button Content="{Binding}" ContentTemplate="{StaticResource personTemplate}"/>
26     <ListBox x:Name="_list" Grid.Row="1" HorizontalContentAlignment="Stretch" ItemTemplate="{StaticResource personTemplate}"/>
27   </Grid>
28 </Window>

 

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Windows.Data;
 9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15 using System.Collections.ObjectModel;
16 
17 namespace WpfApplication2
18 {
19     /// <summary>
20     /// Interaction logic for MainWindow.xaml
21     /// </summary>
22     public partial class MainWindow : Window
23     {
24         public MainWindow()
25         {
26             InitializeComponent();
27             
28             DataContext = new Person { Age = 10, Name="Bart"};
29             _list.ItemsSource = new ObservableCollection<Person>
30             {
31                 new Person{Name = "Bart", Age = 10},
32                 new Person{Name ="Shawn", Age=32},
33                 new Person{Name = "Rebecca", Age=25}
34             };
35 
36         }
37 
38     }
39 }

3:不使用x:key使用DataType定义的<DataTemplate>

对于Generic DataTemplate,DataType所指类型的任何控件都会默认使用这个模板。

注意:放在任何级别的Resource下,去掉x:key。

<Window.Resource> //这个资源可以放到App.xaml以获得更广的支持
    <DataTemplate DataType="{x:Type local:Person}">  //local指向某个Person对象在程序中的位置
      ...
    </DataTemplate>
</Window.Resource>

这里代码没有设置ItemTemplate因为上面Window.Resource指定的是Generic DataTemplate,任何是这个类型的控件会自动使用,不像指定了Key的resourece,需要在xaml指定StaticResource是谁。

代码部分和前面使用x:key的做比较,要把数据都写在DataContex里,而不是找xaml里的list对象的名字

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Windows.Data;
 9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15 using System.Collections.ObjectModel;
16 
17 namespace WpfApplication2
18 {
19     /// <summary>
20     /// Interaction logic for MainWindow.xaml
21     /// </summary>
22     public partial class MainWindow : Window
23     {
24         public MainWindow()
25         {
26             InitializeComponent();
27             
28             DataContext = new ObservableCollection<Person>
29             {
30                 new Person{Name = "Bart", Age = 10},
31                 new Person{Name ="Shawn", Age=32},
32                 new Person{Name = "Rebecca", Age=25}
33             };
34 
35         }
36 
37     }
38 }
View Code
 1 <Window
 2   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
 5   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
 6   xmlns:local="clr-namespace:WpfApplication2"
 7   mc:Ignorable="d" 
 8   x:Class="WpfApplication2.MainWindow"
 9   Title="MainWindow" Width="350" Height="400">
10   <Window.Resources>
11     <DataTemplate DataType="{x:Type local:Person}">
12       <Border BorderBrush="#FF09FF36" BorderThickness="2">
13         <StackPanel Margin="4">
14           <TextBlock TextWrapping="Wrap" Foreground="Red" FontSize="20" Text="{Binding Name}"/>
15           <TextBlock TextWrapping="Wrap" Text="{Binding Age}" FontSize="16" TextAlignment="Right"/>
16         </StackPanel>
17       </Border>
18     </DataTemplate>
19   </Window.Resources>
20   <Grid>
21     <Grid.RowDefinitions>
22       <RowDefinition Height="Auto"/>
23       <RowDefinition/>
24     </Grid.RowDefinitions>
25     <Button Content="{Binding}"/>
26     <ListBox Grid.Row="1" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}"/>
27   </Grid>
28 </Window>

4:Generic DataTemplate的Auto Polymorphism

上面使用x:key使用DataType定义的<DataTemplate>的真正价值是使用Polymorphism

 

View Code
1 (Person.cs)
2 Definition Person class...
3 
4 
5 (Employee.cs)
6 
7 class Employee : Person{
8   public String Department {get; set;}
9 }

 

定义一个{x:Type local:Person}的DataTemplate再定义一个{x:Type local:Employee}的DataTemplate

View Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Collections.ObjectModel;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Windows;
 7 using System.Windows.Controls;
 8 using System.Windows.Data;
 9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15 
16 namespace CH06.DataTypedDataTemplates {
17     /// <summary>
18     /// Interaction logic for MainWindow.xaml
19     /// </summary>
20     public partial class MainWindow : Window {
21         public MainWindow() {
22             InitializeComponent();
23 
24             DataContext = new ObservableCollection<Person> {
25                 new Person { Name = "Bart", Age = 10 },
26                 new Employee { Name = "Homer", Age = 45, Department = "Nuclear" },
27                 new Person { Name = "Marge", Age = 35 },
28                 new Employee { Name = "Lisa", Age = 12, Department = "Accounting" },
29                 new Person { Name = "Maggie", Age = 1 }
30             };
31         }
32     }
33 }
View Code
 1 <Window x:Class="CH06.DataTypedDataTemplates.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:local="clr-namespace:CH06.DataTypedDataTemplates" 
 5         Title="MainWindow" Height="350" Width="525">
 6     <Window.Resources>
 7         <DataTemplate DataType="{x:Type local:Person}">
 8             <Border BorderBrush="Green" BorderThickness="2">
 9                 <StackPanel Margin="4">
10                     <TextBlock Foreground="Red" FontSize="20" Text="{Binding Name}"
11                                 TextAlignment="Center" />
12                     <TextBlock Foreground="Black" FontSize="16" Text="{Binding Age}"
13                                 TextAlignment="Right" />
14                 </StackPanel>
15             </Border>
16         </DataTemplate>
17         <DataTemplate DataType="{x:Type local:Employee}">
18             <Border BorderBrush="Black" BorderThickness="1">
19                 <StackPanel Margin="4" Orientation="Horizontal">
20                     <TextBlock Foreground="Blue" FontSize="20" Text="{Binding Name}"
21                                 TextAlignment="Center" />
22                     <TextBlock Foreground="Red" FontSize="16" Text="{Binding Department}"
23                                 Margin="20,0,0,0" VerticalAlignment="Center"/>
24                 </StackPanel>
25             </Border>
26         </DataTemplate>
27     </Window.Resources>
28     <Grid>
29         <ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding}" />
30         
31     </Grid>
32 </Window>

代码和xaml的共同作用下,DT会按照相应的类型被选择。

 

 

5:DataTemplate Selector(DataTemplate Selector 选用不同DT的方法2)

这是一个data object bound to an ItemsControl,返回一个DataTemplate。这个方法也可以为有相同type的不同object来选择不同的template,需要继承DataTemplateSelector这个abstract类overrideSelectTemplate方法。

  1. 定义Selector: 需要code(继承和重写部分)+xaml(定义不同DataTemplate)见(189)
  2. 链接模板:注意这里xaml可以给变量的property赋值
    <local: ProcessTemplateSelector x:key="_selector"
                SeyetemProcessTemplate="systemTemplate"
                UserProcessTemplate="userTemplate"/>

     

  3. 使用Selector:这里以链接DataContex为例
<ListBox ItemsSource="{Binding}"
                ItemTemplateSelector="{StaticResource _selector}"/>

  4. DataContex = Process.GetProcess();

注意SelectTemplate每一个object只call一次,代表人为改变object,不会重新选择新的DataTemplate。因此有两种方法可以:1:写value converter, 2:set

ItemTemplateSelctor为null 然后再instance回。(191)

Using value converter:

(value converter 选用不同DT的方法3)

Download例子:可以根据dropdown选择的数目来random generate一组预报天气,并且根据天气的类型来表达背景颜色

 

 

//定义数据的基类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeatherForecast
{
    enum GeneralForcast
    {
        Sunny,
        Rainy,
        Snowy,
        Cloudy,
        Dry
    }
    class FroeCast
    {
        public GeneralForcast GeneralForcast { get; set; }
        public double TemperatureHigh { get; set; }
        public double TemperatureLow { get; set; }
        public double Precipitation { get; set; }
    }
}
View Code
//MainView的部分

<Window x:Class="WeatherForecast.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WeatherForecast"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Window.Resources>
        <local:GeneralForecastBrushConverter x:Key="ForCastToBrushes_Converter" />
        <DataTemplate x:Key="MyForecast_Template">
            <Border Margin="4"
                    Background="{Binding GeneralForcast,
                                         Converter={StaticResource ForCastToBrushes_Converter}}"
                    BorderBrush="Black"
                    BorderThickness="2"
                    Padding="4">
                <StackPanel Orientation="Horizontal">
                    <TextBlock FontSize="20"
                               FontWeight="Bold"
                               Text="{Binding GeneralForcast}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding TemperatureLow,
                                              StringFormat=Low: \{0:N2\}}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding TemperatureHigh,
                                              StringFormat=High: \{0:N2\}}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding Precipitation,
                                              StringFormat=Precip: \{0:N2\}}" />
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="4"
                       VerticalAlignment="Center"
                       FontSize="15"
                       Text="Select number of days to forecast:" />
            <ComboBox x:Name="_days"
                      Width="50"
                      SelectedIndex="0" />
            <Button Margin="4"
                    Click="Button_Click"
                    Content="Get Forecast"
                    FontSize="16" />
        </StackPanel>
        <StackPanel Grid.Row="1"
                    Margin="4"
                    Orientation="Horizontal"
                    TextBlock.FontSize="15">
            <TextBlock Margin="4" Text="Select units:" />
            <RadioButton Margin="10,4"
                         Content="Celsius"
                         IsChecked="True" />
            <RadioButton Margin="20,4" Content="Fahrenheit" />

        </StackPanel>
        <ListBox Grid.Row="2"
                 HorizontalContentAlignment="Stretch"
                 ItemTemplate="{StaticResource MyForecast_Template}"
                 ItemsSource="{Binding}" />
    </Grid>
</Window>
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;

namespace WeatherForecast
{
    class GeneralForecastBrushConverter : IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var gf = (GeneralForcast)value;
            switch (gf)
            {
                case GeneralForcast.Cloudy:
                    return Brushes.LightBlue;
                case GeneralForcast.Dry:
                    return Brushes.SaddleBrown;
                case GeneralForcast.Rainy:
                    return Brushes.GhostWhite;
                case GeneralForcast.Snowy:
                    return Brushes.White;
                case GeneralForcast.Sunny:
                    return Brushes.OrangeRed;
            }
            return Binding.DoNothing;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
View Code
//后台代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WeatherForecast;

namespace WeatherForecast
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            
            InitializeComponent();
            _days.ItemsSource = Enumerable.Range(1, 10);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var data = new List<FroeCast>();
            int days = (int)_days.SelectedItem;
            var rnd = new Random();
            for (int i = 0; i < days; i++)
            {
                double temp = rnd.NextDouble() * 40 - 10;
                var forecast = new FroeCast
                {
                    GeneralForcast = (GeneralForcast)rnd.Next(Enum.GetValues(typeof(GeneralForcast)).Length),
                    TemperatureHigh = temp,
                    TemperatureLow = temp + rnd.NextDouble() * 15,
                    Precipitation = rnd.Next(10) > 5 ? rnd.NextDouble() * 10 : 0

                };
                data.Add(forecast);
            }
            this.DataContext = data;
        }
    }
}
View Code

Converter的instance在xaml部分,用一个key表示,并用在binding的表达式里。每一次source property change都会驱动convert methord返回一个新值。

ConvertBack methord是用在two way binding里。One-way binding不用写ConvertBack methord的具体实现。

1:targetType的作用:

  • indicate the expected return type for the binding to work. 这里是连接的Background type是Brushes,所以targetType是typeof(Brush)
if(targetType != typeOf (Brush))
{ throw new ArgumentException("targetType");
  • tagrtType的不同返回不同的类型,Brush或者Color,取决于他binding的属性所需要的类型,这就提供了可以返回不同类型的方法。
  • cultureInfo对应binding expression里的ConverterCulture属性,这个属性设定了如date/time/currency的格式,可以决定使用en, en-US, fr-CA的格式,默认是按照系统现有的格式。
  • 另外converter还可以用来做xaml的binding的Debugging

2: Formatting strings:

情况1(prefix的string):Temp:120。第一种方法可以单独在result前面写一个TextBlock来表达Temp:,第二种方法使用TextBlock的<Run>InLine elements。

情况2(display time,date which have several format):使用converter。或者simply way用StringFormat属性。

Text="{Binding TemperatureLow, StringFormat=Low: \{0:N2\}}"

3: 使用data trigger

之前的converter部分可以用DataTrigger代替如下

<Window.Resources>
        <local:GeneralForecastBrushConverter x:Key="ForCastToBrushes_Converter" />
        <DataTemplate x:Key="MyForecast_Template">
            <Border x:Name="_border"
                    Margin="4"
                    Background="{Binding GeneralForcast,
                                         Converter={StaticResource ForCastToBrushes_Converter}}"
                    BorderBrush="Black"
                    BorderThickness="2"
                    Padding="4">
                <StackPanel Orientation="Horizontal">
                    <TextBlock FontSize="20"
                               FontWeight="Bold"
                               Text="{Binding GeneralForcast}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding TemperatureLow,
                                              StringFormat=Low: \{0:N2\}}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding TemperatureHigh,
                                              StringFormat=High: \{0:N2\}}" />
                    <TextBlock Margin="10,0,0,0"
                               VerticalAlignment="Bottom"
                               FontSize="16"
                               Text="{Binding Precipitation,
                                              StringFormat=Precip: \{0:N2\}}" />
                </StackPanel>
            </Border>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding GeneralForcast}" Value="Sunny">
                    <Setter TargetName="_border" Property="Background" Value="Yellow" />
                </DataTrigger>
                <DataTrigger Binding="{Binding GeneralForcast}" Value="Snowy">
                    <Setter TargetName="_border" Property="Background" Value="LightBlue" />
                </DataTrigger>
                <DataTrigger Binding="{Binding GeneralForcast}" Value="Rainy">
                    <Setter TargetName="_border" Property="Background" Value="LightYellow" />
                </DataTrigger>
                <DataTrigger Binding="{Binding GeneralForcast}" Value="Dry">
                    <Setter TargetName="_border" Property="Background" Value="LightGreen" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </Window.Resources>
View Code

DataTrigger要用TargetName和Property组成setter的目标源和属性,用binding找后台trigger

 

Creating a master-detail view:

Download例子:右边是左边的detail page

 

//code

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MasterDetailView
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = Process.GetProcesses();
        }
    }
}
View Code
<Window x:Class="MasterDetailView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="_master"
                 DisplayMemberPath="ProcessName"
                 FontSize="16"
                 ItemsSource="{Binding}" />
        <Grid Grid.Column="1"
              DataContext="{Binding SelectedItem,
                                    ElementName=_master}"
              TextBlock.FontSize="16">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0"
                       Margin="6"
                       Text="{Binding ProcessName,
                                      StringFormat='Name: \{0\}'}" />
            <TextBlock Grid.Row="1"
                       Margin="6"
                       Text="{Binding Id,
                                      StringFormat='ID: \{0\}'}" />
            <TextBlock Grid.Row="2"
                       Margin="6"
                       Text="{Binding PriorityClass,
                                      StringFormat='Priority Class: \{0\}'}" />
            <TextBlock Grid.Row="3"
                       Margin="6"
                       Text="{Binding Threads.Count,
                                      StringFormat='Threads: 0'}" />
            <TextBlock Grid.Row="4"
                       Margin="6"
                       Text="{Binding TotalProcessorTime,
                                      StringFormat='Processor Time: \{0:G\}'}" />
        </Grid>
    </Grid>
</Window>
View Code

1:做这样的detail page需要有两层DataContex,内层Grid的DataContext根据外层每次选择的项而定。所以第一种方法是inner DataContext用binding到外层

<Grid Grid.Column="1"
              DataContext="{Binding SelectedItem,
                                    ElementName=_master}"
              TextBlock.FontSize="16">
...

方法二是:设置外层DataContext所在Tag的IsSynchronizedWithCurrentItem属性为ture,内层DataContext直接binding到CurrentItem属性上。

 <ListBox x:Name="_master"
                 DisplayMemberPath="ProcessName"
                 FontSize="16"
                 IsSynchronizedWithCurrentItem="True"
                 ItemsSource="{Binding}" />
        <Grid Grid.Column="1"
              DataContext="{Binding CurrentItem}"
              TextBlock.FontSize="16">
...

方法三:方法二的简写,每个TextBlock的Text binding的属性前加一个"/"

<Window x:Class="MasterDetailView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox x:Name="_master"
                 DisplayMemberPath="ProcessName"
                 FontSize="16"
                 IsSynchronizedWithCurrentItem="True"
                 ItemsSource="{Binding}" />
        <Grid Grid.Column="1" TextBlock.FontSize="16">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
                <RowDefinition Height="auto" />
            </Grid.RowDefinitions>
            <TextBlock Grid.Row="0"
                       Margin="6"
                       Text="{Binding /ProcessName,
                                      StringFormat='Name: \{0\}'}" />
            <TextBlock Grid.Row="1"
                       Margin="6"
                       Text="{Binding /Id,
                                      StringFormat='ID: \{0\}'}" />
            <TextBlock Grid.Row="2"
                       Margin="6"
                       Text="{Binding /PriorityClass,
                                      StringFormat='Priority Class: \{0\}'}" />
            <TextBlock Grid.Row="3"
                       Margin="6"
                       Text="{Binding /Threads.Count,
                                      StringFormat='Threads: 0'}" />
            <TextBlock Grid.Row="4"
                       Margin="6"
                       Text="{Binding /TotalProcessorTime,
                                      StringFormat='Processor Time: \{0:G\}'}" />
        </Grid>
    </Grid>
</Window>
View Code

 

 

Key-Specific DataTemplate

 

View Code
  1 Character.cs
  2 
  3 using System;
  4 using System.Collections.Generic;
  5 using System.ComponentModel;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Threading.Tasks;
  9 
 10 namespace WpfApplication3
 11 {
 12     public enum Gender
 13     {
 14         Female,
 15         Male,
 16         Unknown
 17     }
 18     class Character : INotifyPropertyChanged
 19     {
 20         private string _first = string.Empty;
 21         public string First 
 22         {
 23             get { return _first; }
 24             set 
 25             {
 26                 if (_first != value)
 27                 {
 28                     _first = value;
 29                     RaisePropertyChanged("First");
 30                 }
 31                 
 32             }
 33         }
 34 
 35         private string _last = string.Empty;
 36         public string Last
 37         {
 38             get { return _last; }
 39             set
 40             {
 41                 if (_last != value)
 42                 {
 43                     _last = value;
 44                     RaisePropertyChanged("Last");
 45                 }
 46 
 47             }
 48         }
 49         private int _age = 0;
 50         public int Age
 51         {
 52             get { return _age; }
 53             set
 54             {
 55                 if (_age != value)
 56                 {
 57                     _age = value;
 58                     RaisePropertyChanged("Age");
 59                 }
 60             }
 61         }
 62 
 63         private Gender _gender = Gender.Unknown;
 64         public Gender Gender
 65         {
 66             get { return _gender; }
 67             set
 68             {
 69                 if (_gender != value)
 70                 {
 71                     _gender = value;
 72                     RaisePropertyChanged("Gender");
 73                 }
 74             }
 75         }
 76 
 77         private string _image = null;
 78         public string Image
 79         {
 80             get { return _image; }
 81             set
 82             {
 83                 if (_image != value)
 84                 {
 85                     _image = value;
 86                     RaisePropertyChanged("Image");
 87                 }
 88             }
 89         }
 90         public override string ToString()
 91         {
 92             return _first + " " + _last;
 93         }
 94 
 95         #region INotifyPropertyChanged Members
 96 
 97         public event PropertyChangedEventHandler PropertyChanged;
 98         protected void RaisePropertyChanged(string name)
 99         {
100             if (PropertyChanged != null)
101                 PropertyChanged(this, new PropertyChangedEventArgs(name));
102         }
103 
104         #endregion
105     }
106 }

View Code
 1 <Window x:Class="WpfApplication3.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:local="clr-namespace:WpfApplication3"
 5         Title="MainWindow" Height="400" Width="300">
 6   <Window.Resources>
 7     <DataTemplate x:Key="CharacterTemplate">
 8       <Grid>
 9         <Grid.ColumnDefinitions>
10           <ColumnDefinition Width="100"/>
11           <ColumnDefinition Width="*"/>
12         </Grid.ColumnDefinitions>
13         <Image Margin="5" Source="{Binding Image}" />
14         <StackPanel Grid.Column="1" Margin="5">
15           <TextBlock FontWeight="Bold" Text="{Binding First}" />
16         </StackPanel>
17       </Grid>
18     </DataTemplate>
19   </Window.Resources>
20   <ListBox Width="200" Height="300"
21            ItemTemplate="{StaticResource CharacterTemplate}">
22     <local:Character First="Bart" Last="Simpson" Age="10" Gender="Male" Image="images/bart.png" />
23     <local:Character First="Homer" Last="Simpson" Age="38" Gender="Male" Image="images/homer.png" />
24     <local:Character First="Lisa" Last="Simpson" Age="8" Gender="Female" Image="images/lisa.png" />
25     <local:Character First="Maggie" Last="Simpson" Age="10" Gender="Female" Image="images/maggie.png" />
26     <local:Character First="Marge" Last="Simpson" Age="10" Gender="Female" Image="images/marge.png" />
27   </ListBox>
28 </Window>

总结:上面这样x:key类的DataTemplate内部visualTree不需要指定binding的源:as long as we are binding to properties of the data item,WPF will automatically set that data item as the DataContext of the item container in which the template is inflated,The root element of the template, and consequently, all descendants within the template, will inherit this data context.

红字解释

View Code
1 <Image Margin="5" Source="{Binding Image}" />
2         <StackPanel Grid.Column="1" Margin="5">
3           <TextBlock FontWeight="Bold" Text="{Binding First}" />

使用DT如果是在ItemControl下:设置属性ItemTemplate="{StaticResource CharacterTemplate}"就可。

 

Type-Specific DataTemplate

即定义DT的x:type而不是x:key

View Code
1 <DataTemplate DataType="{x:Type sys:Boolean}">
2       <CheckBox IsChecked="{Binding Mode=OneWay}" />
3     </DataTemplate>

CLR DataType DataTemplate

 

 Instead of using x:Key=”CharacterTemplate”use DataType="{x:Type local:Character}", This produces a default visual representation for all Character objects that appear in the logical tree lower than the template declaration

View Code
 1 <DataTemplate DataType="{x:Type src:Character}">
 2     <Grid>
 3       <Grid.ColumnDefinitions>
 4         <ColumnDefinition Width="100" />
 5         <ColumnDefinition Width="*" />
 6       </Grid.ColumnDefinitions>
 7       <Image Margin="5" Source="{Binding Image}" />
 8       <StackPanel Grid.Column="1" Margin="5">
 9         <TextBlock FontWeight="Bold" Text="{Binding First}" />
10       </StackPanel>
11     </Grid>
12   </DataTemplate>

DataTemplateSlector

 简单的data selector是用data Trigger 比如男孩女孩enum的选择,或者true false bool的选择。

难的方法是根据custom data template selector来选择不同的DT,比如当>20岁的时候选择DT1,<20岁的时候选择DT2. (is a class that derives from DataTemplateSelector and overrides the SelectTemplate method to return a template based on custom code execution.)

 

 

 

 

 

Grid

posted @ 2013-03-18 11:51  若愚Shawn  阅读(338)  评论(0编辑  收藏  举报