画一个椭圆
<Window x:Class="WpfApp3.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp3" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Ellipse Name="rectangle" Width="90" Height="90"> <Ellipse.Fill> <RadialGradientBrush GradientOrigin="0.25,0.25" RadiusX="0.75" RadiusY="0.75"> <RadialGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Black" Offset="0.65"/> <GradientStop Color="Gray" Offset="0.8"/> </RadialGradientBrush.GradientStops> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> </Grid> </Window>
GradientOrigin:定义渐变开始的二维焦点的位置。
RadiusX:径向渐变的最外面圆的水平半径。
RadiusY:径向渐变的最外面圆的垂直半径。
GradientStop:渐变中转换点的位置和颜色。
------------------------------------------------------------------------------------------
标记扩展
<Slider Name="slider" HorizontalAlignment="Left" Margin="92,203,0,0" VerticalAlignment="Top" Width="205"/> <TextBlock Text="{Binding ElementName=slider,Path=Value ,Mode=OneWay}" Margin="96,259,244,132"></TextBlock>
------------------------------------------------------------------------------------------
4.x:名称空间
x:key
为了在XAML中使用string类,需要
xmlns:sys="clr-namespace:System;assembly=mscorlib"
在XAML调用时,
<TextBox Text="{StaticResource ResourceKey=my}" Margin="255,314,0,0" VerticalAlignment="Top" Width="120"/>
在c#中调用
string str = this.FindResource("my") as string; this.textbox1.Text = str;
------------------------------------------------------------------------------------------
x:type 案例代码一直提示未找到MyButton
打开工程属性------生成------目标平台:如果原来是Any CPU,则切换为x86或x64,保存!然后清理解决方案!重新生成解决方案!
------------------------------------------------------------------------------------------
x:null
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}"> <Setter Property="Width" Value="60"/> <Setter Property="Height" Value="36"/> <Setter Property="Margin" Value="5"/> </Style> </Window.Resources> <StackPanel Background="LightSlateGray"> <Button Content="ok"/> <Button Content="ok"/> <Button Content="ok"/> <Button Content="ok" Style="{x:Null}"/> </StackPanel> </Window>
x:array
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:sys="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <StackPanel Background="LightSlateGray"> <ListBox> <ListBox.ItemsSource> <x:Array Type="sys:String"> <sys:String>tom</sys:String> <sys:String>jack</sys:String> <sys:String>tony</sys:String> </x:Array> </ListBox.ItemsSource> </ListBox> </StackPanel> </Window>
x:static
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="{x:Static local:MainWindow.WindowsTitle}" Height="450" Width="800"> <StackPanel Background="LightSlateGray"> <TextBox Text="{x:Static local:MainWindow.ShowText}"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public static string WindowsTitle = "啊哈哈哈哈"; public static string ShowText { get { return "abandon"; } } public MainWindow() { InitializeComponent(); } } }
5.控件与布局
6.深入浅出话binding
让binding源的对象具有自动通知binding 自己属性值已变化
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel> <TextBlock x:Name="textblockname"></TextBlock> <Button Height="50" Click="Button_Click"></Button> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { Student stu = new Student(); public MainWindow() { InitializeComponent(); Binding userbinding = new Binding(); userbinding.Source = stu; userbinding.Path = new PropertyPath("Name"); //使用binding绑定数据源和binding目标 BindingOperations.SetBinding(this.textblockname, TextBlock.TextProperty, userbinding);//绑定的绑定目标 绑定的目标属性 描述绑定的BindingBase对象 } class Student : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string name; public string Name { get { return name; } set { name = value; if (this.PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name")); } } } } private void Button_Click(object sender, RoutedEventArgs e) { stu.Name += "1"; } } }
显式的为数据设置DataTemplate
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel> <TextBox x:Name="textBox1" Text="{Binding Path=Id}" BorderBrush="Aqua" Margin="5"></TextBox> <ListBox x:Name="listbox1" Height="100" Margin="5"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Path=Id}" Background="Pink" Width="30"/> <TextBlock Text="{Binding Path=Name}" Background="Blue" Width="50"/> <TextBlock Text="{Binding Path=Age}" Background="Red" Width="80"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); List<Studetn1> stulist = new List<Studetn1>() { new Studetn1(){ Id=0,Name="tim",Age=29 }, new Studetn1(){ Id=2,Name="tom",Age=22 }, new Studetn1(){ Id=3,Name="vina",Age=230 }, new Studetn1(){ Id=4,Name="mike",Age=20 }, }; this.listbox1.ItemsSource = stulist; //this.listbox1.DisplayMemberPath = "Name"; this.textBox1.SetBinding(TextBox.TextProperty, new Binding("SelectedItem.Id") { Source = this.listbox1 }); } public class Studetn1 { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } } }
在使用集合类型作为列表控件的ItemsSource时一般会考虑使用ObservableCollection<T>代替List<T>,因为ObservableCollection<T>类实现了INotifyCollectionChange和INotifyPropertyChange接口,能把集合的变化立刻通知他的列表控件。
6.3.8 使用ADO.NET对象作为Binding的源 后面看实例
在
6.3.9 使用XML数据作为Binding的源
.NET Framework提供了两套处理XML数据的类库
符合DOM(Document Object Model 文档对象模型)
以LINQ(Language-Intergrated Query 语言集成查询)
注意:当使用XML数据作为Binding的Source时我们将使用XPath 属性而不是Path属性来指定对象。
基于DOM标准的XML类库使用
如何建立XML文件
XML文件内容如下
<?xml version="1.0" encoding="utf-8"?> <StudentList> <Student Id="1"> <Name>Time1</Name> </Student> <Student Id="2"> <Name>Time2</Name> </Student> <Student Id="3"> <Name>Time3</Name> </Student> <Student Id="4"> <Name>Time4</Name> </Student> </StudentList>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click_2(object sender, RoutedEventArgs e) { XmlDocument doc = new XmlDocument(); doc.Load(@"D:\suwen\Desktop\XMLFile1.xml"); XmlDataProvider xdp = new XmlDataProvider(); xdp.Document = doc; //使用Xpath选择需要暴露的数据 //现在时需要暴露一组Student xdp.XPath = @"StudentList/Student"; this.listViewStudents.DataContext = xdp; this.listViewStudents.SetBinding(ListView.ItemsSourceProperty, new Binding()); } } }
使用LINQ检索结果作为Binding的源
在Button被单击的时候显示一个Students集合类型对象
从一个已经填充好的List<Student>对象中检索出所有名字以之母T开头的学生,代码如下
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel Background="AliceBlue"> <ListView x:Name="listViewStudents" Height="130" Margin="5"> <ListView.View> <GridView> <GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding Id}"/> <GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}"/> <GridViewColumn Header="Age" Width="120" DisplayMemberBinding="{Binding Age}"/> </GridView> </ListView.View> </ListView> <Button Content="load" Click="Button_Click_2"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } private void Button_Click_2(object sender, RoutedEventArgs e) { List<Student> stulist = new List<Student>() { new Student(){ Id=0,Name="Tim",Age=20}, new Student(){ Id=1,Name="T5im",Age=21}, }; this.listViewStudents.ItemsSource = from stu in stulist where stu.Name.StartsWith("T") select stu; } } }
6.3.11 使用ObjectDataProvider对象作为Binding的Source
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel Background="AliceBlue"> <Button Content="load" Click="Button_Click_2"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public class Calculator { public string Add(string arg1, string arg2) { double x = 0; double y = 0; double z = 0; if (double.TryParse(arg1, out x) && double.TryParse(arg2, out y)) { z = x + y; return z.ToString(); } return "input Error"; } } private void Button_Click_2(object sender, RoutedEventArgs e) { ObjectDataProvider odp = new ObjectDataProvider(); odp.ObjectInstance = new Calculator(); odp.MethodName = "Add"; odp.MethodParameters.Add("100"); odp.MethodParameters.Add("300"); MessageBox.Show(odp.Data.ToString()); } } }
6.3.11 使用Bingding的RelativeSource
不确定作为Source的对象叫什么名字,但知道它作为Binding目标的对象在UI布局上有相对关系。
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <Grid x:Name="grid1" HorizontalAlignment="Left" Height="178" Margin="65,63,0,0" VerticalAlignment="Top" Width="532"> <DockPanel x:Name="dockPanel1" HorizontalAlignment="Left" Height="140" LastChildFill="False" Margin="84,44,0,-6" VerticalAlignment="Top" Width="369"> <StackPanel x:Name="stackPanel1" Height="140" VerticalAlignment="Top" Width="254"> <Grid x:Name="grid2" Height="100"> <Grid x:Name="grid3" Height="100"> <TextBlock x:Name="textBlock1" HorizontalAlignment="Left" Margin="103,46,0,0" TextWrapping="Wrap" VerticalAlignment="Top"><Run Text="TextBlock"/></TextBlock> </Grid> </Grid> </StackPanel> </DockPanel> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); RelativeSource rs = new RelativeSource(RelativeSourceMode.FindAncestor); rs.AncestorLevel = 2;//以Binding目标控件为起点的层级偏移量 rs.AncestorType = typeof(Grid);//告诉Binding寻找哪个类型的对象那个作为自己的源,不是这个类型的对象会被跳过 Binding binding = new Binding("Name") { RelativeSource = rs }; this.textBlock1.SetBinding(TextBlock.TextProperty, binding); } } }
6.4 Binding对数据的转化与校验
6.4.1 binding的数据校验
Binding用于数据有效性校验的关卡是它的ValidationRules
textbox 显示slider的数据,如果数据校验失败显示错误
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <Grid x:Name="grid1" HorizontalAlignment="Left" Height="178" Margin="65,63,0,0" VerticalAlignment="Top" Width="532"> <TextBox x:Name="TextBox1" HorizontalAlignment="Left" Height="23" Margin="189,113,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/> <Slider x:Name="Slider1" Minimum="0" Maximum="110" HorizontalAlignment="Left" Margin="100,59,0,0" VerticalAlignment="Top" Width="327"/> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Binding binding = new Binding("Value") { Source = this.Slider1 }; binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; RangeValidationRule rvr = new RangeValidationRule(); rvr.ValidatesOnTargetUpdated = true; binding.ValidationRules.Add(rvr); binding.NotifyOnValidationError = true; this.TextBox1.SetBinding(TextBox.TextProperty, binding); this.TextBox1.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(this.ValidationError)); } //需要一个ValidationRule的派生类 public class RangeValidationRule : ValidationRule { //实现Validate方法 public override ValidationResult Validate(object value, 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, "Validation Failed"); } } //用于侦听校验错误事件的事件处理器如下: void ValidationError(object sender, RoutedEventArgs e) { if (Validation.GetErrors(this.TextBox1).Count > 0) { this.TextBox1.ToolTip = Validation.GetErrors(this.TextBox1)[0].ErrorContent.ToString(); } } } }
6.4.2 binding的数据转化
涉及,飞机的category单向转换为string(XAML编译器能把string对象解析为图片资源),state与bool?类型之间的双向转化
按下load在listbox显示飞机的类别,名称,状态,,按下save可以将信息保存在txt文件内容。
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <Window.Resources> <local:CategoryToSourceConventer x:Key="cts"/> <local:StateToNullableBollConventer x:Key="stnb"/> </Window.Resources> <StackPanel Background="LightBlue"> <ListBox x:Name="listBoxPlane" Height="160" Margin="5"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Width="20" Height="20" Source="{Binding Path=Category,Converter={StaticResource cts}}"/> <TextBlock Text="{Binding Path=Name}" Width="60" Margin="80,0"/> <CheckBox IsThreeState="True" IsChecked="{Binding Path=State,Converter={StaticResource stnb}}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button x:Name="buttonLoad" Content="Load" Height=" 25" Margin="5,0" Click="buttonLoad_Click"/> <Button x:Name="buttonSave" Content="Save" Height=" 25" Margin="5,5" Click="buttonSave_Click"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void buttonLoad_Click(object sender, RoutedEventArgs e) { List<Plane> palneList = new List<Plane>() { new Plane(){ Category=Category.Bomber,Name="B-1",State=State.Unknown}, new Plane(){ Category=Category.Bomber,Name="B-2",State=State.Unknown}, new Plane(){ Category=Category.Fighter,Name="F-22",State=State.Unknown}, new Plane(){ Category=Category.Fighter,Name="Su-47",State=State.Unknown}, new Plane(){ Category=Category.Bomber,Name="B-52",State=State.Unknown}, new Plane(){ Category=Category.Fighter,Name="J-10",State=State.Unknown}, }; this.listBoxPlane.ItemsSource = palneList; } private void buttonSave_Click(object sender, RoutedEventArgs e) { StringBuilder sb = new StringBuilder(); foreach (Plane p in listBoxPlane.Items) { sb.AppendLine(string.Format("Catedory={0} Name={1},State={2}", p.Category, p.Name, p.State)); } File.WriteAllText(@"D:\PlaneList.txt", sb.ToString()); } } //轰炸机的类别,轰炸机的状态,飞机 public enum Category { Bomber, Fighter } public enum State { Available, Locked, Unknown } public class Plane { public Category Category { get; set; } public string Name { get; set; } public State State { get; set; } } public class CategoryToSourceConventer : IValueConverter { //将Category转换为Uri public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Category c = (Category)value; switch (c) { case Category.Bomber: return @"\Icon\Bomber.png"; case Category.Fighter: return @"\Icon\Fighter.png"; default: return null; } } //不会被调用 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public class StateToNullableBollConventer : IValueConverter { //将State转换为boll? public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { State s = (State)value; switch (s) { case State.Locked: return false; case State.Available: return true; case State.Unknown: default: return null; } } //boll?转换为State public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { bool? nb = (bool?)value; switch (nb) { case true: return State.Available; case false: return State.Locked; case null: default: return State.Unknown; } } } }
6.5 MultiBinding(多路Binding)
UI需要显示的信息由不止一个数据源来决定,需要使用MultBinding
需求:有一个用于新用户注册的UI(包含4个TextBox和1个Button)还有如下一些限定
第一,第二TextBox输入用户名,要求内容一致
第三,第四TextBox输入用户E-Mail,要求内容一致。
当TextBox全部输入一致时,Button可用
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel Background="Lavender"> <TextBox x:Name="textBox1" Height="23" Margin="5"></TextBox> <TextBox x:Name="textBox2" Height="23" Margin="5,0"></TextBox> <TextBox x:Name="textBox3" Height="23" Margin="5"></TextBox> <TextBox x:Name="textBox4" Height="23" Margin="5,0"></TextBox> <Button x:Name="button1" Content="Submit" Width="80" Margin="5"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.SetMultiBinding(); } private void SetMultiBinding() { Binding b1 = new Binding("Text") { Source = this.textBox1 }; Binding b2 = new Binding("Text") { Source = this.textBox2 }; Binding b3 = new Binding("Text") { Source = this.textBox3 }; Binding b4 = new Binding("Text") { Source = this.textBox4 }; MultiBinding mb = new MultiBinding() { Mode = BindingMode.OneWay }; mb.Bindings.Add(b1); mb.Bindings.Add(b2); mb.Bindings.Add(b3); mb.Bindings.Add(b4); mb.Converter = new LogonMultiBindingConvert(); this.button1.SetBinding(Button.IsEnabledProperty, mb); } public class LogonMultiBindingConvert : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (!values.Cast<string>().Any(text=>string.IsNullOrEmpty(text)) && values[0].ToString() == values[1].ToString() && values[2].ToString() == values[3].ToString()) { return true; } return false; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } } }
7.深入浅出话属性
7.22 声明和使用依赖属性
两个TextBox,按下button后,第二个TextBox显示第一个TextBox的内容
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" Title="123" Height="450" Width="800"> <StackPanel Background="Lavender"> <TextBox x:Name="textBox1" Height="23" Margin="5"></TextBox> <TextBox x:Name="textBox2" Height="23" Margin="5,0"></TextBox> <Button x:Name="button1" Content="dependency" Width="80" Margin="5" Click="button1_Click"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public class Student : DependencyObject { public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student)); } private void button1_Click(object sender, RoutedEventArgs e) { Student stu = new Student(); stu.SetValue(Student.NameProperty, this.textBox1.Text); textBox2.Text = (string)stu.GetValue(Student.NameProperty); } } }
提示:在一个类中声明依赖属性时,并不需要手动声明、注册并使用CLR属性封装,只需要输入propdp,,Visual Studio 提示列表中会有一项高亮显示,此时连续按下两下Tab键,,一个标准的依赖属性就声明好了。
8.深入浅出话事件
8.3.3 自定义路由事件
在ListBox显示路由事件发生时间
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="Route Event" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800" local:TimeButton.ReportTime="ReportTimeHandler" > <Grid x:Name="grid_1" local:TimeButton.ReportTime="ReportTimeHandler"> <Grid x:Name="grid_2" local:TimeButton.ReportTime="ReportTimeHandler"> <Grid x:Name="grid_3" local:TimeButton.ReportTime="ReportTimeHandler"> <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="ReportTimeHandler" > <ListBox x:Name="listBox" local:TimeButton.ReportTime="ReportTimeHandler"/> <local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="报时" local:TimeButton.ReportTime="ReportTimeHandler"/> </StackPanel> </Grid> </Grid> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void ReportTimeHandler(object sender, ReporTimeEventArgs e) { FrameworkElement element = sender as FrameworkElement; string timeStr = e.ClickTime.ToLongTimeString(); string contnet = string.Format("{0}到达{1}", timeStr, element.Name); this.listBox.Items.Add(contnet); } } //用于承载时间消息的事件参数 class ReporTimeEventArgs : System.Windows.RoutedEventArgs { public ReporTimeEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { } public DateTime ClickTime { get; set; } } class TimeButton : Button { //声明和注册路由事件 public static readonly RoutedEvent ReporTimeEvent = EventManager.RegisterRoutedEvent ("ReportTime", RoutingStrategy.Bubble, typeof(EventHandler<ReporTimeEventArgs>), typeof(TimeButton)); //CLR事件包装器 public event RoutedEventHandler ReportTime { add { this.AddHandler(ReporTimeEvent, value); } remove { this.RemoveHandler(ReporTimeEvent, value); } } //激发路由事件,借用Click事件的激发方法 protected override void OnClick() { base.OnClick();//保证Button原有功能正常使用 ReporTimeEventArgs args = new ReporTimeEventArgs(ReporTimeEvent, this); args.ClickTime = DateTime.Now; this.RaiseEvent(args); } //ReportTimeEvent路由事件处理器 } }
8.3.3 RoutedEventArgs的Source与OriginalSource
VisualTree与LogicalTree区别就在于LogicalTree的叶子结点是构成用户界面的控件,而VisualTree要连控件中的细微结构也算上。
RoutedEventArgs两个属性,Source表示Logical上的消息源头,OriginalSource表示VisualTree上的源头
8.3.4 事件也附加-深入浅出附加事件
附加事件的宿主没有界面渲染功能,但是是可以使用附加事件与其他对象进行沟通。
设计一个名为Student的类,如果Student实例Name属性值发生的变化就激发一个路由事件,使用界面元素来捕捉这个事件。
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800"> <Grid x:Name="gridMain"> <Button x:Name="button1" Content="OK" Width="80" Height="80" Click="button1_Click"> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //为外层Grid添加路由事件侦听器 Student.AddNameChangedHandler( this.gridMain, new RoutedEventHandler(this.StudentNameChangedHandler)); } private void button1_Click(object sender, RoutedEventArgs e) { Student stu = new Student { ID = 10, Name = "Tim" }; stu.Name = "Tom"; //准备事件消息并发送路由事件 RoutedEventArgs arg = new RoutedEventArgs(Student.NameChangedEvent, stu); this.button1.RaiseEvent(arg); } //Grid捕捉到NameChangedEvent后的处理器 private void StudentNameChangedHandler(object sender, RoutedEventArgs e) { MessageBox.Show((e.OriginalSource as Student).ID.ToString()); } } public class Student { //声明定义路由事件 public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRoutedEvent ("NameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student)); //为界面元素添加路由事件侦听 public static void AddNameChangedHandler(DependencyObject d, RoutedEventHandler h) { UIElement e = d as UIElement; if (e != null) { e.AddHandler(Student.NameChangedEvent, h); } } //移除侦听 public static void RemoveNameChangedHandler(DependencyObject d, RoutedEventHandler h) { UIElement e = d as UIElement; if (e != null) { e.RemoveHandler(Student.NameChangedEvent, h); } } public int ID { get; set; } public string Name { get; set; } } }
9 深入浅出话命令
9.1 命令系统的基本元素与关系
9.1.1 命令系统的基本元素
WPF的命令系统由几个基本要素构成
命令(Comamand)实际上就是实现了ICommad接口的类
命令源(CommandSource)即命令的发送者,是实现了IcommandSource接口的类
命令目标(CommandTarget)命令将发送给谁,命令目标必须是实现了IInputElement接口的类
命令关联(CommandBinding)负责把一些外围逻辑与命令关联起来
9.1.2 基本元素之间的关系
基本元素之间的关系体现在使用命令的过程中。命令使用大概分为如下几步:
(1)创建命令类:及获得一个实现Icommand接口的类,
(2)声明命令实例
(3)指定命令的源
(4)指定命令目标
(5)设置命令关联
9.1.3 小试命令
需求:定义一个命令,使用Button来发送这个命令,当命令送达TextBox时,TextBox会被清空(如果TextBox中没有文字则命令不可以被发送)
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800"> <StackPanel x:Name="stackPanel"> <Button x:Name="button1" Content="send Command" Margin="5" /> <TextBox x:Name="textBoxA" Margin="5" Height="100" /> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitializeCommand(); } //声明并定义命令 private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(MainWindow)); private void InitializeCommand() { //把命令赋值给命令源(发送者)并指定快捷键 this.button1.Command = this.clearCmd; this.clearCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt)); //指定命令目标 this.button1.CommandTarget = this.textBoxA; //创建命令关联 CommandBinding cb = new CommandBinding(); cb.Command = this.clearCmd; //只关注与clearCmd相关的事件 cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute); cb.Executed += new ExecutedRoutedEventHandler(cb_Executed); //把命令关联安置在外围控件上 this.stackPanel.CommandBindings.Add(cb); } //当探测命令是否可以执行是,此方法被调用 void cb_CanExecute(object sender, CanExecuteRoutedEventArgs e) { if (string.IsNullOrEmpty(this.textBoxA.Text)) { e.CanExecute = false; } else { e.Handled = true; } //避免继续向上传而降低程序性能 e.Handled = true; } //当命令到达后,命令被调用 void cb_Executed(object sender, ExecutedRoutedEventArgs e) { this.textBoxA.Clear(); //避免继续向上传而降低程序性能 e.Handled = true; } } }
因为CanExecute事件的激发频率比较高,为避免降低性能处理完后,建议把e.Handled设为true
CommandBinding一定哟啊设置在命令目标的外围控件上,不然呢无法捕捉到CanExecute和Executed等路由事件。
9.1.4 WPF的命令库
WPF准备了一些便捷的命令库
ApplicationCommands
ComponentCommands
NavigationCommands
MediaCommands
EditingCommans
9.1.5 命令参数
运行程序,当TextBox中没有内容时,两个按钮均不可用;当输入文字后,按钮变可用,单击按钮,LIstBox会加入不同条目
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800"> <Grid Margin="6"> <Grid.RowDefinitions> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="24"/> <RowDefinition Height="4"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--命令和命令参数 --> <TextBlock Text="Name:" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Row="0"/> <TextBox x:Name="nameTextBox" Margin="60,0,0,0" Grid.Row="0"/> <Button Content="New Teacher" Command="New" CommandParameter="Teacher" Grid.Row="2"/> <Button Content="New Teacher" Command="New" CommandParameter="Teacher" Grid.Row="2"/> <Button Content="New Student" Command="New" CommandParameter="Student" Grid.Row="4"/> <ListBox x:Name="listBoxNewItems" Grid.Row="6"/> </Grid> <!--为窗体添加CommandBinding--> <Window.CommandBindings> <CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/> </Window.CommandBindings> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e) { if (string.IsNullOrEmpty(this.nameTextBox.Text)) { e.CanExecute = false; } else { e.CanExecute = true; } } private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e) { string name = this.nameTextBox.Text; if (e.Parameter.ToString()=="Teacher") { this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},学而不厌,诲人不倦。", name)); } if (e.Parameter.ToString() == "Student") { this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},好好学习,天天向上。", name)); } } } }
9.1.6 命令与Binding的结合
9.2 近观命令
9.2.1 ICommand接口与RoutedCommand
9.2.2 自定义Command
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800"> <StackPanel> <local:MyCommandSource x:Name="ctrlClear" Margin="10"> <TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LightBlue" Width="80" /> </local:MyCommandSource> <local:MiniView x:Name="miniView"/> </StackPanel> </Window>
<UserControl x:Class="WpfApp4.MiniView" 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" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Border CornerRadius="5" BorderBrush="LawnGreen" BorderThickness="5"> <StackPanel> <TextBox x:Name="textBox1" Margin="5" /> <TextBox x:Name="textBox2" Margin="5,0" /> <TextBox x:Name="textBox3" Margin="5" /> <TextBox x:Name="textBox4" Margin="5,0" /> </StackPanel> </Border> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //声明命令并使用命令源和目标与之关联 ClearCommand clearCmd = new ClearCommand(); this.ctrlClear.Command = clearCmd; this.ctrlClear.CommandTarget = this.miniView; } } public interface IView { //属性 bool isChanged { get; set; } //方法 void SetBinding(); void Refresh(); void Clear(); void Save(); } //自定义命令 public class ClearCommand : ICommand { //当命令可执行状态发生变化时,应被激发 public event EventHandler CanExecuteChanged; //用于判断命令是否可以执行(暂不实现) public bool CanExecute(object parament) { throw new NotImplementedException(); } //命令执行带有业务相关的Clear逻辑 public void Execute(object parameter) { IView view = parameter as IView; if (view!=null) { view.Clear(); } } } //自定义命令源 public class MyCommandSource : UserControl, ICommandSource { //继承自ICommandSource的三个属性 public ICommand Command { get; set; } public object CommandParameter { get; set; } public IInputElement CommandTarget { get; set; } //在组件被单击时连带执行命令 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); //在命令目标上执行命令,或称让命令作用于命令目标 if (this.CommandTarget!=null) { this.Command.Execute(this.CommandTarget); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp4 { /// <summary> /// MiniView.xaml 的交互逻辑 /// </summary> public partial class MiniView : UserControl,IView { public MiniView() { InitializeComponent(); } //继承自IView的成员们 public bool isChanged { get ; set; } public void SetBinding() { } public void Refresh() { } public void Save() { } //用于清除内容的业务逻辑 public void Clear() { this.textBox1.Clear(); this.textBox2.Clear(); this.textBox3.Clear(); this.textBox4.Clear(); } } }
10 深入浅出话资源
10.2 且”静“且“动”的资源
DynamicResource:动态资源使用
StaticResource:静态资源使用
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="450" Width="800"> <Window.Resources> <TextBlock x:Key="res1" Text="海上生明月"/> <TextBlock x:Key="res2" Text="海上生明月"/> </Window.Resources> <StackPanel> <Button Margin="5,5,5,0" Content="{StaticResource res1}"/> <Button Margin="5,5,5,0" Content="{DynamicResource res2}"/> <Button Margin="5,5,5,0" Content="Update" Click="Button_Click"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //声明命令并使用命令源和目标与之关联 } private void Button_Click(object sender, RoutedEventArgs e) { this.Resources["res1"] = new TextBlock() { Text = "天涯共此时" }; this.Resources["res2"] = new TextBlock() { Text = "天涯共此时" }; } } }
10.3 向程序添加二进制资源
注意:如果想让外部文件编译进目标成为二进制资源,必须在属性窗口把文件的Build Action设置为Resource 一般情况下如果Build Action设置为Resource 则Copy to Output Directory设为Do not copy
不希望以资源形式使用外部文件,可以啊Build Action 设为None 把Copy to Output Directory 设为Copy always
10.4 使用Pack URi路径访问二进制资源
pack://application,,,[/程序集名称;][可选版本;][文件夹名称/]文件名称 [文件夹名称/]文件名称
11 深入浅出话模板
11.2 数据的外衣DataTemplate
需求:有一列汽车数据,这列数据显示在ListBox的条目显示汽车的厂商图标和简要参数,单击某个条目后在窗体的详细内容区域显示汽车的照片和详细参数
模式1:事件驱动
<UserControl x:Class="WpfApp4.UserControl2" 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" xmlns:local="clr-namespace:WpfApp4" > <Border BorderBrush="black" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image x:Name="imagePhoto" Width="400" Height="250"/> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="Name:" FontWeight="Bold" FontSize="20"/> <TextBlock x:Name="textBlockName" FontSize="20" Margin="5,0"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="Automaker:" FontWeight="Bold" FontSize="20"/> <TextBlock x:Name="textBlockAutomaker" FontSize="20" Margin="5,0"/> <TextBlock Text="Year:" FontWeight="Bold" FontSize="20"/> <TextBlock x:Name="textBlockYear" FontSize="20" Margin="5,0"/> <TextBlock Text="Top Speed:" FontWeight="Bold" FontSize="20"/> <TextBlock x:Name="textBlockTopSpeed" FontSize="20" Margin="5,0"/> </StackPanel> </StackPanel> </Border> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using static WpfApp4.MainWindow; namespace WpfApp4 { /// <summary> /// UserControl2.xaml 的交互逻辑 /// </summary> public partial class UserControl2 : UserControl { public UserControl2() { InitializeComponent(); } private Car car; public Car Car { get { return car; } set { car = value; this.textBlockName.Text = car.Name; this.textBlockYear.Text = car.Year; this.textBlockTopSpeed.Text = car.TopSpeed; this.textBlockAutomaker.Text = car.Automaker; string uriStr = string.Format(@"/Images/{0}.png", car.Name); this.imagePhoto.Source = new BitmapImage(new Uri(uriStr, UriKind.Relative)); } } } }
<UserControl x:Class="WpfApp4.UserControl1" 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" xmlns:local="clr-namespace:WpfApp4" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image x:Name="imageLogo" Grid.RowSpan="3" Width="64" Height="64"/> <StackPanel Margin="5,10"> <TextBlock x:Name="textBlockName" FontSize="16" FontWeight="Bold"/> <TextBlock x:Name="textBlockYear" FontSize="14"/> </StackPanel> </StackPanel> </Grid> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using static WpfApp4.MainWindow; namespace WpfApp4 { /// <summary> /// UserControl2.xaml 的交互逻辑 /// </summary> public partial class UserControl2 : UserControl { public UserControl2() { InitializeComponent(); } private Car car; public Car Car { get { return car; } set { car = value; this.textBlockName.Text = car.Name; this.textBlockYear.Text = car.Year; this.textBlockTopSpeed.Text = car.TopSpeed; this.textBlockAutomaker.Text = car.Automaker; string uriStr = string.Format(@"/Images/{0}.png", car.Name); this.imagePhoto.Source = new BitmapImage(new Uri(uriStr, UriKind.Relative)); } } } }
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:prop="clr-namespace:WpfApp4.Properties" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <StackPanel Orientation="Horizontal" Margin="5"> <local:UserControl2 x:Name="detailView"/> <ListBox x:Name="listBoxCars" Width="180" Margin="5,0" SelectionChanged="listBoxCars_SelectionChanged"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitialCarList(); } //选择变化事件的处理器 private void listBoxCars_SelectionChanged(object sender, SelectionChangedEventArgs e) { UserControl1 view = e.AddedItems[0] as UserControl1; if (view!=null) { this.detailView.Car = view.Car; } } public class Car { public string Automaker { get; set; } public string Name { get; set; } public string Year { get; set; } public string TopSpeed { get; set; } } //初始化listBox private void InitialCarList() { List<Car> carList = new List<Car>() { new Car(){ Automaker="Lamborghini",Name="Diablo",Year="1990",TopSpeed="340"}, new Car(){ Automaker="Lamborghini",Name="Murcielago",Year="2001",TopSpeed="325"}, }; foreach (Car car in carList) { UserControl1 view = new UserControl1(); view.Car = car; this.listBoxCars.Items.Add(view); } } } }
模式2:数据驱动
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Media.Imaging; namespace WpfApp4 { //厂商名称转换为Logo图片路径 public class AutomakerToLogoPathConvert : IValueConverter { //正向转化 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string uriStr = string.Format(@"/Logos/{0}.png", (string)value); return new BitmapImage(new Uri(uriStr, UriKind.Relative)); } //未被用到 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } //汽车名称转换为照片路径 public class NmaeToLogoPathConvert : IValueConverter { //正向转化 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { string uriStr = string.Format(@"/Images/{0}.png", (string)value); return new BitmapImage(new Uri(uriStr, UriKind.Relative)); } //未被用到 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--converters--> <local:AutomakerToLogoPathConvert x:Key="a21"/> <local:NmaeToLogoPathConvert x:Key="n2p"/> <!--DataTemplate for Detail View--> <DataTemplate x:Key="carDetaiIViewTemplate"> <Border BorderBrush="Black" BorderThickness="1" CornerRadius="6"> <StackPanel Margin="5"> <Image Width="400" Height="250" Source="{Binding Name,Converter={StaticResource n2p}}"/> <StackPanel Orientation="Horizontal" Margin="5"> <TextBlock Text="Name" FontWeight="Bold" FontSize="20"/> <TextBlock Text="{Binding Name}" FontSize="20" Margin="5,0"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="5,0"> <TextBlock Text="Automaker" FontWeight="Bold" FontSize="20"/> <TextBlock Text="{Binding Automaker}" FontSize="20" Margin="5,0"/> <TextBlock Text="Year" FontWeight="Bold" FontSize="20"/> <TextBlock Text="{Binding Year}" FontSize="20" Margin="5,0"/> <TextBlock Text="Top Speed" FontWeight="Bold" FontSize="20"/> <TextBlock Text="{Binding TopSpeed}" FontSize="20" Margin="5,0"/> </StackPanel> </StackPanel> </Border> </DataTemplate> <!--DataTemplate for Item View--> <DataTemplate x:Key="carListItemViewTemplate"> <Grid Margin="2"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Automaker,Converter={StaticResource a21}}" Grid.RowSpan="3" Width="64" Height="64"/> <StackPanel Margin="5,10"> <TextBlock Text="{Binding Name}" FontSize="16" FontWeight="Bold"/> <TextBlock Text="{Binding Year}" FontSize="14"/> </StackPanel> </StackPanel> </Grid> </DataTemplate> </Window.Resources> <!--窗体内容--> <StackPanel Orientation="Horizontal" Margin="5"> <UserControl ContentTemplate="{StaticResource carDetaiIViewTemplate}" Content="{Binding SelectedItem,ElementName=listBoxCars}"/> <ListBox x:Name="listBoxCars" Width="180" Margin="5,0" ItemTemplate="{StaticResource carListItemViewTemplate}"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InitialCarList(); } public class Car { public string Automaker { get; set; } public string Name { get; set; } public string Year { get; set; } public string TopSpeed { get; set; } } //初始化listBox private void InitialCarList() { List<Car> carList = new List<Car>() { new Car(){ Automaker="Lamborghini",Name="Diablo",Year="1990",TopSpeed="340"}, new Car(){ Automaker="Lamborghini",Name="Murcielago",Year="2001",TopSpeed="325"}, }; //填充数据源 this.listBoxCars.ItemsSource = carList; } } }
11.3 控件的外衣ControlTemplate
11.3.2 ItemsConteol的PanelTemplate
11.4 DataTemplate与ControlTemplate的关系与应用
11.4.1 DataTemplate与ControlTemplate的关系
11.4.2 DataTemplate与ControlTemplate的应用
把ControlTemplate应用在所有目标上需要借助Style来实现,但Stayle不能标记x:Key例如如下代码
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <Style TargetType="{x:Type TextBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBox}"> <Border x:Name="Bd" SnapsToDevicePixels="True" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5"> <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="Margin" Value="5"/> <Setter Property="BorderBrush" Value="black"/> <Setter Property="Height" Value="25"/> </Style> </Window.Resources> <StackPanel> <TextBox/> <TextBox/> <TextBox Style="{x:Null}" Margin="5"/> </StackPanel> </Window>
把DataTemplate应用在某个数据类型伤感的方法市设置DataTemplate的DataType属性,并且DataTemplate作为资源时不能带有x:Key标记
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--DataTemplate--> <DataTemplate DataType="{x:Type local:Unit}" > <Grid> <StackPanel Orientation="Horizontal"> <Grid> <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding price}"/> <TextBlock Text="{Binding Year}"/> </Grid> <TextBlock Text="{Binding price}" Margin="5,0"/> </StackPanel> </Grid> </DataTemplate> <!--数据源--> <c:ArrayList x:Key="ds"> <local:Unit Year="2001" price="100"/> <local:Unit Year="2002" price="150"/> <local:Unit Year="2003" price="180"/> <local:Unit Year="2004" price="190"/> <local:Unit Year="2005" price="120"/> <local:Unit Year="2006" price="130"/> <local:Unit Year="2007" price="150"/> </c:ArrayList> </Window.Resources> <StackPanel> <ListBox ItemsSource="{StaticResource ds}"></ListBox> <ComboBox ItemsSource="{StaticResource ds}" Margin="5"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } public class Unit { public int price { get; set; } public string Year { get; set; } } }
当数据是以XML形式存储时,DataTemplate具有直接把XML数据结点当作目标对象的功能——XML数据中元素名可直接作为DataType,元素的子节点可以使用XPath来访问
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--DataTemplate--> <DataTemplate DataType="Unit" > <Grid> <StackPanel Orientation="Horizontal"> <Grid> <Rectangle Stroke="Yellow" Fill="Orange" Width="{Binding XPath=@Price}"/> <TextBlock Text="{Binding XPath=@Year}"/> </Grid> <TextBlock Text="{Binding XPath=@Price}" Margin="5,0"/> </StackPanel> </Grid> </DataTemplate> <!--数据源--> <XmlDataProvider x:Key="ds" XPath="Units/Unit"> <x:XData> <Units xmlns=""> <Unit Year="2000" Price="100"/> <Unit Year="2001" Price="120"/> <Unit Year="2002" Price="140"/> <Unit Year="2003" Price="160"/> <Unit Year="2004" Price="180"/> <Unit Year="2005" Price="200"/> </Units> </x:XData> </XmlDataProvider> </Window.Resources> <StackPanel> <ListBox ItemsSource="{Binding Source={StaticResource ds}}"></ListBox> <ComboBox ItemsSource="{Binding Source={StaticResource ds}}" Margin="5"/> </StackPanel> </Window>
使用TreeView显示多层级,不同类型数据
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--数据源--> <XmlDataProvider x:Key="ds" Source="Data.xml" XPath="Data/Grade"/> <!--年级模板--> <HierarchicalDataTemplate DataType="Grade" ItemsSource="{Binding XPath=Class}"> <TextBlock Text="{Binding XPath=@Name}"/> </HierarchicalDataTemplate> <!--班级模板--> <HierarchicalDataTemplate DataType="Class" ItemsSource="{Binding XPath=Group}"> <RadioButton Content="{Binding XPath=@Name}" GroupName="gn"/> </HierarchicalDataTemplate> <!--小组模板--> <HierarchicalDataTemplate DataType="Group" ItemsSource="{Binding XPath=Student}"> <CheckBox Content="{Binding XPath=@Name}"/> </HierarchicalDataTemplate> </Window.Resources> <Grid> <TreeView Margin="5" ItemsSource="{Binding Source={StaticResource ds}}"/> </Grid> </Window>
同一种数据类型的嵌套结构,只需要设计一个HierachicalDataTemplate就好了,它会自动迭代应用效果
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--数据源--> <XmlDataProvider x:Key="ds" Source="Data.xml" XPath="Data/Operation"/> <!--Operation--> <HierarchicalDataTemplate DataType="Operation" ItemsSource="{Binding XPath=Operation}"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding XPath=@Name}" Margin="10,0"/> <TextBlock Text="{Binding XPath=@Gesture}" Margin="10,0"/> </StackPanel> </HierarchicalDataTemplate> </Window.Resources> <Grid MenuItem.Click="Grid_Click"> <Menu ItemsSource="{Binding Source={StaticResource ds}}"/> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Grid_Click(object sender, RoutedEventArgs e) { MenuItem mi = e.OriginalSource as MenuItem; XmlElement xe = mi.Header as XmlElement; MessageBox.Show(xe.Attributes["Name"].Value); } } }
<?xml version="1.0" encoding="utf-8" ?> <Data xmlns=""> <Operation Name="文件"> <Operation Name="新建" Gesture="N"> <Operation Name="项目" Gesture="Control+P"/> <Operation Name="网站" Gesture="Control+W"/> <Operation Name="文档" Gesture="Control+D"/> </Operation> <Operation Name="保存" Gesture="Control+S"/> <Operation Name="打印" Gesture="Control+P"/> <Operation Name="退出" Gesture="Control+X"/> </Operation> <Operation Name="编辑" > <Operation Name="拷贝" Gesture="Control+C"/> <Operation Name="剪切" Gesture="Control+X"/> <Operation Name="粘贴" Gesture="Control+V"/> </Operation> </Data>
11.4.3 寻找失落的控件
寻找由ControlTempate生成的控件
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <ControlTemplate x:Key="cTmp"> <StackPanel Background="Orange" > <TextBox x:Name="textBox1" Margin="6"/> <TextBox x:Name="textBox2" Margin="6,0"/> <TextBox x:Name="textBox3" Margin="6"/> </StackPanel> </ControlTemplate> </Window.Resources> <StackPanel Background="Yellow"> <UserControl x:Name="uc" Template="{StaticResource cTmp}" Margin="5"/> <Button Content="Find by Name" Width="120" Height="30" Click="Button_Click"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { TextBox tb = this.uc.Template.FindName("textBox1", this.uc) as TextBox; tb.Text = "Hello WPF"; StackPanel sp = tb.Parent as StackPanel; (sp.Children[1] as TextBox).Text = "Hello ControlTemple"; (sp.Children[2] as TextBox).Text = "I can find you!"; } } }
寻找由DataTemplate生成的控件
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--数据对象--> <local:Student x:Key="stu" Id="1" Name="Timothy" Skill="WPF" HasJob="True"/> <!--DataTemplate--> <DataTemplate x:Key="stuDT"> <Border BorderBrush="Orange" BorderThickness="2" CornerRadius="5"> <StackPanel> <TextBlock Text="{Binding Id}" Margin="5"/> <TextBlock x:Name="textBlockName" Text="{Binding Name}" Margin="5"/> <TextBlock Text="{Binding Skill}" Margin="5"/> </StackPanel> </Border> </DataTemplate> </Window.Resources> <!--主体布局--> <StackPanel> <ContentPresenter x:Name="cp" Content="{StaticResource stu}" ContentTemplate="{StaticResource stuDT}" Margin="5"/> <Button Content="Find" Margin="5,0" Click="Button_Click"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { TextBlock tb = this.cp.ContentTemplate.FindName("textBlockName", this.cp) as TextBlock; MessageBox.Show(tb.Text); //Student stu = this.cp.Content as Student; //MessageBox.Show(stu.Name); } } public class Student { public int Id { get; set; } public string Name { get; set; } public string Skill { get; set; } public bool HasJob { get; set; } } }
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <!--数据集合--> <c:ArrayList x:Key="stuList"> <local:Student Id="1" Name="Timoty" Skill="WPF" HasJob="True"/> <local:Student Id="2" Name="Timoty1" Skill="WPF1" HasJob="False"/> <local:Student Id="3" Name="Timoty2" Skill="WPF2" HasJob="True"/> <local:Student Id="4" Name="Timoty3" Skill="WPF3" HasJob="False"/> <local:Student Id="5" Name="Timoty4" Skill="WPF4" HasJob="True"/> </c:ArrayList> <!--DataTemplate--> <DataTemplate x:Key="nameDT"> <TextBox x:Name="textBoxName" Text="{Binding Name}" GotFocus="textBoxName_GotFocus"/> </DataTemplate> <DataTemplate x:Key="skillDT"> <TextBox x:Name="textBoxSkill" Text="{Binding Skill}"/> </DataTemplate> <DataTemplate x:Key="hjDT"> <CheckBox x:Name="checkBoxJob" IsChecked="{Binding HasJob}"/> </DataTemplate> </Window.Resources> <!--主体布局--> <Grid Margin="5"> <ListView x:Name="listViewStudetn" ItemsSource="{StaticResource stuList}"> <ListView.View> <GridView> <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Id}"/> <GridViewColumn Header="姓名" CellTemplate="{StaticResource nameDT}"/> <GridViewColumn Header="技术" CellTemplate="{StaticResource skillDT}"/> <GridViewColumn Header="已工作" CellTemplate="{StaticResource hjDT}" /> </GridView> </ListView.View> </ListView> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void textBoxName_GotFocus(object sender, RoutedEventArgs e) { //访问业务逻辑数据 TextBox tb = e.OriginalSource as TextBox; //获取事件发起的源头 ContentPresenter cp = tb.TemplatedParent as ContentPresenter;//获取模板目标 Student stu = cp.Content as Student;//获取业务逻辑 this.listViewStudetn.SelectedItem = stu;//设置ListView的选中项 //访问界面元素 ListViewItem lvi = this.listViewStudetn.ItemContainerGenerator.ContainerFromItem(stu) as ListViewItem; CheckBox chb = this.FindVisualChild<CheckBox>(lvi); MessageBox.Show(chb.Name); } private ChildType FindVisualChild<ChildType>(DependencyObject obj) where ChildType : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is ChildType) { return child as ChildType; } else { ChildType childOfChiild = FindVisualChild<ChildType>(child); if (childOfChiild != null) { return childOfChiild; } } } return null; } } public class Student { public int Id { get; set; } public string Name { get; set; } public string Skill { get; set; } public bool HasJob { get; set; } } }
11.5 深入浅出话Style
11.5.1 styel中的setter
11.5.2 styel中的Trigger
1.基本的Trigger
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <Style TargetType="CheckBox" > <Style.Triggers> <Trigger Property="IsChecked" Value="True"> <Trigger.Setters > <Setter Property="FontSize" Value="20"/> <Setter Property="Foreground" Value="Orange"/> </Trigger.Setters> </Trigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <CheckBox Content="悄悄的我走了" Margin="5"/> <CheckBox Content="正如我悄悄的来" Margin="5"/> <CheckBox Content="我挥一挥衣袖" Margin="5"/> <CheckBox Content="不带走一片云彩" Margin="5"/> </StackPanel> </Window>
2.MuitiTrigger
多个条件满足才会触发
3.由数据触发的DataTrigger
长度小于6 边框显示为红色
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <local:L2BConvert x:Key="cvtr"/> <Style TargetType="TextBox"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=Text.Length, Converter={StaticResource cvtr}}" Value="false"> <Setter Property="BorderBrush" Value="Red"/> <Setter Property="BorderThickness" Value="1"/> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <TextBox Margin="5"/> <TextBox Margin="5"/> <TextBox Margin="5"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } public class L2BConvert : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { int textLengrh = (int)value; return textLengrh > 6 ? true : false; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
4.多数据条件触发的MultiDataTrigger
需求:用户界面上使用ListBox显示了一列Student数据,当数据同时满足Id为2、Name为Tom时,条目高亮。
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <Style TargetType="ListBoxItem"> <!--使用Style设置DataTemplate--> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Id}" Width="60"/> <TextBlock Text="{Binding Name}" Width="120"/> <TextBlock Text="{Binding Age}" Width="60"/> </StackPanel> </DataTemplate> </Setter.Value> </Setter> <!--MultiDataTrigger--> <Style.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Path=Id}" Value="2"/> <Condition Binding="{Binding Path=Name}" Value="Tom"/> </MultiDataTrigger.Conditions> <MultiDataTrigger.Setters> <Setter Property="Background" Value="Orange"/> </MultiDataTrigger.Setters> </MultiDataTrigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel> <ListBox x:Name="listBoxStudet" Margin="5"/> </StackPanel> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); //初始化listbox List<Student> stuList = new List<Student>() { new Student(){Id=1,Name="Tom2",Age=10}, new Student(){Id=2,Name="Tom",Age=11}, new Student(){Id=3,Name="Tom3",Age=11} }; this.listBoxStudet.ItemsSource = stuList; } } public class Student { public int Id { get; set; } public string Name { get; set; } public int Age{ get; set; } } }
5.由事件触发的EventTrigger
又事件触发触发后是执行一段动画
下面例子中创建了一个针对Button的Style,这个Syle包含两个EventTrigger,一个由MouseEvnet事件触发,另一个由MouseLeave事件触发.
鼠标进入,button变大,鼠标离开,button变小
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Window.Resources> <Style TargetType="Button"> <Style.Triggers> <!--鼠标进入--> <EventTrigger RoutedEvent="MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Width" /> <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Height" /> </Storyboard> </BeginStoryboard> </EventTrigger> <!--鼠标离开--> <EventTrigger RoutedEvent="MouseLeave"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Width" /> <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="Height" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Style.Triggers> </Style> </Window.Resources> <Canvas> <Button Width="40" Height="40" Content="OK"/> </Canvas> </Window>
12.4 动画
12.4.1 简单动画
1.简单线性动画
变化时间Duration:必须指定
变化终点To:如果没有指定,程序将采用上一次动画的终点或默认值。
变化幅度By:如果同时指定了变化终点,变化幅度将被忽略。
变化起点From:如果没有指定变化起点则以变化目标的当前值为起点。
需求:用户界面上有Button按钮,每次点击Button时,会改变Button的显示位置
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid> <Button Content="Move!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="60" Height="60" Click="Button_Click"> <Button.RenderTransform> <TranslateTransform x:Name="tt" X="0" Y="0"/> </Button.RenderTransform> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { DoubleAnimation daX = new DoubleAnimation(); DoubleAnimation daY = new DoubleAnimation(); //指定起点 //daX.From = 0D; //daY.From = 0D; //指定幅度 daX.By = 100D; daY.By = 100D; ////指定终点 //Random r = new Random(); //daX.To = r.NextDouble() * 300; //daY.To = r.NextDouble() * 300; //指定时长 Duration duration = new Duration(TimeSpan.FromMilliseconds(30)); daX.Duration = duration; daY.Duration = duration; //动画主体 是TranslateTransform变形非Button this.tt.BeginAnimation(TranslateTransform.XProperty, daX); this.tt.BeginAnimation(TranslateTransform.YProperty, daY); } } }
2. 高级动画控制
AccelerationRadion 加速速率,介于0.0和1.0之间,与Deceleration之和不大于1.0 模拟汽车启动
DecelerationRadion 减速速率,介于0.0和1.0之间,与Acceleration之和不大于1.0 模拟汽车刹车
SpeedRadion 动画实际播放与正常速度比值 快进播放、慢动作
AutoReverse 是否以相反动画方式从终止值返回起始值 倒退播放
RepeatBehavior 动画的重复行为,取0不播放,使用double类型值可控制循环次数,
取RepeatBehavior.Forever为永远循环 循环播放
BeginTime 正式开始播放前的等待时间 多个动画之间的协同
EasingFunction 缓冲式渐变 乒乓球跳效果
案例:乒乓球弹跳式的效果
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid> <Button Content="Move!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="60" Height="60" Click="Button_Click"> <Button.RenderTransform> <TranslateTransform x:Name="tt" X="0" Y="0"/> </Button.RenderTransform> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { DoubleAnimation daX = new DoubleAnimation(); DoubleAnimation daY = new DoubleAnimation(); //指定起点 BounceEase be = new BounceEase(); be.Bounces = 3;//弹跳3次 be.Bounciness = 3;//弹性程度,值越大反弹越低 daY.EasingFunction = be; //指定终点 daX.To = 300; daY.To = 300; //指定时长 Duration duration = new Duration(TimeSpan.FromMilliseconds(2000)); daX.Duration = duration; daY.Duration = duration; //动画主体 是TranslateTransform变形非Button this.tt.BeginAnimation(TranslateTransform.XProperty, daX); this.tt.BeginAnimation(TranslateTransform.YProperty, daY); } } }
3. 关键帧动画
关键帧动画允许程序员为一段动画设置几个”里程碑“ 动画执行到里程碑所在的关键的时间点时,被动画所控制的属性值也必须达到设定的值。
案例:button按照z字型动作
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid> <Button Content="Move!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="60" Height="60" Click="Button_Click"> <Button.RenderTransform> <TranslateTransform x:Name="tt" X="0" Y="0"/> </Button.RenderTransform> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { DoubleAnimationUsingKeyFrames daX = new DoubleAnimationUsingKeyFrames(); DoubleAnimationUsingKeyFrames daY = new DoubleAnimationUsingKeyFrames(); //设置动画总时长 daX.Duration = new Duration(TimeSpan.FromMilliseconds(900)); daY.Duration = new Duration(TimeSpan.FromMilliseconds(900)); //创建、添加关键帧 LinearDoubleKeyFrame x_kf_1 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame x_kf_2 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame x_kf_3 = new LinearDoubleKeyFrame(); x_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); x_kf_1.Value = 200; x_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600)); x_kf_2.Value = 0; x_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900)); x_kf_3.Value = 200; daX.KeyFrames.Add(x_kf_1); daX.KeyFrames.Add(x_kf_2); daX.KeyFrames.Add(x_kf_3); LinearDoubleKeyFrame y_kf_1 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame y_kf_2 = new LinearDoubleKeyFrame(); LinearDoubleKeyFrame y_kf_3 = new LinearDoubleKeyFrame(); y_kf_1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); y_kf_1.Value = 0; y_kf_2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(600)); y_kf_2.Value = 180; y_kf_3.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(900)); y_kf_3.Value = 180; daY.KeyFrames.Add(y_kf_1); daY.KeyFrames.Add(y_kf_2); daY.KeyFrames.Add(y_kf_3); //动画主体 是TranslateTransform变形非Button this.tt.BeginAnimation(TranslateTransform.XProperty, daX); this.tt.BeginAnimation(TranslateTransform.YProperty, daY); } } }
4. 特殊的关键帧
DoubleAnimationUsingFrames的KeyFrames属性的数据类型时DoubleKeyFrameCollection,此类集合可接收的元素类型为DoubleKeyFrame。DoubleKeyFrame是一个抽象类
DoubleKeyFrame的所有派生类如下
LinearDoubleKeyFrame:线性变化关键帧,目标属性值的变化是直线的、均匀的,即变化的速率不变。
DiscreteDoubleKeyFrame:不连续变化关键帧,目标属性值是跳跃性、跃迁性的。
SplineDoubleKeyFrame:样条函数式变化关键帧,目标属性值的变化速率式一条贝塞尔曲线。
EasingDoubleKeyFrame:缓冲样式变化关键帧,目标属性值以某种缓冲形式变化。
SplineDoubleKeyFrame案例
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid> <Button Content="Move!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="60" Height="60" Click="Button_Click"> <Button.RenderTransform> <TranslateTransform x:Name="tt" X="0" Y="0"/> </Button.RenderTransform> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { //创建动画 DoubleAnimationUsingKeyFrames dakX = new DoubleAnimationUsingKeyFrames(); dakX.Duration = new Duration(TimeSpan.FromMilliseconds(1000)); //创建、添加关键帧 SplineDoubleKeyFrame kf = new SplineDoubleKeyFrame(); kf.KeyTime = KeyTime.FromPercent(1); kf.Value = 400; KeySpline ks = new KeySpline(); ks.ControlPoint1 = new Point(0, 1); ks.ControlPoint2 = new Point(1, 0); kf.KeySpline = ks; dakX.KeyFrames.Add(kf); //执行动画 this.tt.BeginAnimation(TranslateTransform.XProperty, dakX); } } }
5.路径动画
让目标对象沿着给定的路径移动,使用DoubleAnimationUsingPath类,DoubleAnimationUsingPath类需要一个PathGeometry来指明移动路径,Pathgeometry的数据信息可以用AML的Path语法书写。
PathGeometry的重要属性式Source,Source数据类型的取值是PathAnimationSource枚举,
枚举取X时,意味着这个动画关注的是曲线上每一点横坐标的变化。
枚举取Y时,意味着这个动画关注的是曲线上每一点纵坐标的变化。
枚举取Angle时,意味着这个动画关注的是曲线上每一点切线方向的变化。
案例:让一个Button沿着一条贝塞尔曲线做波浪运动
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid x:Name="LayoutRoot"> <Grid.Resources> <!--移动路径--> <PathGeometry x:Key="movingPath" Figures="M 0,150 C300,-100 300,400 600,120"/> </Grid.Resources> <Button Content="Move" HorizontalAlignment="Left" VerticalAlignment="Top" Width="80" Height="80" Click="Button_Click"> <Button.RenderTransform> <TranslateTransform x:Name="tt" X="0" Y="0"/> </Button.RenderTransform> </Button> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { //从XAMl代码中获取移动路径数据 PathGeometry pg = this.LayoutRoot.FindResource("movingPath") as PathGeometry; Duration duration = new Duration(TimeSpan.FromMilliseconds(600)); //创建动画 DoubleAnimationUsingPath dapX = new DoubleAnimationUsingPath(); dapX.PathGeometry = pg; dapX.Source = PathAnimationSource.X; dapX.Duration = duration; DoubleAnimationUsingPath dapY = new DoubleAnimationUsingPath(); dapY.PathGeometry = pg; dapY.Source = PathAnimationSource.Y; dapY.Duration = duration; //自动返回、永远循环 dapX.AutoReverse = true; dapX.RepeatBehavior = RepeatBehavior.Forever; dapY.AutoReverse = true; dapY.RepeatBehavior = RepeatBehavior.Forever; //执行动画 this.tt.BeginAnimation(TranslateTransform.XProperty, dapX); this.tt.BeginAnimation(TranslateTransform.YProperty, dapY); } } }
12.4.2 场景
场景(StoryBoard)并行执行的一组动画
案例
<Window x:Class="WpfApp4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp4" Title="123t" xmlns:c="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="d" x:Name="window_1" Height="350" Width="623"> <Grid Margin="6"> <!--布局控制--> <Grid.RowDefinitions> <RowDefinition Height="38"/> <RowDefinition Height="38"/> <RowDefinition Height="38"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="60"/> </Grid.ColumnDefinitions> <!--跑道红--> <Border BorderBrush="Gray" BorderThickness="1" Grid.Row="0"> <Ellipse x:Name="ballR" Height="36" Width="36" Fill="Red" HorizontalAlignment="Left"> <Ellipse.RenderTransform> <TranslateTransform x:Name="ttR"/> </Ellipse.RenderTransform> </Ellipse> </Border> <!--跑道绿--> <Border BorderBrush="Gray" BorderThickness="1,0,1,1" Grid.Row="1"> <Ellipse x:Name="ballG" Height="36" Width="36" Fill="LawnGreen" HorizontalAlignment="Left"> <Ellipse.RenderTransform> <TranslateTransform x:Name="ttG"/> </Ellipse.RenderTransform> </Ellipse> </Border> <!--跑道蓝--> <Border BorderBrush="Gray" BorderThickness="1,0,1,1" Grid.Row="2"> <Ellipse x:Name="ballB" Height="36" Width="36" Fill="Blue" HorizontalAlignment="Left"> <Ellipse.RenderTransform> <TranslateTransform x:Name="ttB"/> </Ellipse.RenderTransform> </Ellipse> </Border> <!--按钮--> <Button Content="Go!" Grid.Column="1" Grid.RowSpan="3" Click="Button_Click"/> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel;//使用INotifyPropertyChanged using System.Data; using System.Xml; using System.Globalization; using System.IO; using System.Windows.Media.Animation; namespace WpfApp4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { Duration duration = new Duration(TimeSpan.FromMilliseconds(600)); //红球匀速移动 DoubleAnimation daRx = new DoubleAnimation(); daRx.Duration = duration; daRx.To = 400; //绿球变速运动 DoubleAnimationUsingKeyFrames dakGx = new DoubleAnimationUsingKeyFrames(); dakGx.Duration = duration; SplineDoubleKeyFrame kfG = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1.0)); kfG.KeySpline = new KeySpline(1, 0, 0, 1); dakGx.KeyFrames.Add(kfG); //蓝球变速运动 DoubleAnimationUsingKeyFrames dakBx = new DoubleAnimationUsingKeyFrames(); dakBx.Duration = duration; SplineDoubleKeyFrame kfB = new SplineDoubleKeyFrame(400, KeyTime.FromPercent(1.0)); kfB.KeySpline = new KeySpline(0, 1, 1, 0); dakBx.KeyFrames.Add(kfB); //创建场景 Storyboard storyboard = new Storyboard(); Storyboard.SetTargetName(daRx, "ttR"); Storyboard.SetTargetProperty(daRx, new PropertyPath(TranslateTransform.XProperty)); Storyboard.SetTargetName(dakGx, "ttG"); Storyboard.SetTargetProperty(dakGx, new PropertyPath(TranslateTransform.XProperty)); Storyboard.SetTargetName(dakBx, "ttB"); Storyboard.SetTargetProperty(dakBx, new PropertyPath(TranslateTransform.XProperty)); storyboard.Duration = duration; storyboard.Children.Add(daRx); storyboard.Children.Add(dakGx); storyboard.Children.Add(dakBx); storyboard.Begin(this); storyboard.Completed += (a,b) =>{ MessageBox.Show(ttG.X.ToString());}; } } }