热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WPF之Binding初探

  初学wpf,经常被Binding搞晕,以下记录写Binding的基础。首先,盗用张图。这图形象的说明了Binding的机理。对于Binding,意思是数据绑定,基本用法是:1、

 

 

初学wpf,经常被Binding搞晕,以下记录写Binding的基础。

首先,盗用张图。这图形象的说明了Binding的机理。

WPF之Binding初探

对于Binding,意思是数据绑定,基本用法是:

1、在xmal中使用

  如下,在TextBox上绑定了Slider的Value,WPF将会机智的进行双向绑定,即TextBox和Slider中任何一方改变,另外一方也将更随跟新。

        <TextBox Height="20" Margin="0,0,10,87" Text="{Binding ElementName=slider1,Path=Value}" BorderThickness="1"/>
        <Slider Maximum="100" Margin="0,50,0,35" Name="slider1"/>

上面的TextBox相当与下面的。Mode就是数据绑定的方向。

控制Binding数据流向的属性是Model,它的类型是BindingModel的枚举。BindingModel可以取值为TwoWay、OneWay、OneTime、OneWayToSource和Default。这里的Default指的是Binding的模式会根据目标是实际情况来确定,如果是可以编辑的(TextBox的Text属性),Default就采用双向模式。如果是TextBlock,不可编辑,就使用单向模式。

如果我们在TextBox里面输入一个恰当的值按Tab键、让焦点离开TextBox,则Slider手柄就会跳转至相应的值那里。

为什么一定要在TextBox失去焦点以后才改变值呢?这就引出了Binding的另外一个属性-----UpdateSourceTrigger,它的类型是UpdateSourceTrigger枚举,可取值为PropertyChanged、LostFous、Explicit和Default。显然,对于Text的Default行为与LostFocus一致,我们只需要把这个值改成PropertyChanged,则Slider就会随着输入值的变化而变化了。

即加入以下带代码:

UpdateSourceTrigger=PropertyChanged
注意:
顺便提一句,Binding还具有NotifyOnSourceUpdated属性和NotifyOnTargetUpdated两个bool类型是属性。如果设置为True,则在源或目标被更新以后就会触发相应的SourceUpdated事件和TargetUpdated事件。实际工作中我们可以监听这两个事件来找出来哪些数据或控件被更新了。

2、在后台c#代码中使用

等效与上面的代码

  this.textbox1.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = slider1.Name,Mode=BindingMode.TwoWay});

  或者是  

  this.textbox1.SetBinding(TextBox.TextProperty, new Binding("Value") { Source=slider1, Mode = BindingMode.TwoWay });

这里的ElementName与Source之间是有区别的,Source是object类型的,能接受的是一个对象,而ElementName是string型的,之恩能够接受名称。下面这样做是不行地

WPF之Binding初探

3、Binding的数据校验

inding的ValidationRules属性是Collection,从它的名称和数据类型我们可以得知可以为每个Binding设置多个数据校验条件,每一个条件是一个ValidationRule对象。ValidationRule是一个抽象类,在使用的时候我们需要创建它的派生类并实现它的Validate方法的返回值是ValidateionResult类型对象,如果通过验证,就把ValidateionResult对象的IsValidate属性设为true,反之,则需要将IsValidate设置为false并为其ErrorContent属性设置一个合适的消息内容(一般是字符串)。
下面这个程序的UI绘制一个TextBox和一个Slider,然后在后台C#代码中建立Binding把它们关联起来---- 已Slide为源,TextBox为目标。Slider的取值范围是0~100,也就是说我们需要验证TextBox中输入的值是不是在0~100之间。

先定义一个ValidationRule的子类。

    class RangeValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            double d = 0;
            if (double.TryParse(value.ToString(), out d))
            {
                if (d >= 0 && d <= 100)
                {
                    return new ValidationResult(true, null);
                }
            }
            return new ValidationResult(false, "ErrorContent");
        }
    }

然后在后台C#代码中这样定义binding。

 

Binding bind =new Binding("Value") { UpdateSourceTrigger= UpdateSourceTrigger.PropertyChanged,Source=slider1, Mode= BindingMode.TwoWay};            ValidationRule rule = new RangeValidationRule();  

rule.ValidatesOnTargetUpdated = true;  

bind.ValidationRules.Add(rule); 

至于怎么在xmal中实现上面的数据校验,我还没做过,等有时间在探索。

4、关于path

尽管在XAML代码中或者Binding类的构造器参数列表中我们使用字符串来表示Path,但Path的实际类型是PropertyPath。

这样用获取Text的长度属性

Binding Path=Text.Length,ElementName=textBox1,Mode=OneWay

 

获取第四个字符

Binding Path=Text[3]

 

有的时候我们会在代码中我们看大Path是一个“.”或者干脆没有Path的Binding,着实让人摸不着头脑。原来这是一种比较特殊的情况---Binding源本身就是一种数据且不需要Path来指明。典型的string,int等基本类型都是这样,他们是实例本身就是数据,我们无法指定通过那个属性来访问这个数据,这是我们只需要将这个数据设置为.就可以了。在XAML中这个.可以忽略不写,但是在C#中编程必须要带上。

   <StackPanel Height="184" Name="stackPanel1" Width="288">  
            <StackPanel.Resources>  
                <String:String x:Key="myString">  
                    菩提本无树,何处染尘埃。  
          String:String>  
              
       StackPanel.Resources>  
        <TextBlock Height="23" Name="textBlock1" Text="{Binding Path=.,Source={StaticResource ResourceKey=myString}}" />  
    StackPanel>  

或者

<TextBlock Height="23" Name="textBlock1" Text="{Binding .,Source={StaticResource ResourceKey=myString}}" /> 

或者

<TextBlock Height="23" Name="textBlock1" Text="{Binding Source={StaticResource ResourceKey=myString}}" />

后台C#写法

 string myString = "菩提本无树,明镜亦无台。本来无一物,何处染尘埃。";
this.textBlock1.SetBinding(TextBlock.TextProperty, new Binding(".") { Source=myString});

5、最后,谈谈数据绑定一点心得

wpf的数据绑定主要是为了达到数据驱动UI的目的,说到底,还是为了通知UI,数据已经改变了。。。。

WPF的绝大多数控件都实现了INotifyPropertyChanged,所以当我们绑定两个控件时,一个控件的值改变,另一个的值也就能实时更新,这样才有了我们在上面的TextBox和Slider中改变任意一个的值,另外一个实时改变,这里为了做个验证,我在Slider上绑定一个实例属性,看看属性的值改变的时候Slider的值是不是跟着改变,理论上是不会的。

        public MainWindow()
        {
            InitializeComponent();
            //this.textbox1.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = slider1.Name, Mode = BindingMode.TwoWay });
            this.slider1.SetBinding(Slider.ValueProperty, new Binding(".") { Source = ChangeValue });
            //ValidationRule rule = new RangeValidationRule();
            //rule.ValidatesOnTargetUpdated= true;
            

        }
        private int changeValue=20;

        public int ChangeValue
        {
            get { return changeValue; }
            set { changeValue = value; }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            changeValue += 10;
        }

单击button并没有发现Slider的值跟着改变,这里说明ChangeValue并不是依赖属性的前提下并不能实时通知UI的Slider,自己的值改变的事。

这里就引出了依赖属性的学习。如果吧ChangeValue定义成依赖属性,就可以是实现实时通知UI的功能了。

6、在5中,我们将clr属性绑定到Slider后,发现clr属性的值改变的时候Slider的值并不会跟着改变。说明clr属性并不具有实时通知UI界面的能力。这里,我知道2种方法实现这种实时通知的功能。一种是继承INotifyPropertyChanged接口,这个接口的实现在MVVM架构里面经常拿来使用。另外一种就是WPF的依赖属性来实现了。尽管MainWindow类没有实现INotifyPropertyChanged接口,当属性的值发送改变时与之关联的binding对象依然可以得到通知,依赖属性默认的带有这种功能,天生就是合格的数据源。

我们看下用下面的代码

xmal:

<TextBox Name="textbox1" Height="20" Margin="0,0,10,87" BorderThickness="1"/>
        <Slider Maximum="100" Margin="0,50,0,35" Name="slider1"/>
        <Button Width="100" Height="20" Margin="10,87,407,0" Click="Button_Click_1">Button>

c#:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            //this.textbox1.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = slider1.Name, Mode = BindingMode.TwoWay });
            this.slider1.SetBinding(Slider.ValueProperty, new Binding("changeValue") { Source = this, Mode = BindingMode.TwoWay });
            this.textbox1.SetBinding(TextBox.TextProperty, new Binding(".") { Source = clrValue });
            //this.textbox1.SetBinding(TextBox.TextProperty, new Binding("clrValue") { Source = this });//这样写的话程序运行时textbox1是空白,不显示clrValue的初始化值
            //ValidationRule rule = new RangeValidationRule();
            //rule.ValidatesOnTargetUpdated= true;
            

        }

        private string clrValue="10";

        public string ClrValue
        {
            get { return clrValue; }
            set { clrValue = value; }
        }

        public string changeValue
        {
            get { return (string)GetValue(changeValueProperty); }
            set { SetValue(changeValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for changeValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty changeValueProperty =
            DependencyProperty.Register("changeValue", typeof(string), typeof(MainWindow), new PropertyMetadata("20"));

        
        //private int changeValue=20;
        //public int ChangeValue
        //{
        //    get { return changeValue; }
        //    set { changeValue = value; }
        //}

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            clrValue = "80";
            changeValue = "50";
        }
        
    }

上面的代码定义了一个clr属性clrValue和一个依赖属性changeValue,依赖属性绑定到Slider上,clr属性绑定到textbox上,button单击是两个属性的值都发生了变化,我们看看哪个的变化能实时通知到UI界面上。

 程序运行时:

WPF之Binding初探

单击button后,发现textbox里面的值没变,而slider的值变了,说明依赖属性的值改变了能实时通知到UI。这就能力对于clr属性来说本身是没有的,如果clr属性的类实现了INotifyPropertyChange接口,那它也就具有这种能力。

WPF之Binding初探

 

一个类实现INotifyPropertyChanged接口,线面是最简的实现

  class NotificationObject:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

然后需要具有通知UI能力的类只需继承自这个类就行了,这个Model的Myproperty的改变就能实现通知UI了

class Model : NotificationObject
    {
        private string _MyProperty = "Hello!";

        public string Myproperty
        {
            get { return _MyProperty; }
            set
            {
                _MyProperty = value;
                this.RaisePropertyChanged("Myproperty");
            }
        }

        public void Copy(object obj)
        {
            this.Myproperty += " en,Hello!";
        }
    }

 

 7、就上面6中的数据绑定而言,我有一些不解,先记录下来,等以后彻底理解了就来详细说明。

看代码

this.slider1.SetBinding(Slider.ValueProperty, new Binding("changeValue") { Source = this, Mode = BindingMode.TwoWay });
//this.slider1.SetBinding(Slider.ValueProperty, new Binding(".") { Source = changValue, Mode = BindingMode.TwoWay });//这样写不能实现实时通知UI
this.textbox1.SetBinding(TextBox.TextProperty, new Binding(".") { Source = clrValue }); 
//this.textbox1.SetBinding(TextBox.TextProperty, new Binding("clrValue") { Source = this });//这样写的话程序运行时textbox1是空白,不显示clrValue的初始化值

在上面的代码中,我发现对于依赖属性,如果使用第一行的写法,也就是Binding的双引号里面放属性名,Source里面放this,可以实现实时通知UI的功能,如果按照第二行的
写法,程序运行时能显示changeValue的初始值,而不能实现
通知UI值改变的功能。
而对于clr属性来说,也就是Binding的双引号里面放点,Source里面放属性名,这样才能在程序运行时让textbox显示属性的值,乳沟按第二种写法,textbox显示的是空白。
以上两种写法导致的的差异的原因,作为新学的我来说还是不能理解,希望能尽快搞懂。
写博客真费功夫以上内容参考了很多深入浅出WPF中的内容。



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
author-avatar
将登太行的2602939913
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有