当前位置: 首页 > news >正文

腾讯官方网站用户体验差有哪些网站

腾讯官方网站,用户体验差有哪些网站,免费网站的软件,七台河建设网站在过去#xff0c;Windows开发人员必须在方便性和灵活性之间做出选择。为得到最大的方便性#xff0c;他们可以使用预先构建好的控件。这些控件可以工作的足够好#xff0c;但可定制性十分有限#xff0c;并且几乎总是具有固定的可视化外观。偶尔#xff0c;某些控件提供了…在过去Windows开发人员必须在方便性和灵活性之间做出选择。为得到最大的方便性他们可以使用预先构建好的控件。这些控件可以工作的足够好但可定制性十分有限并且几乎总是具有固定的可视化外观。偶尔某些控件提供了不很直观的“自主绘图”模式允许开发人员通过响应回调来绘制控件的一部分。但基本控件——按钮、文本框、复选框和列表框等——被完全锁定了。因此希望实现一些特殊效果的开发人员不得不从头构建自定义控件。这确实是一个问题——手工编写绘图逻辑不但非常费时而且很困难但自定义控件开发人员还需要从头实现基本功能例如在文本框中选择文本以及在按钮中处理按键。并且即使自定义控件是完美的将它们插入到已有应用程序中也需要进行一些重要的修改通常需要修改代码并且还需要进行更多的测试。简单的说自定义控件是必须的内容——它们是实现新颖时髦的用户界面的唯一方法但支持它们并将它们集成到应用程序中也是一件棘手的事情。 WPF最终通过样式以及模板解决了控件的自定义问题。这些特性能够很好地工作的原因是在WPF中控件的实现方式发生了重大变化。在以前的用户界面技术如Windows窗体中常用的控件实际上不是由.NET代码实现的。相反Windows窗体控件封装了来自Win32 API的核心要素它们是不能改变的而WPF中的每个控件是由纯粹的.NET 代码构成的其背后没有使用任何Win32 API。因此WPF能够提供一种机制样式和模板运行您进入这些元素的内部并“扭曲”它们。实际上“扭曲”是一种错误的说法因为正如控件模板所做到的可采用所能想到的方式对WPF控件进行最彻底的重新设计。 逻辑树与可视化树 在一个窗口中添加的元素分类称为逻辑树WPF编程人员需要耗费大部分时间构建逻辑树然后使用事件处理代码支持它们。实际上WPF的特性如属性值继承、事件路由以及样式都是通过逻辑树进行工作的。 然而如果希望自定义元素逻辑树起不到多大帮助作用。显然可使用另一个元素替换整个元素例如可使用自定义的 FancyButton类 替换当前的 Button类但这需要做更多工作并且可能扰乱应用程序的用户界面代码。因此WPF通过可视化树进入更深层次。 可视化树是逻辑树的扩展版本。它将元素分成更小的部分。它并不查看被精心封装到一起的黑色方框如按钮而是查看按钮的可视化元素——使按钮具有阴影背景特性的边框Border、内部的容器ContentPresenter以及存储按钮文本的块TextBlock。所有这些细节本身都是元素——换句话说控件中的每个单独的细节都是由FrameworkElement 类的派生类表示的。 通过可视化树可以完成以下两项非常有用的工作 1、可使用样式改变可视化树中的元素。可使用 Style.TargetType 属性选择希望修改的特定元素。甚至当控件属性发生变化时可使用触发器自动完成更改。不过某些特定的细节很难甚至无法修改。 2、可为控件创建新模板。对于这种情况控件模板将被用于按期望的方式构建可视化树。 WPF提供了用于浏览逻辑树和可视化树的两个类System.Windows.LogicalTreeHelper 和 System.Windows.Media.VisualTreeHelper。 LogicalTreeHelper 类允许通过动态加载XAML文档在WPF应用程序中关联事件处理程序。LogicalTreeHelper 类提供了较少的方法尽管这些方法偶尔很有用但大多数情况下会改用特定的FrameworkElement 类中的方法。 FindLogicalNode() 根据名称查找特定元素从指定的元素开始并向下查找逻辑树 BringIntoView() 如果元素在可滚动的容器中并且当前不可见就将元素滚动到视图中。FrameworkElement.BringIntoView() 方法执行相同的工作。 GetParent() 获取指定元素的父元素。 GetChildren() 获取指定元素的子元素。不同的元素支持不同的内容模型。例如面板支持多个子元素而内容控件只支持一个子元素。然而GetChildren() 方法抽象了这一区别并且可以使用任何类型的元素进行工作。 除了专门用来执行低级绘图操作的一些方法外例如命中测试和边界检查的方法VisualTreeHelper 类提供的方法与LogicalTreeHelper类提供的方法类似也提供了 GetChildrenCount()、GetChild()以及GetParent()方法。 VisualTreeHelper类还提供了一种研究应用程序中可视化树的有趣方法。使用GetChild()方法可以遍历任意窗口的可视化树并且为了进行分析可以将它们显示出来。这是一个非常好的学习工具只需要使用一些递归的代码就可以实现。 VisualTreeDisplay.xaml Window x:ClassTestControlTemplate.VisualTreeDisplayxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:dhttp://schemas.microsoft.com/expression/blend/2008xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006xmlns:localclr-namespace:TestControlTemplatemc:IgnorabledTitleVisualTreeDisplay Height450 Width800TreeView NametreeElements Margin10/ /WindowVisualTreeDisplay.xaml.cs 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.Shapes;namespace TestControlTemplate;public partial class VisualTreeDisplay : Window {public VisualTreeDisplay(){InitializeComponent();}public void ShowVisualTree(DependencyObject element){// Clear the tree.treeElements.Items.Clear();// Start processing elements, begin at the root.ProcessElement(element, null);}private void ProcessElement(DependencyObject element, TreeViewItem previousItem){// Create a TreeViewItem for the current element.TreeViewItem item new TreeViewItem();item.Header element.GetType().Name;item.IsExpanded true;// Check whether this item should be added to the root of the tree//(if its the first item), or nested under another item.if (previousItem null){treeElements.Items.Add(item);}else{previousItem.Items.Add(item);}// Check if this element contains other elements.for (int i 0; i VisualTreeHelper.GetChildrenCount(element); i){// Process each contained element recursively.ProcessElement(VisualTreeHelper.GetChild(element, i), item);}} }理解模板 对于可视化树的分析引出了几个有趣的问题。例如控件如何从逻辑树扩展成可视化树表示 每个控件都有一个内置的方法用于确定如何渲染控件作为一组更基础的元素。该方法称为控件模板使用XAML标记块定义的。 每个WPF控件都设计成无外观的lookless这意味着完全可以重定义其可视化元素外观。但不能改变控件的行为控件的行为被固化到控件类中尽管经常可使用各种属性微调控件的行为。当选择使用类似Button的控件时是希望得到类似按钮的行为换句话说选择的是一个元素该元素提供了能被单击的内容通过单击来触发动作并且可用做窗口上的默认按钮或取消按钮。然而可自由的改变控件的外观以及当鼠标移动到元素上或按下鼠标时的响应方式。另外也可自由改变控件外观的其他方面和可视化行为。 下面是普通Button类的模板 Window.ResourcesSolidColorBrush x:KeyButton.MouseOver.Background Color#FFBEE6FD/SolidColorBrush x:KeyButton.MouseOver.Border Color#FF3C7FB1/SolidColorBrush x:KeyButton.Pressed.Background Color#FFC4E5F6/SolidColorBrush x:KeyButton.Pressed.Border Color#FF2C628B/SolidColorBrush x:KeyButton.Disabled.Background Color#FFF4F4F4/SolidColorBrush x:KeyButton.Disabled.Border Color#FFADB2B5/SolidColorBrush x:KeyButton.Disabled.Foreground Color#FF838383/ControlTemplate x:KeyButtonTemplate TargetType{x:Type Button}Border x:Nameborder Background{TemplateBinding Background} BorderBrush{TemplateBinding BorderBrush} BorderThickness{TemplateBinding BorderThickness} SnapsToDevicePixelstrueContentPresenter x:NamecontentPresenter FocusableFalse HorizontalAlignment{TemplateBinding HorizontalContentAlignment} Margin{TemplateBinding Padding}RecognizesAccessKeyTrue SnapsToDevicePixels{TemplateBinding SnapsToDevicePixels} VerticalAlignment{TemplateBinding VerticalContentAlignment}//BorderControlTemplate.TriggersTrigger PropertyIsDefaulted ValuetrueSetter PropertyBorderBrush TargetNameborder Value{DynamicResource {x:Static SystemColors.HighlightBrushKey}}//TriggerTrigger PropertyIsMouseOver ValuetrueSetter PropertyBackground TargetNameborder Value{StaticResource Button.MouseOver.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.MouseOver.Border}//TriggerTrigger PropertyIsPressed ValuetrueSetter PropertyBackground TargetNameborder Value{StaticResource Button.Pressed.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.Pressed.Border}//TriggerTrigger PropertyIsEnabled ValuefalseSetter PropertyBackground TargetNameborder Value{StaticResource Button.Disabled.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.Disabled.Border}/Setter PropertyTextElement.Foreground TargetNamecontentPresenter Value{StaticResource Button.Disabled.Foreground}//Trigger/ControlTemplate.Triggers/ControlTemplate/Window.Resources 除去触发器部分就剩下了Border标签其内就是在可视化树中看到的扩展内容。Border定义了按钮的标准可视化外观而ContentPresenter类 存储了提供的所有内容。如果希望构建全新按钮只需要创建新的控件模板。 当按钮获得焦点、被单击以及被禁用时触发器控制按钮如何进行变换。对于这些触发器实际上没什么特别需要介绍的内容。针对获取焦点和单击的触发器并不会修改按钮本身只是修改为按钮提供可视化外观的 Border 类的属性。 当构建自己的控件模板时将看到同样的职责分离。如果足够幸运可直接使用触发器完成所有工作可能不需要创建自定义类并添加代码。另一方面如果需要提供更复杂的可视化设计可能需要继承自定义的修饰类。 剖析控件 当创建控件模板是新建的控件模板完全代替了原来的模板。这样可以得到更大的灵活性但更复杂些。大多数情况下在创建满足自己需求的模板之前需要查看控件使用的标准模板。某些情况下自定义的控件模板可镜像标准模板并只进行很少的修改。 WPF文档没有列出标准控件模板的XAML。然而可通过编程获取所需的信息。基本思想是从Template属性该属性在Control类中定义获取控件的模板然后使用XamlWriter类将该模板串行化到XAML文件中。 ControlTemplateDisplay.xaml Window x:ClassTestControlTemplate.ControlTemplateDisplayxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:dhttp://schemas.microsoft.com/expression/blend/2008xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006xmlns:localclr-namespace:TestControlTemplatemc:IgnorabledTitleControlTemplateDisplay Height450 Width800 LoadedWindow_LoadedGrid NamegridGrid.ColumnDefinitionsColumnDefinition Width*/ColumnDefinition Width3*//Grid.ColumnDefinitionsListBox Grid.Column0 NameListTypes SelectionChangedListTypes_SelectionChanged DisplayMemberPathName/ListBoxTextBox Grid.Column1 NameTextTemplate TextWrappingWrap VerticalScrollBarVisibilityVisible FontFamilyConsolas/TextBox/Grid /Window ControlTemplateDisplay.xaml.cs using System; using System.Collections.Generic; using System.Linq; using System.Reflection; 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.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Xml;namespace TestControlTemplate;public class TypeComparer : IComparerType {public int Compare(Type x, Type y){return x.Name.CompareTo(y.Name);} }public partial class ControlTemplateDisplay : Window {public ControlTemplateDisplay(){InitializeComponent();}private void Window_Loaded(object sender, RoutedEventArgs e){Type controlType typeof(Control);ListType derivedTypes new ListType();Assembly? assembly Assembly.GetAssembly(typeof(Control));if (assembly ! null){foreach (Type type in assembly.GetTypes()){if (type.IsSubclassOf(controlType) !type.IsAbstract type.IsPublic){derivedTypes.Add(type);}}}derivedTypes.Sort(new TypeComparer());ListTypes.ItemsSource derivedTypes;}private void ListTypes_SelectionChanged(object sender, SelectionChangedEventArgs e){try{Type type (Type)ListTypes.SelectedItem;ConstructorInfo? info type.GetConstructor(System.Type.EmptyTypes);if (info null){return;}Control control (Control)info.Invoke(null);Window? win control as Window;if (win ! null){win.WindowState System.Windows.WindowState.Minimized;win.ShowInTaskbar false;win.Show();}else{control.Visibility Visibility.Collapsed;grid.Children.Add(control);}ControlTemplate template control.Template;XmlWriterSettings settings new XmlWriterSettings();settings.Indent true;StringBuilder sb new StringBuilder();XmlWriter writer XmlWriter.Create(sb, settings);XamlWriter.Save(template, writer);TextTemplate.Text sb.ToString();if (win ! null){win.Close();}else{grid.Children.Remove(control);}}catch (Exception err){TextTemplate.Text Error generating template: err.Message ;}} }构建该应用的诀窍是使用反射reflection反射是用于检查类型的 .NET API。当第一次加载应用程序的主窗口时扫描 PresentationFramework.dll核心程序集在该程序集中定义了控件类中的所有类型。然后将这些类型添加到一个集合中根据类型名称进行排序然后将该集合绑定到一个列表。 创建控件模板 当创建自定义控件时可以不用担心标准化和主题集成实际上WPF不像以前的用户界面技术那样强调用户界面标准化。反而更需要关注如何创建富有吸引力的新颖控件并将它们混合到用户界面的其他部分。因此可能不需要创建诸如ButtonChrome的类而可使用已有的元素设计自给自足的不使用代码的控件模板。 简单按钮 为应用自定义控件模板只需要设置控件的Template属性。尽管可定义内联模板通过在控件标签内部嵌入控件模板标签但这种方法基本没有意义。这是因为几乎总是希望为同一控件的多个皮肤实例重用模板。为适应这种设计需要将控件模板定义为资源并使用StaticResource引用该资源。 Button Template{StaticResource ButtonTemplate}ButtonTemplate/Button 通过这种方法不仅可以较容易地创建许多自定义按钮在以后还可以很灵活地修改控件模板而不会扰乱应用程序用户界面的其余部分。 要为基本按钮创建模板需要绘制边框和背景然后在按钮中放置内容。绘制边框的两种可选方法是使用Rectangle 和 Border 类。这里使用Border类将具有圆角的桔色轮廓与红色背景和白色文本结合在一起。此外还应该包含一个 ContentPresenter元素所有内容控件都需要ContentPresenter元素——它是表示“在此插入内容”的标记器告诉WPF在何处保存内容 ControlTemplate x:KeyButtonTemplate2 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteContentPresenter RecognizesAccessKeyTrue/ContentPresenter/Border/ControlTemplate 该ContentPresenter元素将RecognizesAccessKey 属性设置为true。尽管这不是必需的但可确保按钮支持访问键——具有下划线的字母可使用该字母触发按钮。 如果控件继承自ContentControl类其模板将包含一个ContentPresenter元素指示将在何处放置内容。如果控件继承自ItemsControl 类其模板将包含一个 ItemsPresenter 元素指示在何处放置包含列表项的面板。在极少数情况下控件可能使用这些类的派生版本——例如ScrollViewer的控件模板使用继承自ContentPresenter类的ScrollContentPresenter类。  模板绑定 现在这个按钮模板还存在一个小问题。现在为按钮添加的标签将Margin 属性的值指定为10并将Padding属性的值指定为5。父容器关注的是Margin属性但忽略了Padding属性是按钮的内容和侧边挤压在一起。此处的问题是Padding 属性不起作用除非在模板中特别注意它。换句话说模板负责检索内边距值并使用该值在内容周围插入额外的空白。 辛运的是WPF专门针对该目的设计了一个工具模板绑定。通过使用模板绑定模板可从应用模板的控件中提取一个值。在这里可使用模板绑定检索Padding属性的值并使用该属性值在ContentPresenter元素周围创建外边距 ContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter 这样就会得到所期望的效果在边框和内容之间添加了一些空白。 模板绑定和普通的数据绑定类似但它们的量级更轻因为它们是专门针对在控件模板中使用而设计的。它们只支持单向数据绑定换句话说它们可从控件向模板传递信息但不能从模板向控件传递信息并且不能用于从Freezable类的派生类的属性中提取信息。如果遇到模板绑定不生效的情形可改用具有完整功能的数据绑定。 模板绑定支持WPF的变化监测基础结构所有依赖项属性都包含该基础结构。这意味着如果修改了控件的属性模板会自动考虑该变化。当使用在一小段时间内重复改变属性值的动画时该细节尤其有用。 预计需要哪些模板绑定的唯一方法是检查默认控件模板。如果查看Button类的控件模板就会发现在模板绑定的使用方法上与自定义模板是完全相同的——获取为按钮指定的内边距并将它转换为ContentPresenter元素周围的外边距。还会发现标准按钮模板包含另外几个模板绑定如HorizontalAlignment、VerticalAlignment以及Background这个简单的自定义模板中没有使用这些模板绑定。这意味着如果为按钮设置了这些属性对于这个简单的自定义模板来说这些设置没有效果。 改变属性触发器 如果测试上面创建的按钮就会发现它令人非常失望。本质上它不过是一个红色的圆角矩形——当在它上面移动鼠标或单击鼠标时其外观没有任何反应。 可通过为控件模板添加触发器来方便的解决这个问题。 ControlTemplate.TriggersTrigger PropertyIsMouseOver ValueTrueSetter TargetNameborder PropertyBackground ValueDarkRed/Setter/TriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameborder PropertyBackground ValueIndianRed/SetterSetter TargetNameborder PropertyBorderBrush ValueDarkKhaki/Setter/Trigger/ControlTemplate.Triggers 在所有按钮中还需要另一个元素——焦点指示器。虽然无法改变现有的边框以添加焦点效果但是还可以很容易的添加另一个元素以显示是否具有焦点并且可以简单地使用触发器根据Button.IsKeyboardFocused属性显示或隐藏该元素。尽管可使用许多方法创建焦点效果但下面的示例值添加了一个具有虚线边框的透明的Rectangle元素。Rectangle元素不能包含子内容从而需要确保Rectangle元素和其余内容相互重叠。完成该操作最容易的方法是使用只有一个单元格的Grid控件来封装Rectangle元素和ContentPresenter元素这两个元素位于同一个单元格中。 ControlTemplate x:KeyButtonTemplate3 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlack StrokeThickness1 StrokeDashArray1 2 SnapsToDevicePixelsTrue /RectangleContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter/Grid/BorderControlTemplate.TriggersTrigger PropertyIsMouseOver ValueTrueSetter TargetNameborder PropertyBackground ValueDarkRed/Setter/TriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameborder PropertyBackground ValueIndianRed/SetterSetter TargetNameborder PropertyBorderBrush ValueDarkKhaki/Setter/TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible //TriggerTrigger PropertyIsEnabled ValueFalseSetter TargetNameborder PropertyTextBlock.Foreground ValueGray /Setter TargetNameborder PropertyBackground ValueMistyRose //Trigger/ControlTemplate.Triggers/ControlTemplate 设置器再次使用TargetName属性查找需要改变的元素。另外还添加了一个触发器当按钮的IsEnable属性变为false时该触发器改变按钮的背景色。为确保该规则优于其它相冲突的触发器设置应当在触发器列表的末尾定义它。 模板与样式 模板与样式有类似之处。通常在整个应用程序中这两个特性都可以改变元素的外观。然而样式被限制在一个小得多的范围之内。它们可调整控件的属性但不能使用全新的由不同元素组成的可视化树替代控件原来的外观。 在前面看到的简单按钮包含了一些仅凭样式无法实现的特性。尽管可使用样式设置按钮的背景色但当按下按钮时调整按钮的背景色会遇到更多麻烦因为按钮的内置模板已经针对该目的提供了一个触发器。另外也不能很方便的添加焦点矩形。 还可以通过控件模板实现许多特殊类型的按钮如果使用样式是无法获得此类效果的。例如不是使用矩形边框而是创建类似椭圆形状的按钮或使用路径绘制更复杂的形状。其余的标记——甚至是用于在不同状态之间切换背景色的触发器——基本上不需要加以修改。 使用动画触发器 触发器并非仅局限于设置属性当特定属性发生变化时还可以使用事件触发器运行动画。 ControlTemplate x:KeyButtonTemplate4 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlue StrokeThickness1 StrokeDashArray2 2 SnapsToDevicePixelsTrue /RectangleContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter/Grid/BorderControlTemplate.TriggersEventTrigger RoutedEventMouseEnterBeginStoryboardStoryboardColorAnimation Storyboard.TargetNameborder Storyboard.TargetPropertyBackground.ColorToBlue Duration0:0:1 AutoReverseTrue RepeatBehaviorForever/ColorAnimation/Storyboard/BeginStoryboard/EventTriggerEventTrigger RoutedEventMouseLeaveBeginStoryboardStoryboardColorAnimation Storyboard.TargetNameborder Storyboard.TargetPropertyBackground.Color Duration0:0:0.5/ColorAnimation/Storyboard/BeginStoryboard/EventTriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameborder PropertyBackground ValueIndianRed /Setter TargetNameborder PropertyBorderBrush ValueDarkKhaki //TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible //Trigger/ControlTemplate.Triggers/ControlTemplate 这里使用ColorAnimation对象来改变按钮。下面是可能希望使用EventTrigger驱动的动画执行的其他一些任务 显示或隐藏元素 为此需要改变控件模板中的Opacity属性 改变形状或位置 可使用TranslateTransform 对象调整元素的位置例如稍偏移元素是按钮具有已被按下的感觉。当用户将鼠标移到元素上时可使用ScaleTransform 或 RotateTransform 对象稍微旋转元素的外观。 改变光照或着色  为此需使用改变绘制背景的画刷的动画。可使用ColorAnimation 动画改变SolidBrush 画刷中的颜色也可动态显示更复杂的画刷以得到更高级的效果。例如可改变LinearGradientBrush画刷中的一种颜色这是默认按钮控件模板执行的操作也可改变RadialGradientBrush 画刷的中心点。 有些高级光照效果使用多层透明元素。对于这种情况可使用动画修改其中一层的透明度从而让其他层能够透过该层显示。 组织模板资源 当使用控件模板时需要决定如何更广泛地共享模板以及是否希望自动地或明确地应用模板。 第一个问题是关于希望在何处使用模板的问题。例如是将它们限制在特定的窗口中吗大多数情况下控件模板应用于多个窗口甚至可能应用于整个应用程序。为避免多次定义模板可在Application类的Resources集合中定义模板资源。 然而为此需要考虑领一个事项。通常控件模板在多个应用程序之间共享。单个应用程序很可能使用单独开发的模板。然而一个应用程序只有一个App.xaml文件和一个Application.Resources集合。因此在单独资源字典中定义资源是一个更好的主意。这样可灵活地在特定窗口或在整个应用程序中使用资源。而且还可以结合使用样式因为任何应用程序都可以包含多个资源字典。 虽然可将所有模板组合到单个资源字典文件中但富有经验的开发人员更愿意为每个控件模板创建单独的资源字典。这是因为控件模板可能很快会变得过于复杂并可能需要使用其他相关资源。将它们保存在一个单独的地方并与其它控件相隔离是一种很好的组织方式。 为使用资源字典只需要将它们添加到特定窗口或应用程序这种情况更常见的Resources集合中。 分解按钮控件模板 当完善或扩展控件模板时可发现其中封装了大量的不同细节包括特定的形状、几何图形和画刷。从您的控件模板中提取这些细节并将它们定义为单独的资源是一个好主意。一个原因是通过该步骤可以更方便的在一组相关的控件中重用这些画刷。为使该工作更加容易可为画刷创建一个单独资源字典Brush.xaml并将该资源字典合并到每个控件如Button.Xaml、CheckBox.xaml、RadioButton.xaml的资源字典中。 Resources/Brush.xaml ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyHighlightBackgroundGradientStop ColorWhite Offset0 /GradientStop ColorBlue Offset.4 //RadialGradientBrushRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyPressedBackgroundGradientStop ColorWhite Offset0 /GradientStop ColorBlue Offset1 //RadialGradientBrushSolidColorBrush ColorBlue x:KeyDefaultBackground/SolidColorBrushSolidColorBrush ColorGray x:KeyDisabledBackground/SolidColorBrushRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyBorderGradientStop ColorWhite Offset0 /GradientStop ColorBlue Offset1 //RadialGradientBrush /ResourceDictionary 为查看这种技术的工作情况分析下面的标记。这些标记代表了一个按钮的完整资源字典包括控件模板使用的资源、控件模板以及为应用程序中每个按钮应用控件模板的样式规则。始终需要遵循这一顺序因为资源需要在使用之前先定义如果在模板之后定义画刷将收到错误消息因为模板找不到所需的画刷。 Resources/GradientButton.xaml ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml x:ClassTestControlTemplate.Resources.GradientButtonResourceDictionary.MergedDictionariesResourceDictionary SourceBrush.xaml/ResourceDictionary/ResourceDictionary.MergedDictionariesControlTemplate x:KeyGradientButtonTemplate TargetType{x:Type Button}Border NameBorder BorderBrush{StaticResource Border} BorderThickness2 CornerRadius2 Background{StaticResource DefaultBackground} TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlack StrokeThickness1 StrokeDashArray1 2 SnapsToDevicePixelsTrue/RectangleContentPresenter Margin{TemplateBinding Padding} RecognizesAccessKeyTrue/ContentPresenter/Grid/BorderControlTemplate.TriggersTrigger PropertyIsMouseOver ValueTrueSetter TargetNameBorder PropertyBackground Value{StaticResource HighlightBackground} //TriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameBorder PropertyBackground Value{StaticResource PressedBackground} //TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible/Setter/TriggerTrigger PropertyIsEnabled ValueFalseSetter TargetNameBorder PropertyBackground Value{StaticResource DisabledBackground}/Setter/Trigger/ControlTemplate.Triggers/ControlTemplateStyle TargetType{x:Type Button}Setter PropertyControl.Template Value{StaticResource GradientButtonTemplate}/Setter/Style /ResourceDictionary 通过样式应用模板 这种设计存在局限性控件模板本质上硬编码了一些细节如颜色方案。这意味着如果希望在按钮中使用相同的元素组合Border、Grid、Rectangle和ContentPresenter并采用相同的方式安排他们但希望提供不同的颜色方案就必须创建引用不同画刷资源的新模板副本。 这未必是个问题毕竟布局和格式化细节可能紧密相关以至于不希望以任何方式隔断它们。但这确实限制可重用控件模板的能力。如果模板使用了元素的复合排列方式并且希望重用这些具有各种不同格式化细节通常是颜色和字体的元素可从模板中将这些细节提取出来并将它们放到样式中。 为此需要重新编写模板。这次不能使用硬编码的颜色而需要使用模板绑定从控件属性中提取出信息。下面示例为前面特殊按钮定义了一个精简模板。控件模板将一些细节作为基础的固定要素——焦点框和两个单位宽的圆角边框。唯一需要保留的触发器是显示焦点框的那个触发器。 Resources/CustomButton.xaml ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlResourceDictionary.MergedDictionariesResourceDictionary SourceBrush.xaml/ResourceDictionary/ResourceDictionary.MergedDictionariesControlTemplate x:KeyCustomButtonTemplate TargetType{x:Type Button}Border x:Nameborder Background{TemplateBinding Background} BorderBrush{TemplateBinding BorderBrush} BorderThickness{TemplateBinding BorderThickness} SnapsToDevicePixelstrueGridRectangle NameFocusCue VisibilityHidden StrokeAliceBlue StrokeThickness2 StrokeDashArray1 2 SnapsToDevicePixelstrue/RectangleContentPresenter x:NamecontentPresenter FocusableFalse HorizontalAlignment{TemplateBinding HorizontalContentAlignment} Margin{TemplateBinding Padding}RecognizesAccessKeyTrue SnapsToDevicePixels{TemplateBinding SnapsToDevicePixels} VerticalAlignment{TemplateBinding VerticalContentAlignment}//Grid/BorderControlTemplate.TriggersTrigger PropertyIsKeyboardFocused ValuetrueSetter PropertyVisibility TargetNameFocusCue ValueVisible//Trigger/ControlTemplate.Triggers/ControlTemplateStyle TargetType{x:Type Button}Setter PropertyControl.Template Value{StaticResource CustomButtonTemplate}/SetterSetter PropertyBorderBrush Value{StaticResource Border}/SetterSetter PropertyBackground Value{StaticResource DefaultBackground}/SetterSetter PropertyTextBlock.Foreground ValueWhite/SetterStyle.TriggersTrigger PropertyIsMouseOver ValueTrueSetter PropertyBackground Value{StaticResource HighlightBackground}/Setter/TriggerTrigger PropertyIsPressed ValueTrueSetter PropertyBackground Value{StaticResource PressedBackground}/Setter/TriggerTrigger PropertyIsEnabled ValueFalseSetter PropertyBackground Value{StaticResource DisabledBackground}/Setter/Trigger/Style.Triggers/Style /ResourceDictionary 理想情况下应能在控件模板中保留所有触发器因为它们代表控件的行为并使用样式简单设置基本属性。但在此如果希望样式能够设置颜色方案是不可能实现的。如果在控件模板和样式中都设置了触发器那么样式触发器具有优先权。 由用户选择的皮肤 在一些应用程序中可能希望动态改变模板通常是根据用户的个人爱好加以改变。这很容易实现但文档中没有对此进行详细说明。基本技术是在运行时加载新的资源字典并使用新加载的资源字典代替当前的资源字典不需要替换所有资源只需要替换那些用于皮肤的资源。 诀窍在于检索ResourceDictionary对象该对象经过编译并作为资源嵌入到应用程序中。最简单的方法是使用ResourceManager类来加载所需资源。 例如前面已经创建了一个按钮控件模板保存在GradientButton.xaml文件中现在创建另一个按钮控件模板保存在 GradientButton.Variant.xaml中这两个文件都位于Resources文件夹中。 Resources/Brush.Variant.xmal ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyHighlightBackgroundGradientStop ColorWhite Offset0 /GradientStop ColorGreen Offset.4 //RadialGradientBrushRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyPressedBackgroundGradientStop ColorWhite Offset0 /GradientStop ColorGreen Offset1 //RadialGradientBrushSolidColorBrush ColorDarkGreen x:KeyDefaultBackground/SolidColorBrushSolidColorBrush ColorGray x:KeyDisabledBackground/SolidColorBrushRadialGradientBrush RadiusX1 RadiusY5 GradientOrigin0.5,0.3 x:KeyBorderGradientStop ColorWhite Offset0 /GradientStop ColorGreen Offset1 //RadialGradientBrush /ResourceDictionary Resources/GradientButton.Variant.xaml ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml x:ClassTestControlTemplate.Resources.GradientButtonVariantResourceDictionary.MergedDictionariesResourceDictionary SourceBrush.Variant.xaml/ResourceDictionary/ResourceDictionary.MergedDictionariesControlTemplate x:KeyGradientButtonTemplate TargetType{x:Type Button}Border NameBorder BorderBrush{StaticResource Border} BorderThickness2 CornerRadius2 Background{StaticResource DefaultBackground} TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlack StrokeThickness1 StrokeDashArray1 2 SnapsToDevicePixelsTrue /RectangleContentPresenter Margin{TemplateBinding Padding} RecognizesAccessKeyTrue/ContentPresenter/Grid/BorderControlTemplate.TriggersTrigger PropertyIsMouseOver ValueTrueSetter TargetNameBorder PropertyBackground Value{StaticResource HighlightBackground} //TriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameBorder PropertyBackground Value{StaticResource PressedBackground} //TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible/Setter/TriggerTrigger PropertyIsEnabled ValueFalseSetter TargetNameBorder PropertyBackground Value{StaticResource DisabledBackground}/Setter/Trigger/ControlTemplate.Triggers/ControlTemplateStyle TargetType{x:Type Button}Setter PropertyControl.Template Value{StaticResource GradientButtonTemplate}/Setter/Style /ResourceDictionary 现在在一个容器中使用两个按钮控件模板中的一个比如 GradientButton.xaml StackPanel NameskinStackPanelStackPanel.ResourcesResourceDictionaryResourceDictionary.MergedDictionariesResourceDictionary SourceResources/GradientButton.xaml/ResourceDictionary/ResourceDictionary.MergedDictionaries/ResourceDictionary/StackPanel.ResourcesButton Margin10 Padding5A Simple Button with a Custom Template/ButtonButton Margin10 Padding5Another Button with a Custom Template/ButtonButton Margin10 Padding5A _Third Button with a Custom Template/ButtonButton Margin10 Padding5 IsEnabledFalse A Disabled Button/ButtonCheckBox Margin10 CheckedchkGreen_Checked UncheckedchkGreen_UncheckedUse Alternate Theme/CheckBox/StackPanel 通过CheckBox的Checked和Unchecked事件处理程序更换皮肤 private void chkGreen_Checked(object sender, RoutedEventArgs e){ResourceDictionary resourceDictionary new ResourceDictionary();resourceDictionary.Source new Uri(Resources/GradientButton.Variant.xaml, UriKind.Relative);skinStackPanel.Resources.MergedDictionaries[0] resourceDictionary;}private void chkGreen_Unchecked(object sender, RoutedEventArgs e){ResourceDictionary resourceDictionary new ResourceDictionary();resourceDictionary.Source new Uri(Resources/GradientButton.xaml, UriKind.Relative);skinStackPanel.Resources.MergedDictionaries[0] resourceDictionary;} 上面代码加载GradientButton.Variant.xaml资源字典并将它放置到MergedDictionaries集合的第一个位置。在此没有清空MergedDictionaries集合或其他任何窗口资源因为您可能连接到了其他希望继续使用的资源字典。也没有为MergedDictionaries集合添加新条目因为这可能与位于不同集合中的同名资源发生冲突。 如果正在为整个应用程序改变皮肤可使用相同的方法但应使用应用程序资源字典。还可以使用pack URI语法加载在另一个程序集中定义的资源字典 ResourceDictionary resourceDictionary new ResourceDictionary();resourceDictionary.Source new Uri(ControlTemplateLibrary;component/GradientButton.Variant.xaml, UriKind.Relative);skinStackPanel.Resources.MergedDictionaries[0] resourceDictionary; 当加载新的资源字典时会自动使用新模板更新所有按钮。如果当修改控件时不需要完全改变皮肤还可以为皮肤提供基本样式。 这里GradientButton.xaml 和 GradientButton.Variant.xaml 资源使用元素类型样式自动改变按钮。还有一种方法——可通过手动设置Button对象的Template 或 Style 属性来选用新的模板。如果使用这种方法务必使用Dynamic Resource引用而不能使用StaticResource。如果使用StaticResource当切换皮肤时不会更新按钮模板。 当使用DynamicResource引用时首先要保证所需要的资源位于资源层次结构中。如果资源并不位于资源层次结构中就会忽略资源。而且按钮会恢复为它们的标准外观而不会生成错误。 还有一种通过编写代码加载资源字典的方法。可使用与为窗口创建代码隐藏类几乎相同的方法为资源字典创建代码隐藏类。然后就可以直接实例化这个类而不是使用ResourceDictionary.Source 属性。这种方法有一个优点它是强类型的没有机会为Source属性输入无效的URI并且可为资源类添加属性、方法以及其他功能。例如可以使用这种方法为自定义窗口模板创建具有事件处理代码的资源。 尽管为资源字典创建代码隐藏类很容易但是VisualStudio并不能自动完成该工作。需要为继承自ResourceDictionary的部分类添加代码文件并在构造函数中调用InitializeComponent() 方法 Resources/GradientButton.xaml.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows;namespace TestControlTemplate.Resources;public partial class GradientButton : ResourceDictionary {public GradientButton(){InitializeComponent();} }Resources/GradientButton.Variant.xaml.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows;namespace TestControlTemplate.Resources;public partial class GradientButtonVariant : ResourceDictionary {public GradientButtonVariant(){InitializeComponent();} }需要在对应的资源字典中添加Class属性 ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml x:ClassTestControlTemplate.Resources.GradientButton... /ResourceDictionary ResourceDictionary xmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xaml x:ClassTestControlTemplate.Resources.GradientButtonVariant... /ResourceDictionary 现在可使用该代码创建资源字典并将它应用于窗口 private void chkGreen2_Checked(object sender, RoutedEventArgs e){GradientButtonVariant gradientButtonVariant new GradientButtonVariant();skinStackPanel2.Resources.MergedDictionaries[0] gradientButtonVariant;}private void chkGreen2_Unchecked(object sender, RoutedEventArgs e){GradientButton gradientButton new GradientButton();skinStackPanel2.Resources.MergedDictionaries[0] gradientButton;} 测试代码文件清单 除了创建工程自动生成的App.xaml、App.xaml.cs、AssemblyInfo.cs外 在前面已经列出了 VisualTreeDisplay.xaml、VisualTreeDisplay.xaml.cs、ControlTemplateDisplay.xaml、ControlTemplateDisplay.xaml.cs、Resource/Brush.xaml、Resource/Brush.Variant.xaml、Resource/CustomButton.xaml、Resource/GradientButton.xaml、Resource/GradientButton.xaml.cs、Resource/GradientButton.Variant.xaml、Resource/GradientButton.Variant.xaml.cs 还剩下主窗口 MainWindow.xaml Window x:ClassTestControlTemplate.MainWindowxmlnshttp://schemas.microsoft.com/winfx/2006/xaml/presentationxmlns:xhttp://schemas.microsoft.com/winfx/2006/xamlxmlns:dhttp://schemas.microsoft.com/expression/blend/2008xmlns:mchttp://schemas.openxmlformats.org/markup-compatibility/2006xmlns:localclr-namespace:TestControlTemplatemc:IgnorabledTitleMainWindow Height450 Width800Window.ResourcesSolidColorBrush x:KeyButton.MouseOver.Background Color#FFBEE6FD/SolidColorBrush x:KeyButton.MouseOver.Border Color#FF3C7FB1/SolidColorBrush x:KeyButton.Pressed.Background Color#FFC4E5F6/SolidColorBrush x:KeyButton.Pressed.Border Color#FF2C628B/SolidColorBrush x:KeyButton.Disabled.Background Color#FFF4F4F4/SolidColorBrush x:KeyButton.Disabled.Border Color#FFADB2B5/SolidColorBrush x:KeyButton.Disabled.Foreground Color#FF838383/ControlTemplate x:KeyButtonTemplate TargetType{x:Type Button}Border x:Nameborder Background{TemplateBinding Background} BorderBrush{TemplateBinding BorderBrush} BorderThickness{TemplateBinding BorderThickness} SnapsToDevicePixelstrueContentPresenter x:NamecontentPresenter FocusableFalse HorizontalAlignment{TemplateBinding HorizontalContentAlignment} Margin{TemplateBinding Padding}RecognizesAccessKeyTrue SnapsToDevicePixels{TemplateBinding SnapsToDevicePixels} VerticalAlignment{TemplateBinding VerticalContentAlignment}//BorderControlTemplate.TriggersTrigger PropertyIsDefaulted ValuetrueSetter PropertyBorderBrush TargetNameborder Value{DynamicResource {x:Static SystemColors.HighlightBrushKey}}//TriggerTrigger PropertyIsMouseOver ValuetrueSetter PropertyBackground TargetNameborder Value{StaticResource Button.MouseOver.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.MouseOver.Border}//TriggerTrigger PropertyIsPressed ValuetrueSetter PropertyBackground TargetNameborder Value{StaticResource Button.Pressed.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.Pressed.Border}//TriggerTrigger PropertyIsEnabled ValuefalseSetter PropertyBackground TargetNameborder Value{StaticResource Button.Disabled.Background}/Setter PropertyBorderBrush TargetNameborder Value{StaticResource Button.Disabled.Border}/Setter PropertyTextElement.Foreground TargetNamecontentPresenter Value{StaticResource Button.Disabled.Foreground}//Trigger/ControlTemplate.Triggers/ControlTemplateControlTemplate x:KeyButtonTemplate2 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter/Border/ControlTemplateControlTemplate x:KeyButtonTemplate3 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlue StrokeThickness1 StrokeDashArray2 2 SnapsToDevicePixelsTrue /RectangleContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter/Grid/BorderControlTemplate.TriggersTrigger PropertyIsMouseOver ValueTrueSetter TargetNameborder PropertyBackground ValueDarkRed/Setter/TriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameborder PropertyBackground ValueIndianRed/SetterSetter TargetNameborder PropertyBorderBrush ValueDarkKhaki/Setter/TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible //TriggerTrigger PropertyIsEnabled ValueFalseSetter TargetNameborder PropertyTextBlock.Foreground ValueGray /Setter TargetNameborder PropertyBackground ValueMistyRose //Trigger/ControlTemplate.Triggers/ControlTemplateControlTemplate x:KeyButtonTemplate4 TargetType{x:Type Button}Border Nameborder BackgroundRed BorderBrushOrange BorderThickness3 CornerRadius2 TextBlock.ForegroundWhiteGridRectangle NameFocusCue VisibilityHidden StrokeBlue StrokeThickness1 StrokeDashArray2 2 SnapsToDevicePixelsTrue /RectangleContentPresenter RecognizesAccessKeyTrue Margin{TemplateBinding Padding}/ContentPresenter/Grid/BorderControlTemplate.TriggersEventTrigger RoutedEventMouseEnterBeginStoryboardStoryboardColorAnimation Storyboard.TargetNameborder Storyboard.TargetPropertyBackground.ColorToBlue Duration0:0:1 AutoReverseTrue RepeatBehaviorForever/ColorAnimation/Storyboard/BeginStoryboard/EventTriggerEventTrigger RoutedEventMouseLeaveBeginStoryboardStoryboardColorAnimation Storyboard.TargetNameborder Storyboard.TargetPropertyBackground.Color Duration0:0:0.5/ColorAnimation/Storyboard/BeginStoryboard/EventTriggerTrigger PropertyIsPressed ValueTrueSetter TargetNameborder PropertyBackground ValueIndianRed /Setter TargetNameborder PropertyBorderBrush ValueDarkKhaki //TriggerTrigger PropertyIsKeyboardFocused ValueTrueSetter TargetNameFocusCue PropertyVisibility ValueVisible //Trigger/ControlTemplate.Triggers/ControlTemplate/Window.ResourcesStackPanelButton ClickShowVisualTree_ClickShowVisualTree/ButtonButton ClickShowControlTemplate_ClickShowControlTemplate/ButtonButton Template{StaticResource ButtonTemplate}ButtonTemplate/ButtonButton Style{x:Null} Template{StaticResource ButtonTemplate2} Padding5 HorizontalContentAlignmentCenterButtonTemplate2/ButtonButton Template{StaticResource ButtonTemplate3} Padding5 HorizontalContentAlignmentCenterButtonTemplate3/ButtonButton Template{StaticResource ButtonTemplate4} Padding5 HorizontalContentAlignmentCenterButtonTemplate4/ButtonButton ContentGradientButtonTemplateButton.ResourcesResourceDictionaryResourceDictionary.MergedDictionariesResourceDictionary SourceResources/GradientButton.xaml/ResourceDictionary/ResourceDictionary.MergedDictionaries/ResourceDictionary/Button.Resources/ButtonButton ContentCustomButtonTemplateButton.ResourcesResourceDictionaryResourceDictionary.MergedDictionariesResourceDictionary SourceResources/CustomButton.xaml/ResourceDictionary/ResourceDictionary.MergedDictionaries/ResourceDictionary/Button.Resources/ButtonStackPanel NameskinStackPanelStackPanel.ResourcesResourceDictionaryResourceDictionary.MergedDictionariesResourceDictionary SourceResources/GradientButton.xaml/ResourceDictionary/ResourceDictionary.MergedDictionaries/ResourceDictionary/StackPanel.ResourcesButton Margin10 Padding5A Simple Button with a Custom Template/ButtonButton Margin10 Padding5Another Button with a Custom Template/ButtonButton Margin10 Padding5A _Third Button with a Custom Template/ButtonButton Margin10 Padding5 IsEnabledFalse A Disabled Button/ButtonCheckBox Margin10 CheckedchkGreen_Checked UncheckedchkGreen_UncheckedUse Alternate Theme/CheckBox/StackPanelStackPanel NameskinStackPanel2StackPanel.ResourcesResourceDictionaryResourceDictionary.MergedDictionariesResourceDictionary SourceResources/GradientButton.xaml/ResourceDictionary/ResourceDictionary.MergedDictionaries/ResourceDictionary/StackPanel.ResourcesButton Margin10 Padding5A Simple Button with a Custom Template/ButtonButton Margin10 Padding5Another Button with a Custom Template/ButtonButton Margin10 Padding5A _Third Button with a Custom Template/ButtonButton Margin10 Padding5 IsEnabledFalse A Disabled Button/ButtonCheckBox Margin10 CheckedchkGreen2_Checked UncheckedchkGreen2_UncheckedUse Alternate Theme/CheckBox/StackPanel/StackPanel /WindowMainWindow.xaml.cs 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 TestControlTemplate.Resources;namespace TestControlTemplate;/// summary /// Interaction logic for MainWindow.xaml /// /summary public partial class MainWindow : Window {public MainWindow(){InitializeComponent();}private void ShowVisualTree_Click(object sender, RoutedEventArgs e){VisualTreeDisplay treeDisplay new VisualTreeDisplay();treeDisplay.ShowVisualTree(this);treeDisplay.Show();}private void ShowControlTemplate_Click(object sender, RoutedEventArgs e){ControlTemplateDisplay controlTemplate new ControlTemplateDisplay();controlTemplate.Show();}private void chkGreen_Checked(object sender, RoutedEventArgs e){ResourceDictionary resourceDictionary new ResourceDictionary();resourceDictionary.Source new Uri(Resources/GradientButton.Variant.xaml, UriKind.Relative);skinStackPanel.Resources.MergedDictionaries[0] resourceDictionary;}private void chkGreen_Unchecked(object sender, RoutedEventArgs e){ResourceDictionary resourceDictionary new ResourceDictionary();resourceDictionary.Source new Uri(Resources/GradientButton.xaml, UriKind.Relative);skinStackPanel.Resources.MergedDictionaries[0] resourceDictionary;}private void chkGreen2_Checked(object sender, RoutedEventArgs e){GradientButtonVariant gradientButtonVariant new GradientButtonVariant();skinStackPanel2.Resources.MergedDictionaries[0] gradientButtonVariant;}private void chkGreen2_Unchecked(object sender, RoutedEventArgs e){GradientButton gradientButton new GradientButton();skinStackPanel2.Resources.MergedDictionaries[0] gradientButton;} }
http://www.zqtcl.cn/news/661189/

相关文章:

  • 房产网站做那个比较好网页设计属于前端吗
  • 衡水企业网站建设费用html5网页设计教程
  • 用wp系统做网站网站有收录没排名
  • 网站源码程序下载ios开发软件
  • 设计好的网站什么是企业网站策划案
  • 北京网站建设亿玛酷适合5传奇网站装备动态图怎么做
  • 多平台网站设计实例3d效果图什么网站做的好
  • 58同城西安网站建设购物网站前端浮动特效怎么做
  • asp网站模板源码wordpress 画图插件
  • 免费网站建站 知乎伪原创嵌入网站
  • 2网站建设城乡住房建设网站
  • 游戏网站建设公司建设银行网站登陆二星是什么意思
  • 长春网站排名优化泉州网站建设方案服务
  • 教育培训机构加盟十大排名搜索引擎优化宝典
  • 全景精灵网站建设网站建设长尾关键词
  • 老城网站建设注册网站不需要手机验证的
  • 可以赚钱做任务的网站有哪些莘县做网站
  • 可信网站 认证规则山东网站建设代理
  • 网站怎么谈设计常用的软件开发文档有哪些
  • 该怎么给做网站的提页面需求焦作做网站公司
  • 自己做的网站找不到了制作网站问题和解决方法
  • 5118站长平台cento安装wordpress
  • 政务大厅网站建设管理制度wordpress商城移动端
  • 提供中小企业网站建设北京企业网站建设公司哪家好
  • 做海报找图片的网站黑群晖按照wordpress
  • 网站建设与运营市场开拓方案网站首页策划
  • 做国外网站什么好网站快速优化排名排名
  • 如东做网站专注高密网站建设
  • dw网页设计作品简单宁波seo排名方案
  • 网站做微信接口吗小说网站首页模板