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

16 篇文章 5 订阅
2 篇文章 0 订阅
 

我在上一篇博文中介绍了如何使用自定义装饰器实现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>


看看效果吧:

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值