GDI+Windows VistaWindows 7Visual Studio 2008XAML设计/图形LINQWindows XP.NET 3.5WPF中级开发Visual StudioWindows.NETC#
WPF:Windows 7 式按钮颜色热跟踪






4.26/5 (11投票s)
一个可以应用到您的代码中的很棒的想法。

引言
几天前,我读到我好朋友 Rudi Grobler 的一篇精彩博文,他在其中解释了如何在 Windows 7 的 Button
上实现颜色悬停跟踪。我评论说这在实现上带来了重大变化,我想与大家分享。我借此机会感谢 Rudi,他不仅是一位优秀的开发人员,也是一个很棒的人。
背景
本文基于这篇博文中描述的实现:让你的 WPF 按钮具有颜色悬停跟踪!
而转换器的想法来自 Grant Hinkson 在他的文章中:“McGuffin” - 启用图像转换器。
Using the Code
这种技术实际上非常简单。我们创建一个 AttachedProperty
,它将被用来应用包含在其中的元素中最常见颜色的背景画笔。
AttachedProperty
的定义如下
public static readonly DependencyProperty ApplyHotTrackingProperty =
DependencyProperty.RegisterAttached("ApplyHotTracking",
typeof(Boolean),
typeof(Win7ColorHotTrackExtension),
new UIPropertyMetadata(
new PropertyChangedCallback(
(sender, e) =>
{
if ((bool)e.NewValue)
{
(sender as FrameworkElement).PreviewMouseMove +=
new System.Windows.Input.MouseEventHandler(
Win7ColorHotTrackExtenssion_PreviewMouseMove);
(sender as FrameworkElement).Loaded +=
new RoutedEventHandler(Win7ColorHotTrackExtenssion_Loaded);
}
else
(sender as FrameworkElement).PreviewMouseMove -=
Win7ColorHotTrackExtenssion_PreviewMouseMove;
})));
public static Boolean GetApplyHotTracking(DependencyObject sender)
{
return (Boolean)sender.GetValue(Win7ColorHotTrackExtension.ApplyHotTrackingProperty);
}
public static void SetApplyHotTracking(DependencyObject sender, Boolean value)
{
sender.SetValue(Win7ColorHotTrackExtension.ApplyHotTrackingProperty, value);
}
如你所见,PropertyChangedCallback
被定义为匿名方法,并且在这个方法内部,我们有两件事正在被绑定:第一件事是 PreviewMouseMove
,用于在光标移动到 Control
上时进行动画处理,另一件事是加载事件,以便我们可以在视觉对象渲染后执行转换。这些方法定义如下
static void Win7ColorHotTrackExtenssion_Loaded(object sender,
RoutedEventArgs e)
{
// As stated before, it will not work if the element
// is not a content control so an exception is thrown
if (!(sender is ContentControl))
throw new NotSupportedException("This attached property " +
"is just supported by an ContentControl");
var control = sender as ContentControl;
// verify if any data is binded to the Tag property,
// because if it is, we don't want to lose it
if (control.GetValue(FrameworkElement.TagProperty) == null)
{
// Instantiate and Invalidate the VisualBrush
// needed for the analysis of the content
VisualBrush b = new VisualBrush();
b.SetValue(VisualBrush.VisualProperty, control.Content);
control.InvalidateVisual();
// if the control has no visual (with a height lesser or equal to zero)
// we don't need to perform any action,
// because the result will be a transparent brush anyway
if ((control as FrameworkElement).ActualHeight <= 0)
return;
// Render the visual of the element
// to an bitmap with the RenderTargetBitmap class
RenderTargetBitmap RenderBmp = new RenderTargetBitmap(
(int)(control.Content as FrameworkElement).Width,
(int)(control.Content as FrameworkElement).Height,
96,
96,
PixelFormats.Pbgra32);
RenderBmp.Render(b.Visual);
// Set the value to the Tag property
control.SetValue(FrameworkElement.TagProperty, RenderBmp);
// Instantiate and initialize a Binding element to handle the new tag property
Binding bindBG = new Binding("Tag");
bindBG.Source = control;
// Define the converter that will be used to handle
// the transformation from an image to average color
bindBG.Converter = new IconToAvgColorBrushConverter();
// Set the binding to the Background property
control.SetBinding(ContentControl.BackgroundProperty, bindBG);
// if the Background is a LinearGradientBrush
// we also want our control to use the border
// colored as Win7 does
if (control.Background is LinearGradientBrush)
{
Binding bindBorder = new Binding("GradientStops[1].Color");
bindBorder.Source = control.Background;
control.SetBinding(ContentControl.BorderBrushProperty, bindBorder);
}
}
}
static void Win7ColorHotTrackExtenssion_PreviewMouseMove(object sender,
System.Windows.Input.MouseEventArgs e)
{
// As already said the sender must be a Content Control
if (!(sender is ContentControl))
return;
ContentControl element = sender as ContentControl;
// if the Brush is not a linearGradientBrush
// we don't need to do anything so just returns
if (!(element.GetValue(ContentControl.BackgroundProperty)
is LinearGradientBrush))
return;
// Get the brush
LinearGradientBrush b = element.GetValue(ContentControl.BackgroundProperty)
as LinearGradientBrush;
// Get the ActualWidth of the sender
Double refZeroX = (double)element.GetValue(ContentControl.ActualWidthProperty);
// Get the new point for the StartPoint and EndPoint of the Gradient
System.Windows.Point p =
new System.Windows.Point(e.GetPosition(element).X / refZeroX, 1);
// Set the new values
b.StartPoint = new System.Windows.Point(1 - p.X, 0);
b.EndPoint = p;
}
所以现在,我们只需要在 XAML 中使用 AttachedProperty
,如下所示
<Window
x:Class="ColorHotTrackButton.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ColorHotTrackButton"
Title="Color Hot-Track Buttons"
SizeToContent="WidthAndHeight">
<Window.Resources>
<BooleanToVisibilityConverter
x:Key="BooleanToVisibilityConverter"
/>
<LinearGradientBrush
x:Key="GlossGradient"
EndPoint="0.0149999996647239,0.0160000007599592"
StartPoint="0.486000001430511,0.723999977111816"
>
<GradientStop
Color="#0CFFFFFF"
/>
<GradientStop
Color="#4CFFFFFF"
Offset="1"
/>
</LinearGradientBrush>
<local:IconToAvgColorBrushConverter
x:Key="iconToAvgColorBrushConverter"
/>
<Style
x:Key="ColorHotTrackButton"
TargetType="{x:Type Button}"
BasedOn="{x:Null}"
>
<Setter
Property="Background"
Value="#00FFFFFF"
/>
<Setter
Property="BorderBrush"
Value="#00FFFFFF"
/>
<Setter
Property="Template"
>
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Button}"
>
<ControlTemplate.Resources>
<Storyboard
x:Key="GotFocus"
>
<DoubleAnimationUsingKeyFrames
BeginTime="00:00:00"
Storyboard.TargetName="rectangle"
Storyboard.TargetProperty="(UIElement.Opacity)"
>
<SplineDoubleKeyFrame
KeyTime="00:00:00"
Value="0"
/>
<SplineDoubleKeyFrame
KeyTime="00:00:00.3000000"
Value="1"
/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Grid
x:Name="Grid"
ClipToBounds="True"
>
<Border
x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
CornerRadius="3,3,3,3"
Opacity="0.0"
ClipToBounds="True"
/>
<Rectangle
x:Name="rectangle"
RadiusX="3"
RadiusY="3"
Fill="#33FFFFFF"
Opacity="0"
/>
<ContentPresenter
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}"
RecognizesAccessKey="True"
/>
<Path
x:Name="gloss"
Fill="{StaticResource GlossGradient}"
Stretch="Fill"
Margin="2,2,2,29.204"
ClipToBounds="True"
Data="M2.9999995,0 L151,0 C152.65686,1.0728836E-06 154,
1.3431468 154,3.0000018 L154,21.0382 151.53519,
21.193919 C90.378815,
25.365844 36.495198,48.231778 1.1935941,
97.114381 L0,98.795694 0,3.0000018 C4.7683716E-07,
1.3431468 1.3431462,1.0728836E-06 2.9999995,0 z"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger
Property="IsFocused"
Value="True"
>
<Trigger.ExitActions>
<RemoveStoryboard
BeginStoryboardName="GotFocus_BeginStoryboard"
/>
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard
x:Name="GotFocus_BeginStoryboard"
Storyboard="{StaticResource GotFocus}"
/>
</Trigger.EnterActions>
</Trigger>
<Trigger
Property="IsKeyboardFocused"
Value="true"
>
<Setter
Property="BorderBrush"
Value="{DynamicResource DefaultedBorderBrush}"
TargetName="Border"
/>
</Trigger>
<Trigger
Property="IsMouseOver"
Value="true"
>
<Setter
Property="Opacity"
Value="1.0"
TargetName="Border"
/>
</Trigger>
<Trigger
Property="IsEnabled"
Value="true"
/>
<Trigger
Property="IsEnabled"
Value="false"
>
<Setter
Property="Background"
Value="{DynamicResource DisabledBackgroundBrush}"
TargetName="Border"
/>
<Setter
Property="BorderBrush"
Value="{DynamicResource DisabledBorderBrush}"
TargetName="Border"
/>
<Setter
Property="Foreground"
Value="{DynamicResource DisabledForegroundBrush}"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Window.Background>
<LinearGradientBrush
EndPoint="0.5,1"
StartPoint="0.5,0"
>
<GradientStop
Color="Black"
Offset="0.974"
/>
<GradientStop
Color="#FF656565"
/>
</LinearGradientBrush>
</Window.Background>
<DockPanel>
<Border
DockPanel.Dock="Bottom"
>
<StackPanel
Orientation="Horizontal"
MaxHeight="140"
>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/
Assets/Burning Box V2 .ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/
Assets/carte graphique.ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/Assets/
connection réseaux Bagg's.ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/
Assets/favoris'Box.ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/
Assets/lecteur box.ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
<Grid>
<Button
Margin="2.5,0,2.5,0"
Style="{DynamicResource ColorHotTrackButton}"
local:Win7ColorHotTrackExtension.ApplyHotTracking="True"
>
<Image
Source="/ColorHotTrackButton;component/
Assets/private Box.ico"
Width="128"
Height="128"
Margin="12.5,5,12.5,5"
/>
</Button >
</Grid>
</StackPanel>
</Border>
</DockPanel>
</Window>
历史
- 2009 年 10 月 22 日:初始发布
- 2009 年 10 月 27 日:文章已更新