65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF:Windows 7 式按钮颜色热跟踪

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.26/5 (11投票s)

2009年10月22日

CPOL

1分钟阅读

viewsIcon

52020

downloadIcon

1026

一个可以应用到您的代码中的很棒的想法。

引言

几天前,我读到我好朋友 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 日:文章已更新
© . All rights reserved.