WPF使用装饰器实现SAP焦点样式(改进版)

我在上一篇博文中介绍了如何使用自定义装饰器实现SAP的焦点样式。

上次的思路是遍历窗体中的控件,如果是指定类型则使用自定义的装饰器。但是如果要求部分控件使用自定义装饰器,或者用户控件中的控件也要实现,上一种方法是无法完成的。

看了MSDN的中“AdornerDecorator“类能为 “可视化树”中的子元素提供 AdornerLayer。
用法:

<AdornerDecorator> Child </AdornerDecorator>


那么是否能自定义一个AdornerDecorator来实现AdornerLayer呢?答案是肯定的。但是就必须写成:

<MyAdornerDecorator> Child </MyAdornerDecorator>

这样有个弊端,如果Child(控件)是复杂控件,则不能很好的实现AdornerLayer的装饰器。
如果改成附加属性则会使代码最小化。Good idea!我们就按这个思路来实现。

思路:
1.定义一个自定义AdornerDecorator,用于为控件创建一个AdornerLayer;
2.在AdornerLayer上定义自定义的装饰器样式
代码比较简单,见注释说明。

实现步骤:
1.创建自定义AdornerDecorator类,AdornerDecoratorHelper

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Controls; namespace AdornerDecoratorDemo { public class AdornerDecoratorHelper { public static DependencyProperty GetAdornerLayer(DependencyObject obj) { return (DependencyProperty)obj.GetValue(AdornerLayerProperty); } public static void SetAdornerLayer(DependencyObject obj, DependencyProperty value) { obj.SetValue(AdornerLayerProperty, value); } /// <summary> /// 注册附加属性 /// 用于设置控件的AdornerLayer /// </summary> public static readonly DependencyProperty AdornerLayerProperty = DependencyProperty.RegisterAttached("AdornerLayer", typeof(DependencyObject), typeof(AdornerDecoratorHelper), new UIPropertyMetadata(null, (o,n) => { if (o is Control && n.NewValue is Visual) { //获得注册附加属性的控件 Control c = o as Control; c.Loaded += (s1, e1) => { AdornerLayer layer = AdornerLayer.GetAdornerLayer((Visual)n.NewValue); //为控件添加获得焦点和失去焦点事件 if (layer != null) { c.GotFocus += (s, e) => { //添加自定义装饰器 layer.Add(new SimpleAdorner(c)); }; c.LostFocus += (s, e) => { //取消控件的装饰器 Adorner[] ads = layer.GetAdorners(c); if (ads != null) { for (int i = ads.Length - 1; i >= 0; i--) { layer.Remove(ads[i]); } } }; } }; } })); } }


2.创建自定义装饰器,用于在控件的四个角上显示红线

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Documents; using System.Windows; using System.Windows.Media; namespace AdornerDecoratorDemo { /// <summary> /// 自定义装饰器 /// </summary> public class SimpleAdorner : Adorner { public SimpleAdorner(UIElement adornedElement) : base(adornedElement) { } /// <summary> /// 绘制自定义装饰器的呈现样子 /// </summary> /// <param name="drawingContext"></param> protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) { Rect adornedElementRect = new Rect(this.AdornedElement.RenderSize); Pen renderPen = new Pen(new SolidColorBrush(Colors.Red), 1.0); // Draw a circle at each corner. drawingContext.DrawLine(renderPen, new Point(adornedElementRect.TopLeft.X - 3, adornedElementRect.TopLeft.Y - 3), new Point(adornedElementRect.TopLeft.X + 5, adornedElementRect.TopLeft.Y - 3)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.TopLeft.X - 3, adornedElementRect.TopLeft.Y - 3), new Point(adornedElementRect.TopLeft.X - 3, adornedElementRect.TopLeft.Y + 5)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.TopRight.X + 3, adornedElementRect.TopRight.Y - 3), new Point(adornedElementRect.TopRight.X - 5, adornedElementRect.TopRight.Y - 3)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.TopRight.X + 3, adornedElementRect.TopRight.Y - 3), new Point(adornedElementRect.TopRight.X + 3, adornedElementRect.TopRight.Y + 5)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.BottomLeft.X - 3, adornedElementRect.BottomLeft.Y + 3), new Point(adornedElementRect.BottomLeft.X + 5, adornedElementRect.BottomLeft.Y + 3)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.BottomLeft.X - 3, adornedElementRect.BottomLeft.Y + 3), new Point(adornedElementRect.BottomLeft.X - 3, adornedElementRect.BottomLeft.Y - 5)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.BottomRight.X + 3, adornedElementRect.BottomRight.Y + 3), new Point(adornedElementRect.BottomRight.X - 5, adornedElementRect.BottomRight.Y + 3)); drawingContext.DrawLine(renderPen, new Point(adornedElementRect.BottomRight.X + 3, adornedElementRect.BottomRight.Y + 3), new Point(adornedElementRect.BottomRight.X + 3, adornedElementRect.BottomRight.Y - 5)); } } }


3.窗体实现

<Window x:Class="AdornerDecoratorDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:m="clr-namespace:AdornerDecoratorDemo" Title="用装饰器实现 模仿SAP焦点样式" Height="225" Width="442"> <Grid Name="grid"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="2*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Text="Name:" /> <TextBox Grid.Row="0" Grid.Column="1" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}" /> <TextBlock Grid.Row="1" Text="Gender:" /> <ComboBox Grid.Row="1" Grid.Column="1" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}"> <ComboBoxItem Content="Male" /> <ComboBoxItem Content="Female" /> </ComboBox> <TextBlock Grid.Row="2" Text="Marriage:" /> <StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal"> <RadioButton Content="Married" GroupName="Marry" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}"/> <RadioButton Content="Single" GroupName="Marry" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}"/> </StackPanel> <TextBlock Grid.Row="3" Text="Interest:" /> <StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal"> <CheckBox Content="Badminton" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}"/> <CheckBox Content="angle" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}"/> </StackPanel> <StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal"> <Button Content="Ok" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}" /> <Button Content="Reset" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}" /> <Button Content="Cancel" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}" /> </StackPanel> </Grid> </Window>

注意:<TextBox Grid.Row="0" Grid.Column="1" m:AdornerDecoratorHelper.AdornerLayer="{Binding ElementName=grid}" />
AdornerDecoratorHelper.AdornerLayer是我定义的附加属性,附加属性中指定了一个grid对象,目的是用grid的默认AdornerLayer,在器上面添加自定义装饰器(控件周围的红线)。


4.辅助的样式,为了美观,在App中定义一些默认样式

<Application x:Class="AdornerDecoratorDemo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <Style x:Key="ControlBaseStyle" TargetType="{x:Type Control}"> <Setter Property="Margin" Value="5" /> <Setter Property="Width" Value="180" /> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="VerticalAlignment" Value="Center" /> </Style> <Style TargetType="{x:Type TextBlock}" > <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="Margin" Value="10,0" /> </Style> <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource ControlBaseStyle}" /> <Style TargetType="{x:Type RadioButton}" BasedOn="{StaticResource ControlBaseStyle}"> <Setter Property="Width" Value="Auto" /> </Style> <Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource ControlBaseStyle}"> <Setter Property="Width" Value="Auto" /> </Style> <Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ControlBaseStyle}" /> <Style TargetType="{x:Type Button}" BasedOn="{StaticResource ControlBaseStyle}"> <Setter Property="Width" Value="75" /> </Style> </Application.Resources> </Application>


看看效果吧:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值