WPF學習(8)數據綁定

說到數據綁定,其實這並非一個新的玩意兒。瞭解asp.net的朋友都知道,在asp.net中已經用到了這個概念,例如Repeater等的數據綁定。那麼,在WPF中的數據綁定相比較傳統的asp.net中的數據綁定又有哪些優勢呢?asp.net

1)具備雙向性,即從源到目標是雙向的
2)及時更新,源發生改變時,可以及時更新UI
3)Validation和Converter,前者保證數據的合法性,後者保證數據的有效性
接下來,咱們將從這麼幾個方面來講明:Binding對象(對應xaml中的Binding擴展標記)、Binding的Path以及Source、Validation及Converter和MultiBinding。ide

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的某個方法名。
這裏須要注意一下幾點:ui

1)有Path沒Source,將去找其父元素有該Path的DataContext;
2)有Source沒Path,則將Source也做爲其Path;this

3)無Source無Path,則將其父元素的DataContext既做爲Source也做爲Path。spa

關於Binding的Source這裏只說下Xml、Linq to Xml和ObjectDataProvider,其它幾種略過。.net

2.1使用Xml做爲Binding Source

首先,咱們來準備xml數據,命名爲Students.xml,以下:code

<?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代碼以下:orm

<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代碼以下:xml

    /// <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:\dotnet\WPF\學習\WPF\Demo\WpfDemo\DataBindingDemo\Students.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:\dotnet\WPF\學習\WPF\Demo\WpfDemo\DataBindingDemo\Students.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接口

相關文章
相關標籤/搜索