转载

如何建立一个“绑定友好的”usercontrol--wpf

这几天在打算将以前用winform写的工具程序重构到wpf,顺便学习理解看过的wpf的知识。

因为程序设计到一个Excel文件的读取和操作,比较耗时,所以打算制作一个缓冲动画。该动画应该分为两个部分,一个部分是“一排一次排列的方块组成的渐变动画”,一部分是一个Label类的控件,用来显示后台操作Excel时候的进度。

先放上渐变动画的Xaml,在usercontrol里面加上以下代码就行了。这个不是今天记录的重点。

<Canvas  Margin="0,0,0,50">  <Rectangle x:Name="r1" Fill="Black" Opacity="1" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="20" Canvas.Top="16"/>  <Rectangle x:Name="r2" Fill="Black" Opacity="0.9" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="70" Canvas.Top="16"/>  <Rectangle x:Name="r3" Fill="Black" Opacity="0.8" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="120" Canvas.Top="16"/>  <Rectangle x:Name="r4" Fill="Black" Opacity="0.7" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="170" Canvas.Top="16"/>  <Rectangle x:Name="r5" Fill="Black" Opacity="0.6" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="220" Canvas.Top="16"/>  <Rectangle x:Name="r6" Fill="Black" Opacity="0.5" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="270" Canvas.Top="16"/>  <Rectangle x:Name="r7" Fill="Black" Opacity="0.4" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="320" Canvas.Top="16"/>  <Rectangle x:Name="r8" Fill="Black" Opacity="0.3" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="370" Canvas.Top="16"/>  <Rectangle x:Name="r9" Fill="Black" Opacity="0.2" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="420" Canvas.Top="16"/>  <Rectangle x:Name="r10" Fill="Black" Opacity="0.1" HorizontalAlignment="Left" Height="30" Stroke="Black" VerticalAlignment="Top" Width="30" Canvas.Left="470" Canvas.Top="16"/>  <Canvas.Triggers>   <EventTrigger RoutedEvent="Canvas.Loaded">    <BeginStoryboard >     <Storyboard RepeatBehavior="Forever">      <DoubleAnimation Storyboard.TargetName="r1" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0" To="0"/>      <DoubleAnimation Storyboard.TargetName="r2" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.08" To="0"/>      <DoubleAnimation Storyboard.TargetName="r3" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.16" To="0"/>      <DoubleAnimation Storyboard.TargetName="r4" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.24" To="0"/>      <DoubleAnimation Storyboard.TargetName="r5" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.32" To="0"/>      <DoubleAnimation Storyboard.TargetName="r6" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.40" To="0"/>      <DoubleAnimation Storyboard.TargetName="r7" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.48" To="0"/>      <DoubleAnimation Storyboard.TargetName="r8" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.56" To="0"/>      <DoubleAnimation Storyboard.TargetName="r9" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.64" To="0"/>      <DoubleAnimation Storyboard.TargetName="r10" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08" BeginTime="0:0:0.72" To="0"/>     </Storyboard>    </BeginStoryboard>   </EventTrigger>  </Canvas.Triggers> </Canvas> 

现在重点来说说label类控件显示进度描述的,因为打算在自定义控件中利用binding来进行实时的通知,所以这个usercontrol必须是一个绑定友好的控件。

网上也查找了不少,也问了问同事,著名的“老赵”(不敢妄加评论大神,勿怪)和同事都是为usercontrol配一个viewmodle来实现binding的友好,但是这种不是我想要的,总觉得给一个控件配一个viewmodle不太舒服。就想实现普通类似:

<TextBlock Text="{Binding Name}" Height="30"/>

如上代码的binding。

继续百度+看书,确认可以使用依赖属性来暴露出usercontrol的一个属性,利用这个属性来实现bind。

这里我重新制作了一个usercontrol,核心是两个TextBlock,对第二个TextBlock中的text部分设置进行一个属性暴露的操作。

代码如下xmal:

<UserControl x:Class="MetroTest.LoaclControl.DoubleTextBox"  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   mc:Ignorable="d"   d:DesignHeight="150" d:DesignWidth="300"> <Grid>     <StackPanel>         <Label Content="A"></Label>         <TextBox x:Name="txtBox1" Text="试一试" Height="30" Margin="5"/>         <Label Content="B"></Label>         <TextBox x:Name="txtBox2"  Height="30" Margin="5"/>     </StackPanel> </Grid> </UserControl> 

这个usercontrol对应的后台代码:

 /// <summary> /// DoubleTextBox.xaml 的交互逻辑 /// </summary> public partial class DoubleTextBox : UserControl {  public DoubleTextBox()  {   InitializeComponent();  }  public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("TempName", typeof(string),   typeof (DoubleTextBox),new PropertyMetadata("TextBlock",new PropertyChangedCallback(OnTextChanged)));  private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)  {   DoubleTextBox dtb = (DoubleTextBox) d;   dtb.txtBox2.Text = (string) e.NewValue;  }  public string TempName  {   get { return (string) GetValue(ValueProperty); }   set { SetValue(ValueProperty, value); }  } } 

这里使用DependencyProperty定义一个依赖属性ValueProperty,利用register操作进行注册,说一下各个参数的含义:

1:暴露出来的属性名称,这里为“TempName”,可以如下使用

 <localControl:DoubleTextBox Height="200" TempName="{Binding Name}"></localControl:DoubleTextBox>

2:暴露出的属性的类型,这里是字符串string

3:属性所有者的名字,看前面的定义,“Temp”是“DoubleTextBox”的一个公开属性,所以名字就是DoubleTextBox

4:用来处理属性改变时候的操作,回调函数参数1:就是依赖属性的默认值,随便给一个string类型都可以 2、具体处理方法

如上,一个bind友好的控件就制作完了,可以在此基础上继续改造升级。

放一个简单的使用demo(不包含渐变动画):

建立一个testWindow,在里面使用控件(命名中的拼写错误请忽略)

<Window x:Class="MetroTest.Test"  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  xmlns:localControl="clr-namespace:MetroTest.LoaclControl"  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  Title="Test" Height="300" Width="594"> <StackPanel>  <localControl:DoubleTextBox Height="200" TempName="{Binding Name}"></localControl:DoubleTextBox>  <Button  Command="{Binding OpenFileCommand}">Submit</Button>  <TextBlock Text="{Binding Name}" Height="30"/> </StackPanel> </Window> 

这其中,xmlns:localControl="clr-namespace:MetroTest.LoaclControl"是我自定义控件所放置的文件夹的路径。

界面组成:1、定义好的uercontrol 2、一个button 3、一个Textblock

分别用处:1、不用说了 2、导向一个vm中的command用来更新bind的内容 3、检查是不是bind更新成功的

写一个简单的VieModle(忽略command的命名,copy的其他工程的)

class TextViewModle:ViewModleBase {   private string _name;  public string Name  {   set   {    if (_name != value)    {     _name = value;     OnPropertyChanged("Name");    }   }   get { return _name; }  }  private int count = 1;  //定义一个命令  public ICommand OpenFileCommand { get { return new RelayCaommand(OpenFileExecute, CanOpenFileExecute); } }  private bool CanOpenFileExecute()  {   return true;  }  private void OpenFileExecute()  {   Name = count.ToString();   count++;  } } 

记得给TestWindow的datacontent进行绑定。(命名中的拼写错误请忽略)

 /// <summary> /// Test.xaml 的交互逻辑 /// </summary> public partial class Test : Window {  public Test()  {   InitializeComponent();   this.DataContext = new TextViewModle();  } } 

到此,一个简单的例子就完成了,水平浅陋,希望对大家有帮助。。

顺便推销个人博客: http://www.dingshuo89.top

正文到此结束
Loading...