zoukankan      html  css  js  c++  java
  • WPF学习(8)数据绑定 https://www.cnblogs.com/jellochen/p/3541197.html

    说到数据绑定,其实这并不是一个新的玩意儿。了解asp.net的朋友都知道,在asp.net中已经用到了这个概念,例如Repeater等的数据绑定。那么,在WPF中的数据绑定相比较传统的asp.net中的数据绑定又有哪些优点呢?

    1)具有双向性,即从源到目标是双向的
    2)及时更新,源发生改变时,能够及时更新UI
    3)Validation和Converter,前者保证数据的合法性,后者保证数据的有效性
    接下来,我们将从这么几个方面来说明:Binding对象(对应xaml中的Binding扩展标记)、Binding的Path以及Source、Validation及Converter和MultiBinding。

    1.Binding对象

    在Binding对象中,主要成员可以分为这么几类:
    1)路径:Path属性和XPath属性
    2)源:Source、RelativeSource和ElementName
    3)更新通知:NotifyOnSourceUpdated、NotifyOnTargetUpdated和NotifyOnValidationError
    4)转换器:Converter、ConverterCulture和ConverterParameter
    5)验证:ValidatesOnDataErrors、ValidatesOnExceptions、ValidatesOnNotifyDataErrors和ValidationRules
    6)绑定方式:Mode,BindingMode枚举类型:TwoWay,OneWay,OneTime,OneWayToSource和Default
    需要注意的是:Binding的目标必须是依赖对象的某个依赖属性。

    2.Binding的Path以及Source

    对于Binding的Path及Source,并非要是依赖属性及依赖对象。几乎任何一个对象都可以作为Binding的Source,主要有普通CLR对象、ado.net对象、XML、Linq、依赖对象、容器的DataContext、RelativeSource和ObjectDataProvider等。
    而Path就是普通CLR对象、容器的DataContext、RelativeSource和Linq的某个属性、
    ado.net对象的某个字段、依赖对象的某个依赖属性和ObjectDataProvider的某个方法名。
    这里需要注意一下几点:

    1)有Path没Source,将去找其父元素有该Path的DataContext;
    2)有Source没Path,则将Source也作为其Path;

    3)无Source无Path,则将其父元素的DataContext既作为Source也作为Path。

    关于Binding的Source这里只说下Xml、Linq to Xml和ObjectDataProvider,其它几种略过。

    2.1使用Xml作为Binding Source

    首先,我们来准备xml数据,命名为Students.xml,如下:

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <Students>
      <Student ID="1">
        <Name>Jello</Name>
        <Score>80</Score>
      </Student>
      <Student ID="2">
        <Name>Taffy</Name>
        <Score>100</Score>
      </Student>
    </Students>
    复制代码

    Xaml代码如下:

    复制代码
    <Window x:Class="DataBindingDemo.XmlWindow1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="XmlWindow1" Height="300" Width="300">
        <Grid>
            <ListBox x:Name="lb">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="tbID" Text="{Binding XPath=@ID}" Width="20" Foreground="Red"/>
                            <TextBlock x:Name="tbName" Text="{Binding XPath=Name}" Width="40" Foreground="Green"/>
                            <TextBlock x:Name="tbScore" Text="{Binding XPath=Score}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    复制代码

     cs代码如下:

    复制代码
        /// <summary>
        /// XmlWindow1.xaml 的交互逻辑
        /// </summary>
        public partial class XmlWindow1 : Window
        {
            public XmlWindow1()
            {
                InitializeComponent();
                /*第一种写法:
                XmlDocument doc = new XmlDocument();
                doc.Load(@"./Students.xml");
                XmlDataProvider xdp = new XmlDataProvider();
                xdp.Document = doc;
                xdp.XPath = "/Students/Student";
                this.lb.SetBinding(ListBox.ItemsSourceProperty, new Binding(".") { Source = xdp });
                 */
                //第二种写法:
                XmlDataProvider xdp = new XmlDataProvider();
                xdp.Source = new Uri(@"F:dotnetWPF学习WPFDemoWpfDemoDataBindingDemoStudents.xml");
                xdp.XPath = "/Students/Student";
                this.lb.DataContext = xdp;
                this.lb.SetBinding(ListBox.ItemsSourceProperty, new Binding());
            }
        }
    复制代码

     第一种写法借助于XmlDataProvider的Document属性,第二种写法借助于XmlDataProvider的Source属性。

    如果想直接在Xaml代码里面来直接使用XmlDataProvider来进行绑定的话,需要将Xml数据放在<x:XData>...</x:XData>标签内,代码如下:

    复制代码
    <Window x:Class="DataBindingDemo.XmlWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="XmlWindow" Height="300" Width="300">
        <Window.Resources>
            <XmlDataProvider x:Key="xdp" XPath="Students/Student">
                <x:XData>
                    <Students xmlns="">
                        <Student ID="1">
                            <Name>Jello</Name>
                            <Score>80</Score>
                        </Student>
                        <Student ID="2">
                            <Name>Taffy</Name>
                            <Score>100</Score>
                        </Student>
                    </Students>
                </x:XData>
            </XmlDataProvider>
        </Window.Resources>
        <Grid>
            <ListBox x:Name="lb" ItemsSource="{Binding Source={StaticResource xdp}}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding XPath=@ID}" Width="20" Foreground="Red"/>
                            <TextBlock Text="{Binding XPath=Name}" Width="40" Foreground="Green"/>
                            <TextBlock Text="{Binding XPath=Score}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    复制代码

     效果如下:

    2.2使用Linq to Xml作为Binding Source

    在使用Linq to Xml作为Binding Source之前,我们当然需要准备model,这里创建一个Student实体类:

        public class Student
        {
            public int ID { get; set; }
            public string Name { get; set; }
            public int Score { get; set; }
        }

    Xaml代码如下:

    复制代码
    <Window x:Class="DataBindingDemo.XmlWindow2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="XmlWindow2" Height="300" Width="300">
        <Grid>
            <ListBox x:Name="lb">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock x:Name="tbID" Text="{Binding Path=ID}" Width="20" Foreground="Red"/>
                            <TextBlock x:Name="tbName" Text="{Binding Path=Name}" Width="40" Foreground="Green"/>
                            <TextBlock x:Name="tbScore" Text="{Binding Path=Score}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    复制代码

     cs代码如下:

    复制代码
        /// <summary>
        /// XmlWindow2.xaml 的交互逻辑
        /// </summary>
        public partial class XmlWindow2 : Window
        {
            public XmlWindow2()
            {
                InitializeComponent();
    
                XDocument doc = XDocument.Load(@"F:dotnetWPF学习WPFDemoWpfDemoDataBindingDemoStudents.xml");
                this.lb.ItemsSource = from e in doc.Descendants("Student")
                                      select new Student
                                      {
                                          ID = Int32.Parse(e.Attribute("ID").Value),
                                          Name = e.Element("Name").Value,
                                          Score = Int32.Parse(e.Element("Score").Value)
                                      };
            }
        }
    复制代码

     2.3使用ObjectDataProvider作为Binding Source

    其他对象都是针对属性作为Path的情况,而ObjectDataProvider对象主要是针对方法。所以,我们先准备一个具有Add方法的Calculate类:

    复制代码
        public class Calculate
        {
            public double Add(string d1, string d2)
            {
                double i, j;
                if (double.TryParse(d1, out i) && double.TryParse(d2, out j))
                    return i + j;
                else
                    return 0;
            }
        }
    复制代码

    Xaml代码如下:

    复制代码
    <Window x:Class="DataBindingDemo.ODPWindow1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="ODPWindow1" Height="300" Width="300">
        <Grid>
            <StackPanel>
                <TextBox x:Name="tb1"/>
                <TextBox x:Name="tb2"/>
                <TextBox x:Name="tbResult"/>
            </StackPanel>
        </Grid>
    </Window>
    复制代码

     cs代码如下:

    复制代码
        /// <summary>
        /// ODPWindow1.xaml 的交互逻辑
        /// </summary>
        public partial class ODPWindow1 : Window
        {
            public ODPWindow1()
            {
                InitializeComponent();
    
                ObjectDataProvider odp = new ObjectDataProvider();
                odp.ObjectInstance = new Calculate();
                odp.MethodName = "Add";
                odp.MethodParameters.Add("0");
                odp.MethodParameters.Add("0");
    
                this.tb1.SetBinding(TextBox.TextProperty, new Binding("MethodParameters[0]")
                {
                    Source = odp,
                    BindsDirectlyToSource = true,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
                this.tb2.SetBinding(TextBox.TextProperty, new Binding("MethodParameters[1]")
                {
                    Source = odp,
                    BindsDirectlyToSource = true,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
                this.tbResult.SetBinding(TextBox.TextProperty, new Binding(".")
                    {
                        Source = odp
                    });
            }
        }
    复制代码

     效果如下:

    3.Binding的Converter

    数据在Binding的Target端和Source端交换时,经常会出现类型或者格式不一致的情况,这时候,我们就可以使用Converter来处理。

    WPF内置了许多的Converter,例如:

    <Grid.Background>
        Red
    </Grid.Background>

     Backgroud属性是Brush抽象类型,而我们只是用一个Red字符串赋值就能达到效果,这是内置的从String类型到SolidColorBrush类型的Converter。

    我们也可以实现自己的Converter,只要实现IValueConverter接口即可。

    4.Binding的Validation

    数据在Binding的Target端和Source端交换时,除了经常出现类型或者格式不一致,还出现数据不合法的情况。为了避免脏数据的出现,需要在交换前进行Validate。例如:

    <StackPanel>
                <TextBox x:Name="tb" Text="{Binding Value,ElementName=slider,UpdateSourceTrigger=PropertyChanged}"/>
                <Slider x:Name="slider" Minimum="0" Maximum="99" />
            </StackPanel>

    这里,我们要实现的效果是在TextBox中输入一个0到99之间的数字,Slider会划到相应位置,若输入的数字不在该范围,则TextBox提示数据不合法。

    Xaml代码如下:

    <StackPanel>
                <TextBox x:Name="tb" />
                <Slider x:Name="slider" Minimum="0" Maximum="200" />
            </StackPanel>

     cs代码如下:

    复制代码
        /// <summary>
        /// ConverterWnd.xaml 的交互逻辑
        /// </summary>
        public partial class ConverterWnd : Window
        {
            public ConverterWnd()
            {
                InitializeComponent();
    
                Binding binding = new Binding("Value")
                    {
                        Source = this.slider,
                        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
                    };
                RangeValidation rv = new RangeValidation();
                rv.ValidatesOnTargetUpdated = true;//验证Source
                binding.NotifyOnValidationError = true;//触发Validation.ErrorEvent
                binding.ValidationRules.Add(rv);
                this.tb.SetBinding(TextBox.TextProperty, binding);
                this.tb.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(NotifyError));
            }
            protected void NotifyError(object sender, RoutedEventArgs e)
            {
                TextBox tb = sender as TextBox;
                if (tb != null)
                {
                    if (Validation.GetErrors(tb).Count > 0)
                        tb.ToolTip = Validation.GetErrors(tb)[0].ErrorContent.ToString();
                }
            }
        }
        public class RangeValidation : ValidationRule
        {
            public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
            {
                double d;
                if (double.TryParse(value.ToString(), out d))
                {
                    if (d >= 0 && d <= 99)
                        return new ValidationResult(true, null);
                }
                return new ValidationResult(false, "数据不合法");
            }
        }
    复制代码

    这里将Slider的最大值设为了200,当其值大于99时,由于ValidatesOnTargetUpdate=true,所以也会路由Validation.ErrorEvent事件。

    效果如下:

     其实,当未添加验证时,Slider的CoerceValue会强制处理。

    5.MultiBinding多路绑定

    经常会遇到这样的需求,要求显示“开始日期 -- 结束日期”这样的格式,这时候比较好的做法就是使用MultiBinding,当然,你也可以重新定义一个属性。

    Xaml代码如下:

    复制代码
    <Grid>
            <StackPanel>
                <TextBlock Text="{Binding Name}"/>
                <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0:yyyy-MM-dd}至{1:yyyy-MM-dd}">
                            <Binding Path="StartDate" />
                            <Binding Path="EndDate" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </StackPanel>
        </Grid>
    复制代码

     cs代码如下:

    复制代码
        /// <summary>
        /// MultiBindingWnd.xaml 的交互逻辑
        /// </summary>
        public partial class MultiBindingWnd : Window
        {
            public MultiBindingWnd()
            {
                InitializeComponent();
    
                Fruit fruit = new Fruit() { Name = "Apple", StartDate = DateTime.Today, EndDate = DateTime.Today.AddYears(1) };
                this.DataContext = fruit;
            }
        }
    复制代码

     这里需要注意两点:

    1)MultiBinding添加Binding的顺序会影响Converter

    2)MultiBinding的Converter实现的是IMultiValueConverter接口

  • 相关阅读:
    HDU 1010 Tempter of the Bone
    HDU 4421 Bit Magic(奇葩式解法)
    HDU 2614 Beat 深搜DFS
    HDU 1495 非常可乐 BFS 搜索
    Road to Cinema
    Sea Battle
    Interview with Oleg
    Spotlights
    Substring
    Dominating Patterns
  • 原文地址:https://www.cnblogs.com/Jeely/p/11076673.html
Copyright © 2011-2022 走看看