wpf控件之间互相绑定

WPF学习之数据绑定

WPF中的数据绑定提供了很强大的功能。与普通的WinForm程序相比,其绑定功能为我们提供了很多便利,例如Binding对象的自动通知/刷新,Converter,Validation Rules,Two Way Binding等功能,省去了很多维护的繁琐工作。另外对于WPF中提供的数据模板功能,让我们可以轻松定制可以被复用的控制呈现的模块—但这是以数据绑定为前提来做到轻松易用的效果的。数据提供者例如XmlDataProvider和ObjectDataProvider更是简化了将对象以特定方式绑定并呈现的过程。可以说,数据绑定是WPF中让我们真正能够开始体现其便利性的特征之一,而对以数据驱动的应用来讲,其重要性不言而喻。

数据绑定的关键是System.Windows.Data.Binding对象,它会把两个对象(UI对象与UI对象之间,UI对象与.NET数据对象之间)按照指定的方式粘合在一起,并在他们之间建立一条通信通道,绑定一旦建立,接下来的应用生命周期中它可以自己独立完成所有的同步工作。根据其应用场合的不同我们将在本文中从以下几个部分分别讨论:

·         对象间的绑定

·         绑定到集合

·         数据模板

·         向绑定添加规则和转换器

1.     UI对象间的绑定

UI对象间的绑定,也是最基本的形式,通常是将源对象Source的某个属性值绑定 (拷贝) 到目标对象Destination的某个属性上。源属性可以是任意类型,但目标属性必须是依赖属性(Dependency Property)。通常情况下我们对于UI对象间的绑定源属性和目标属性都是依赖属性 (有些属性不是) ,因为依赖属性有垂直的内嵌变更通知机制,WPF可以保持目标属性和源属性的同步。

看个简单的例子是如何在XAML中实现数据绑定的:

<Window x:Class="Allan.WpfBinding.Demo.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Basic Bindings" Height="400" Width="700" Style="{StaticResource windowStyle}">

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="40" />

            <RowDefinition Height="*" />

            <RowDefinition Height="40" />

        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">

            <Button x:Name="btnBasicBinding" Content="Basic" Style="{StaticResource buttonStyle}"></Button>

            <Button x:Name="btnCollectionBinding" Content="Collection" Style="{StaticResource buttonStyle}"></Button>

            <Button x:Name="btnDataTemplate" Content="Data Template" Style="{StaticResource buttonStyle}"></Button>

            <Button x:Name="btnAdvanceBindings" Content="Advance" Style="{StaticResource buttonStyle}"></Button>

            <Button x:Name="btnExit" Content="Exit" Style="{StaticResource buttonStyle}"></Button>

        </StackPanel>

        <StackPanel Grid.Row="1" HorizontalAlignment="Left">

            <TextBox x:Name="txtName" Margin="5" Width="400" BorderThickness="0" Height="50" Text="Source Element"></TextBox>

            <TextBlock x:Name="tbShowMessage" Margin="5" Width="400" Height="50" Text="{Binding ElementName=txtName,Path=Text }" />            

        </StackPanel>

    </Grid>

</Window>

·         XAML绑定语法:

上边的代码我们将名为txtName的对象的Text属性作为源对象分别绑定给了两个TextBlock的Text属性。这里我们用了Binding关键字并指定了ElementName和Path,这两个就是指定源对象(Source)和源属性(Source Property). 通常我们在设定绑定时都用与StaticResource标记类似的语法{Binding… }并设置ElementName和Path属性:

Text=”{Binding ElementName=SourceObjectName, Path=SourceProperty}”

·         用Coding(C#)添加Binding

而对于C#里和绑定相关的代码,则看起来会罗嗦很多。但它们都同样的使用了Binding对象,然后指定PropertyPath的一个实例为源属性,然后可以有两个方法来加载绑定规则:

1.       调用FrameworkElement 或FrameworkContentElement对象的SetBinding方法

2.       调用BindingOperations.SetBinding静态方法

以下代码实现了和上边XAML文件类似的功能:

Binding binding = new Binding();

//设置源对象

binding.Source = txtName;

//设置源属性

binding.Path = new PropertyPath("Text");

//添加到目标属性

this.tbShowMessage.SetBinding(TextBlock.TextProperty, binding);

//or

//BindingOperations.SetBinding(tbShowMessage, TextBlock.TextProperty, binding);

·         用Coding(C#)移除Binding

当你在应用程序中某个地方添加了绑定,而在某个时候又不想这个绑定在接下来继续有效时,你可以有两种方式来断开这个绑定:

1.       用BindingOperations.ClearBinding静态方法。

例如BindingOperations.ClearBinding(currentTextBlock, TextBlock.TextProperty); BindingOperations同时还提供了ClearAllBindings方法,只需要传入要清除绑定的目标对象的名称,它就会将所有这个对象的绑定移除。

2.       简单的将目标属性设置为一个新的值。

这个简单的方法同样有效,可以断开与前边设置的binding的连接。简单的设置为任何值即可:如:currentTextBlock.Text = “it’s a new value.”;

·         Binding对象的属性

Property

Description

Converter

转换器

ElementName

绑定的源对象

FallbackValue

绑定无法返回有效值时的默认显示。

Mode

绑定方式

Path

属性

RelativeSource

常用于自身绑定或者数据模板中来指定绑定的源对象。

Source

源对象

StringFormat

格式化表达式

UpdateSourceTrigger

Sets the events on which binding will occur.

ValidationRules

验证规则

总结:对于对象间的绑定,绑定源为ElementName,Path为绑定源属性。ElementName必须为以下可选项之一:

DataContext

DataContext是WPF最后才试图查找的源。一旦RelativeSource和Source对象都没有被设置,则会在逻辑树种向上搜寻。

RelativeSource

用来标识和当前控件关联的对象,通常用于自我引用或数据模板。

Source

数据提供者/对象

2.     绑定到集合

·         利用ItemsSource来绑定数据源

常用标记:{Binding Path =””}    ItemSource    DisplayMemberPath  

通常来说这是我们在做以数据驱动为主的应用时最经常用到的绑定方式。WPF支持任何类型的.NET对象作为数据源绑定到WPF对象。对于所有的ItemsControl对象都有一个ItemsSource依赖属性,这是专门为数据绑定而准备的。ItemsSource的类型是IEnumerable,所以对于我们几乎所有的集合类型我们都可以轻易的改变成ItemsSource的源对象。通过以下语句我们可以将一个名为photos的集合赋予ListBox对象,并以显示Name属性的值:

<ListBox x:Name=”pictureBox” DisplayMemberPath=”Name”  

ItemsSource=”(Binding {DynamicResource photos}”

我们知道,依赖属性内建的垂直通知功能让UI对象间的绑定可以自己负责同步处理,但是对于.NET集合/对象来讲,它不具备这样的能力。为了让目标属性与源集合的更改保持同步,源集合必须实现一个叫INotifyCollectionChanged的接口,但通常我们只需要将集合类继承于ObservableCollection类即可。因为ObservableCollection实现了INotifyPropertyChanged和INotifyCollectionChanged接口。示例代码中我们这么去定义Photos集合类:

public class Photos : ObservableCollection<Photo>

·         利用DataContext来作为共享数据源

常用标记:{Binding Path=””}   DataContext

顾名思义,DataContext就是数据上下文对象,它是为了避免多个对象共享一个数据源时重复的对所有对象显式地用binding标记每个Source/RelativeSource/ElementName,而把同一个数据源在上下文对象的某个范围内共享,这样当一个绑定没有显式的源对象时,WPF会便利逻辑数找到一个非空的DataContext为止。

例如我们可以通过以下代码给ListBox和Title设置绑定:

<StackPanel Orentation=”Vertical” Margin=”5” DataContext=”{DynamicResource photos}”>

<Label x:Name=”TitleLabel” Content=”{Binding Path=Count}” DockPanel.Dock=”Bottom” />

<ListBox x:Name=”pictureBox” DisplayMemeberPath=”Name” ItemSource=”{Binding}” />

</StackPanel>

对于这些简单的绑定我们可以很灵活的组合他们的应用来达到我们的要求,这也是我们通常使用的方法。例如:

<Window.Resources>

     <local:Employee 

        x:Key="MyEmployee" EmployeeNumber="123" FirstName="John" 

       LastName="Doe" Department="Product Development" Title="QA Manager" />

</Window.Resources>

<Grid DataContext="{StaticResource MyEmployee}">

    <TextBox Text="{Binding Path=EmployeeNumber}"></TextBox>

    <TextBox Text="{Binding Path=FirstName}"></TextBox>

    <TextBox Text="{Binding Path=LastName}" />

    <TextBox Text="{Binding Path=Title}"></TextBox>

    <TextBox Text="{Binding Path=Department}" />

</Grid>

总结:对于集合的绑定,通常会需要用到以下几个标记:

DisplayMemberPath

指定源对象中被显示的属性。ToString()方法会被默认调用。

ItemsSource

指定要显示的数据源

ItemsTemplate

指定以什么样的格式来显示数据(类似于符合控件,可以在数据模板中利用多种控件来控制展现方式)

Path

数据源对象中的属性—控制显示

DataContext

共享数据源

3.     数据模板 – Data Template

当源属性和目标属性为兼容的数据类型,且源所显示的东西正是你需要显示的东西时,数据绑定确实很简单,你只需要向Section 1中讲的来匹配对象关系即可。而通常情况下我们对数据绑定都要做一些定制,特别对于.NET对象的绑定,你需要将数据源按照不同的方式分割显示。Data Template就负责来完成这样的功能:按照预想的数据展现模式将数据源的不同部分显示,而其作为可以被复用的独立结构,一旦定义可以被添加到一个对象内部,将会创建一个全新的可视树。

数据模板通常会被应用到以下几类控件来填充其类型为DataTemplate的属性:

·         内容控件(Content Control):ContentTemplate属性,控制Content的显示

·         项控件(Items Control) : ItemTemplate属性,应用于每个显示的项

·         头控件(Header Content Control) : HeaderTemplate属性,控制Header的展现。

每个数据模板的定义都是类似的方式,你可以像设计普通的窗体一样来设计其展现的方式,而且他们共享数据模板父空间所赋予的绑定源。例如下边的代码我们用一个图片来替代ListBox中的每一项:

    <ListBox x:Name="pictureBox" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"> 

        <ListBox.ItemTemplate>

                    <DataTemplate>

                        <Image Source="{Binding Path=FullPath}" Margin="3,8" Height="35">

                            <Image.LayoutTransform>

                                <StaticResource ResourceKey="st"/>

                            </Image.LayoutTransform>

                            <Image.ToolTip>

                                <StackPanel>

                                    <TextBlock Text="{Binding Path=Name}"/>

                                    <TextBlock Text="{Binding Path=DateTime}"/>

                                </StackPanel>

                            </Image.ToolTip>

                        </Image>

                    </DataTemplate>

                </ListBox.ItemTemplate>

        </ListBox>

最终的ListBox中每一项的展现将按照我们在数据模板中设定的样式以图片来显示:

通常数据模板是不需要被内联声明的,它可以被定义成一个资源存放在Application.Resources这样的全局资源辞典中,或者单独的Resource Dictionary中在多个元素间共享。

4.     向绑定添加规则和转换器

·         使用值转换器Value Converter

无论你的绑定XAML写得多么漂亮,所有的绑定值毫无疑问你都可以得到,但是它不总是可以满足你不经过任何程序变化显示出来就能满足要求的。例如对于本文示例代码的照片总数的显示,我们还想显示得更为智能一些:对于一些符合某种要求的数据我们将其背景显示为黄色,而对于有多于一条记录时我们显示15 Items,仅有一条时显示1 Item。这时Value Converter就派上用场了。

要定义一个Value Converter需要声明一个类让其继承于System.Windows.Data.IValueConverter接口,并实现其中的两个方法Convert和ConvertBack方法。

public class RawCountToDescriptionConverter : IValueConverter

    {

        public object Convert(object value, Type targetType, object parameter,

            CultureInfo culture)

        {

            // Let Parse throw an exception if the input is bad

            int num = int.Parse(value.ToString());

            return num + (num == 1 ? " item" : " items");

        }

        public object ConvertBack(object value, Type targetType, object parameter,

            CultureInfo culture)

        {

            throw new NotSupportedException();

        }

    }

在XAML中声明资源,然后将其通过静态资源引用的方式赋予Binding对象的Converter属性。

<Window.Resources>

        <local:CountToBackgroundConverter x:Key="myConverter"/>

        <local:RawCountToDescriptionConverter x:Key="myConverter2"/>

    </Window.Resources>

<TextBlock x:Name="filePath" DockPanel.Dock="Top" Style="{StaticResource titleStyle}" 

        Text="{Binding Count, Converter={StaticResource myConverter2}}"></TextBlock>

同样,我们可以对输入进行转换。如果数据的输入是被验证规则(如果有的话)标记为有效的,那么值转换器将会被调用,来对输入进行转换后反应出来。 (参考附件代码中的BindingConverter窗体)

·         向绑定添加规则

每个Binding对象都有一个ValidationRules属性,可以被设置为一个或多个派生自ValidationRule的对象,每个规则都会检查特定的条件并更具结果来标记数据的有效性。就像我们在ASP.NET中应用RequiredValidator, CustomValidator一样,你只需要定义自己的规则,WPF会在每次调用数据时(通常是TextBox等输入控件失去焦点)会调用验证检查。这些是在值转换器之前发生的,如果数据无效,它会标记此次更新无效,并将数据标记为无效—这是通过设置目标元素的Validation.HasError属性为true并触发Validation.Error事件(ValidationResult会被返回,并且其IsValid属性为false)。我们可以通过一个触发器来设定当数据无效时对用户的提示。例如下边的代码我们就通过定义一个JpgValidationRule,当数据无效时通过tooltip来提示用户输入无效。

public class JpgValidationRule : ValidationRule

{

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)

    {

        string filename = value.ToString();

        // Reject nonexistent files:

        if (!File.Exists(filename))

        {

            return new ValidationResult(false, "Value is not a valid file.");

        }

        // Reject files that don’t end in .jpg:

        if (!filename.EndsWith(".jpg", StringComparison.InvariantCultureIgnoreCase))

        {

            return new ValidationResult(false, "Value is not a .jpg file.");

        }

        else

        {

            return new ValidationResult(true, null);

        }

    }

}

上边的代码定义了我们验证的规则。接下来在XAML中来应用这个规则。我们将这个规则用来检测输入框中的数据是否合法:

<TextBox Style="{StaticResource validateTextBoxStyle}">

                <TextBox.Text>

                    <Binding UpdateSourceTrigger="PropertyChanged" Path="Department">

                        <Binding.ValidationRules>

                            <local:JpgValidationRule/>

                        </Binding.ValidationRules>

                    </Binding>

                </TextBox.Text>

            </TextBox>

当数据不合法时我们以什么样的方式来告诉用户呢?这里有两个方法可以做,一个是定义你自己的ErrorTemplate,另外一个是根据Trigger来设置一些可见信息。通常我们都可以来自己定义一些Error Provider和可以复用的ErrorTemplate,这个话题我们会在下一篇文章中讲。这里我们只让背景做改变并用tooltip来提示用户—显示的是ValidationRule返回的出错信息。因为都是控制显示的,所以定义成共用的Style:

<Style x:Key="validateTextBoxStyle" TargetType="{x:Type TextBox}">

            <Setter Property="Width" Value="300" />

            <Style.Triggers>

                <Trigger Property="Validation.HasError" Value="True">

                    <Setter Property="Background" Value="Red"/>

                        <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>

                </Trigger>

            </Style.Triggers>

        </Style>

 总的来说,对于验证,我们常用一下几个属性来定义错误验证规则和错误展现方式:
•Errors – 错误信息集合
•HasError – 是否有错误出现.
•ErrorTemplate – 错误提示的展现方式.
•Binding.ValidationRules 绑定验证规则

Coming Next:

本文我们了解了有关Binding以及和绑定有关的附加验证规则,转换器等。附加验证规则我们将在下一篇中了解更多自定义Error Provider,Error Template等。附加的Demo里提供了所有本文中的实例。在下一篇中我们会了解以下几个问题:

·         Validation Rules

·         Triggers

 点击这里获得DEMO:)

前台代码,请注意蓝色的代码

<Window x:Class="Wpf数据绑定.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" Height="371.642" Width="471.642">
    <Grid Margin="0,0,2,0">
        <Label Content="{Binding Username}" HorizontalAlignment="Left" Margin="147,53,0,0" VerticalAlignment="Top" Width="256" Height="57"/>
        <TextBox Name="pw"  HorizontalAlignment="Left" Height="50" Margin="132,115,0,0" TextWrapping="Wrap" Text="{Binding Pass}" VerticalAlignment="Top" Width="230"/>
        <Button Content="Button" HorizontalAlignment="Left" Margin="280,264,0,0" VerticalAlignment="Top" Width="123" Height="35" Click="Button_Click_1"/>
        <TextBox HorizontalAlignment="Left" Height="52" Margin="132,184,0,0" TextWrapping="Wrap" Text="{Binding Text,ElementName=pw,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="230"/>

    </Grid>
</Window>
时间: 2024-08-22 12:10:15

wpf控件之间互相绑定的相关文章

WINFORM中加入WPF控件并绑定数据源实现跨线程自动更新

1. WINFORM中添加两个ElementHost,一个放WPF的Button,一个放WPF的TextBox.其中TextBox与数据源绑定,实现跨线程也可以自动更新,而不会出现WINFORM的TextBox控件与数据源绑定后,存在子线程中更新数据源报错(跨线程更新控件)的情况. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System

WPF 控件库——可拖动选项卡的TabControl

原文:WPF 控件库--可拖动选项卡的TabControl 一.先看看效果 二.原理 1.选项卡大小和位置 这次给大家介绍的控件是比较常用的TabControl,网上常见的TabControl样式有很多,其中一部分也支持拖动选项卡,但是带动画效果的很少见.这也是有原因的,因为想要做一个不失原有功能,还需要添加动画效果的控件可不是一行代码的事.要做成上图中的效果,我们不能一蹴而就,最忌讳的是一上来就想实现所有效果. 一开始,我们最好先用Blend看看原生的TabControl样式模板部分是如何实现

跟我一起学WPF(3):WPF控件基础

WPF控件简介 通过上一篇XAML语言的介绍,我们知道,XAML是一个树形结构,同样,WPF控件作为构成整个XAML树的一部分,也是一个树形结构.我们看一个简单的例子. <Button.Content> <DockPanel> <Image Source="F:\01.Code\01.MyCode\WpfApplication1\WpfApplication1\Resources\荷.jpg" Width="30"/> <T

DevExpress v15.1:WPF控件升级(四)

<下载最新版DevExpress WPF Controls v15.1.5> WPF编辑器 在Token编辑器中的Per-Pixel滚动 水平和垂直的per-pixel滚动 渐进式搜索 查找和组合框编辑器:在只读模式中渐进式搜索. WPF Tab Control Web浏览器的分页用户界面 在新的版本中,我们的WPF Tab控件可用于创建具有分页用户界面的应用程序--如同那些在当代Web浏览器中的应用程序一样.新功能包含了一个新的DXTabbedWindow组件,支持最终用户拖拽.自定义用户界

WPF控件NumericUpDown (转)

WPF控件NumericUpDown示例 (转载请注明出处) 工具:Expression Blend 2 + Visual Studio 2008 语言:C# 框架:.Net Framework 3.5 工程:WPF Windows Application 布局 设计预览 XAML代码: <UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http

Sparrow.Chart.Wpf控件的动态调用

最近需要在Wpf程序中显示曲线,感觉Sparrow.Chart.Wpf控件不错(http://sparrowtoolkit.codeplex.com/),完全开源的一个控件支持,可以通过nuget下载.源代码中的例子完全采用MVVM方式实现,我项目中需求是完全通过代码创建控件,通过代码绑定数据,因此经过简单,实现了一个基本的例子: 首先顶一个CPU类(来自sparrow源代码): public class CPU { private DateTime time; public DateTime

wpf控件设计时支持(3)

原文:wpf控件设计时支持(3) wpf设计时调试 编辑模型 装饰器 1.wpf设计时调试 为了更好的了解wpf设计时框架,那么调试则非常重要,通过以下配置可以调试控件的设计时代码 (1)将启动项目配置成外部的visual studio ide启动程序devenv.exe (2)F5启动调试然后会打开一个新的visual studio ide,这个时候要记得重新打开你要调试的那个项目. 以上两个步骤就可以实现设计时调试了 2.编辑模型体系 当选中某些控件出现的设计时,这个选中的控件便成了可编辑的

WPF 控件与布局

[控件到底是什么?] 程序的本质就是"数据+算法"------用户输入原始的数据,算法处理原始数据并得到结果数据.问题就在于程序如何将结果数据显示给用户.同样一组数据,你可以使用LED阵列显示出来,或者以命令行模式借助各种格式控制符(如TAB)对齐并输出,但这些都不如图形界面(GUI  Graphic User Interface)来的友好和方便.GUI的方便在于它对数据展示的直观性,程序员可以通过编程的方式将数据通过图形的方式展示出来,从而避免了用户面对一大堆复杂数据的痛苦.提高了工

WPF 控件之 Popup

1.常用属性说明 IsOpen: 布尔值,指示 Popup 控件是否显示 StaysOpen: 布尔值,指示在 Popup 控件失去焦点的时候,是否关闭 Popup 控件的显示 PopupAnimation:指示显示窗口时是否使用动画,只有在 AllowsTransparency 等于true时此属性才有用 Popup 窗口本身是一个不可见的元素,只有在窗口上放置了信息后才能显示 Popup的定位方式与一般控件的定位方法不一样, Popup 使用五个属性来设定位置信息: PlacementTar