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

如何建立一个“绑定友好的”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

原文地址:https://www.cnblogs.com/tilv37/p/4745693.html