这几天在打算将以前用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