WPF Binding(绑定)详解

时间:2025-04-17 08:50:39

Binding概念理解:

        WPF为了实现了UI与数据逻辑的解耦,将UI从数据逻辑中分离出来形成Xaml文件,而UI与数据逻辑之间的联系则通过Bingding来实现。Bingding就像UI与数据逻辑之间的桥梁,能够让分离的两部分组合成一个整体,实现WPF的数据驱动。

        把Binding比作桥梁,那么它的两端分别是Binding的源(Source)和目标(Target),Path为Binding指定访问路径,数据从哪里来哪里就是源(未指明Source,数据就会一直往上查找),Bingding是驾在中间的桥梁,Bingding的目标是数据去向哪里。当Bingding的源变化需要通知目标变化时,需要通过属性的set语句激发一个ProtertyChanged事件。这个事件不需要我们自己声明,而是让数据源类实现名称空间中的INotifyPropertyChanged接口。

        Binding数据流向的属性是Mode:TwoWay(双向)、OneWay(单向)、OneTime(单次)、OneWayToSource(对数据源单向)和Default(根据具体目标情况确定,如可编辑未双向模式)。数据源根据什么类型变化进行通知,可设置属性UpdateSourceTrigger:PropertyChanged、LostFocus、Explicit(显式,在程序启动时更新源)和Default。

 实例链接:WPF Binding(绑定)详解实例

WPF前端数据绑定

实例如下:拖动Slider滑块值时,绑定的跟着变化。

    <Grid>
        <StackPanel>
            <TextBox Text="{Binding Path=Value,ElementName=slider1}"/>
            <Slider x:Name="slider1" Maximum="100" Minimum="0"/>
        </StackPanel>   
    </Grid>

WPF后端数据绑定

实例如下:一个绑定到另一个,另一个变化时它也跟着变化。

<TextBox x:Name="tbSource" BorderBrush="Black" Margin="5"/>
<TextBox x:Name="tbPath" BorderBrush="Black" Margin="5"/>

//1.创建Bingding
Binding binding = new Binding()
{
     Source = tbSource,                // 数据源  
     Path = new PropertyPath("Text"),  // 需绑定的数据源属性名  
     Mode = ,        // 绑定模式  
     UpdateSourceTrigger =     //触发器
};

//2.设置Bingding
//方法1
//(, binding);

//方法2
(
     tbPath                  // 需绑定的控件  
     ,
         // 需绑定的控件属性  
     ,
     binding);

Binding不同使用方式和场景:

Binding还支持多级路径。如,我们想让一个TextBox显示另一个TextBox的长度,可以这样写:

<StackPanel Margin="10">
     <TextBox x:Name="textBox1" BorderBrush="Black" Margin="5"/>
     <TextBox x:Name="textBox2" Text="{Binding Path=,ElementName=textBox1,Mode=OneWay}" BorderBrush="Black" Margin="5"/>
</StackPanel>

集合类型的索引器(Indexer)又称为带参数属性,所有索引器也能作为Path来使用。

<TextBox x:Name="txt1" BorderBrush="Black" Margin="5"/>
<TextBox x:Name="txt2" Text="{Binding Path=Text.[3],ElementName=txt1,Mode=OneWay}" BorderBrush="Black" Margin="5"/>

当数据源本身就是数据就不需要使用Path指明,Path后面可以.来表示,也可以之间省略Path。

<Window x:Class=""
        xmlns="/winfx/2006/xaml/presentation"
        xmlns:x="/winfx/2006/xaml"
        xmlns:d="/expression/blend/2008"
        xmlns:mc="/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfBingdingDemo"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="400">
    <>
        <sys:String x:Key="myStr">
            Hello World!
        </sys:String>
    </>
    <Grid>
        <StackPanel>
            <TextBox x:Name="txt1" BorderBrush="Black"  Text="{Binding Source={StaticResource ResourceKey=myStr},Mode=OneWay}" Margin="5"/>
            <TextBox x:Name="txt2" Text="{Binding Path=Text.[3],ElementName=txt1,Mode=OneWay}" BorderBrush="Black" Margin="5"/>
        </StackPanel>
    </Grid>
</Window>

当数据源是集合时,可以通过"/"来表示。当结合元素的属性仍然时集合时,我们想不子级集合中的元素当作Path,则可以使用多级斜线的语法(如:"/Provinces/")。

当没有指定数据源时,Binding会沿着UI元素树一路向上找,每到一个结点就要看看这个结点的DataContext是否具有Path所指定的属性。如果有,那就把这个对象作为自己的Source。

    //后台定义类
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
    
    <StackPanel>
            <>
                <local:Student Id="3" Name="Tom" Age="12"/>
            </>
            <!--未指明Source,Binding往上查找-->
            <TextBox Text="{Binding Name}" BorderBrush="Black" Margin="5"/>
    </StackPanel>

使用XML数据作为Binding源:

        .Net Framework提供了两套处理XML数据的类库:符合DOM标准类库,以LINQ为基础的类库。大多数据传输基于SOAP(简单对象访问协议),而SOAP又是通过将对象序列化为XML文本进行传输。XML文本是树形结构,它能方便地用于线性结合和树形结构数据。使用XPath指明Binding XML路径,路径后使用@符号加字符串表示的是XML元素的Attribute,不加@的字符串表示的是子级元素。

DOM标准的XML类库:

<StackPanel ="2" Margin="50">
      <>
          <XmlDataProvider x:Key="MyColors"  Source="E:\Xml\"  XPath="colors"/>
      </>
      <ListBox x:Name="listBox1"  Height="128"   Width="92"  ItemsSource="{Binding Source={StaticResource MyColors}, XPath=color/@name,Mode=TwoWay}"/>
       <TextBox x:Name="textBox1"  Height="28"  Width="92"  TextWrapping="Wrap" Text="{Binding SelectedValue, ElementName=listBox1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

LINQ查询的结果是IEnumerable<T>类型对象,而IEnumerable<T>派生自IEnumerable,所有它可以作为ItemsSource来使用。

XDocument xdoc = (@"E:\Xml\");
 = from element in ("color") where ("name").("b")
           select new
           {
                   Name=("name").Value,
                   ID= ("ID").Value
           };
 = "Name";

使用ObectDataProvider对象作为Binding的Source:

        在我们开发过程中,很难保证一个类所有数据都使用属性暴露出来,这个时候需要使用ObjectDataProvider来包装作为Binding源的数据对象。

实例:通过ObjectDataProvider传递对类方法并作为数据源。

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }

        public Student GetStudent()
        {
            return new Student
            {
                Id = 2,
                Name = "Jack",
                Age = 9
            };
        }
    }

    <Grid>
        <>
            <ObjectDataProvider x:Key="student"  ObjectType="{x:Type local:Student}" MethodName="GetStudent" />
        </>
        <TextBlock Text="{Binding Name,Source={StaticResource student}}" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
    </Grid>

使用Binding的RelativeSource属性进行绑定:

        当我们不确定作为Source的对象叫什么名字,但知道它与作为Binding目标的对象在UI布局上有相对关系,比如控件自己关联自己的某个数据、关联自己某级容器的数据。这个时候我们就要使用Binding的RelativeSource属性。

实例:绑定它上面布局元素的Name。

    <Grid x:Name="grid1">
        <Grid Width="100" Height="100" x:Name="grid2">
            <Border Width="60" Height="60">
                <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Grid},AncestorLevel=2},Path=Name}"/>
            </Border>
        </Grid>
    </Grid>

Binding对数据的转换和校验:

        我们可以在连接Source和Target之间Binding桥上设置关卡,来判断哪些符号条件的数据流通。通过实现ValidationRule这个抽象类,对数进行校验。

实例:与数值进行绑定,并对输入TextBox值进行校验,校验不通过则提示。

    <StackPanel>
        <TextBox x:Name="textBox1" Margin="5"/>
        <Slider x:Name="slider1" Minimum="0" Maximum="100" Margin="5"/>
    </StackPanel>
    public class RangeValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double d = 0;
            if(((),out d))
            {
                if(d>=0&&d<=100)
                {
                    return new ValidationResult(true, null);
                }
            }
            return new ValidationResult(false, "Validation Failed");
        }
    }

    public partial class ValidationRuleDemo : Window
    {
        public ValidationRuleDemo()
        {
            InitializeComponent();
            Binding binding = new Binding("Value") { Source = this.slider1 };
             = ;
            RangeValidationRule rule = new RangeValidationRule();
            (rule);
            //当数据校验失败时Binding会发出一个信号并以Binding对象的Target为起点在UI元素树上传播。
             = true;
            this.(, binding);
            this.(, new RoutedEventHandler());
        }

        private void ValidationError(object sender, RoutedEventArgs e)
        {
            this. = (this.textBox1)[0].();
        }
    }

Binding的数据转换(Converter):

        我们在进行数据绑定时,Source和Target传递数据类型可能不一致,这个时候我们就可以用Converter进行数据转换。

实例:TextBox前景色绑定到自身输入的Text,并通过Converter进行类型转换。

    <Grid>
        <TextBox Width="200" Height="30" FontSize="20" FontWeight="Bold" Foreground="{Binding RelativeSource={RelativeSource self},Path=Text,Mode=TwoWay, Converter={StaticResource ct}}"/>
    </Grid>
    public class BrushConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string color = (value).ToLower();
            switch(color)
            {
                case "red":
                    return new SolidColorBrush((Color)(color));
                case "green":
                    return new SolidColorBrush((Color)(color));
                case "blue":
                    return new SolidColorBrush((Color)(color));
            }
            return new SolidColorBrush();
        }
        //双向转换回调
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var solidColorBrush = (SolidColorBrush)value;
            if(solidColorBrush!=null)
            {
                return "Red";
            }
            return "Black";
        }
    }

多路绑定(MultiBinding):

实例:当多个TextBox输入字符一致时,Button的IsEnable属性为true,否Button不能点击。

        <StackPanel>
            <TextBox x:Name="txt1" Height="30" Margin="5"/>
            <TextBox x:Name="txt2" Height="30" Margin="5"/>
            <TextBox x:Name="txt3" Height="30" Margin="5"/>
            <Button x:Name="btn1" Content="Click" Width="100" Height="30" Margin="5"/>
        </StackPanel>
    public partial class MultiBindingDemo : Window
    {
        public MultiBindingDemo()
        {
            InitializeComponent();
            SetMultiBinding();
        }

        private void SetMultiBinding()
        {
            Binding b1 = new Binding("Text") { Source = this.txt1 };
            Binding b2 = new Binding("Text") { Source = this.txt2 };
            Binding b3 = new Binding("Text") { Source = this.txt3 };

            MultiBinding mb = new MultiBinding() { Mode =  };
            (b1);
            (b2);
            (b3);
             = new MultiBindingConverter();
            this.(, mb);
        }
    }

    public class MultiBindingConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if(!<string>().Any(text=>(text))&&values[0].ToString()==values[1].ToString() && values[0].ToString() == values[2].ToString())
            {
                return true;
            }
            return false;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

实例链接:WPF Binding(绑定)详解实例