如何在不重复整个控件模板的情况下将TemplateBindings更改为按钮控件模板中的Bindings

时间:2010-12-13 19:27:43

标签: c# wpf c#-4.0 wpf-controls wpf-4.0

我想将按钮的背景颜色在其正常,moused-over和press状态下更改为各种绿色阴影。我被迫在我的按钮中添加以下怪异的Button.Template,以便我可以修改RenderMouseOverRenderPressed属性以使用Binding代替TemplateBinding,这样我的触发器(在Button.Style中)实际上生效而不是由于TemplateBinding的编译时性质而被忽略。无论如何,对于Aero主题,是否更干净地覆盖这两个属性,而不是完整地重复整个模板绑定? XAML如下:

<Window x:Class="ButtonMouseOver.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Button>
      Hello
      <Button.Template>
        <ControlTemplate TargetType="{x:Type Button}">
          <Microsoft_Windows_Themes:ButtonChrome SnapsToDevicePixels="true" 
            x:Name="Chrome" Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" RenderDefaulted="{TemplateBinding IsDefaulted}" 
            RenderMouseOver="{Binding IsMouseOver}" RenderPressed="{Binding IsPressed}"
            >
            <ContentPresenter Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          RecognizesAccessKey="True"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
          </Microsoft_Windows_Themes:ButtonChrome>
        </ControlTemplate>
      </Button.Template>
      <Button.Style>
        <Style TargetType="Button">
          <Setter Property="Background" Value="LightGreen"/>
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property = "Background" Value="Green"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property = "Foreground" Value="DarkGreen"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </Button.Style>
    </Button>
  </Grid>
</Window>

重要提示:仅对Aero主题需要控制模板。对于其他主题,只需单独使用Button.Style即可完成任务。

2 个答案:

答案 0 :(得分:1)

我能想到的最好的方法是添加一个附加行为,一旦Button加载,就会用反射来禁用它。我不知道你是否喜欢这个比重新模板更好,但你不必包括PresentationFramework.Aero及其可重复使用的

<Button behaviors:DisableButtonChromeBehavior.DisableButtonChrome="True"
        ...>
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Background" Value="LightGreen"/>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Setter Property="Background" Value="Green"/>
                </Trigger>
                <Trigger Property="IsPressed" Value="true">
                    <Setter Property="Foreground" Value="DarkGreen"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

DisableButtonChromeBehavior

public static class DisableButtonChromeBehavior 
{
    public static readonly DependencyProperty DisableButtonChromeProperty = 
        DependencyProperty.RegisterAttached 
        (
            "DisableButtonChrome", 
            typeof(bool),
            typeof(DisableButtonChromeBehavior),
            new UIPropertyMetadata(false, OnDisableButtonChromePropertyChanged) 
        );
    public static bool GetDisableButtonChrome(DependencyObject obj) 
    {
        return (bool)obj.GetValue(DisableButtonChromeProperty); 
    }
    public static void SetDisableButtonChrome(DependencyObject obj, bool value) 
    {
        obj.SetValue(DisableButtonChromeProperty, value); 
    }
    private static void OnDisableButtonChromePropertyChanged(DependencyObject dpo, 
                                                             DependencyPropertyChangedEventArgs e) 
    {
        Button button = dpo as Button;
        if (button != null) 
        { 
            if ((bool)e.NewValue == true) 
            {
                button.Loaded += OnButtonLoaded; 
            } 
            else 
            {
                button.Loaded -= OnButtonLoaded; 
            } 
        } 
    }
    private static void OnButtonLoaded(object sender, RoutedEventArgs e) 
    {
        Button button = sender as Button; 
        Action action = () =>
            {
                object buttonChrome = VisualTreeHelper.GetChild(button, 0);
                Type type = buttonChrome.GetType();
                PropertyInfo propertyInfo = type.GetProperty("RenderMouseOver");
                if (propertyInfo != null)
                {
                    propertyInfo.SetValue(buttonChrome, false, null);
                    propertyInfo = type.GetProperty("RenderPressed");
                    propertyInfo.SetValue(buttonChrome, false, null);
                    propertyInfo = type.GetProperty("RenderDefaulted");
                    propertyInfo.SetValue(buttonChrome, false, null);
                }
            };
        button.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 
    } 
}

<强>更新
还有更短的不可重用方式,只需在后面的代码中执行此操作。

private void Button_Loaded(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    object buttonChrome = VisualTreeHelper.GetChild(button, 0);
    PropertyInfo renderMouseOverInfo = buttonChrome.GetType().GetProperty("RenderMouseOver");
    if (renderMouseOverInfo != null)
    {
        renderMouseOverInfo.SetValue(buttonChrome, false, null);
    }
}

或(如果你不介意包括Aero)

<Button ...>
    <Button.Resources>
        <Style TargetType="Microsoft_Windows_Themes:ButtonChrome">
            <EventSetter Event="Loaded" Handler="ButtonChrome_Loaded"/>
        </Style>
    </Button.Resources>

void ButtonChrome_Loaded(object sender, RoutedEventArgs e)
{
    ButtonChrome buttonChrome = sender as ButtonChrome;
    if (buttonChrome != null)
    {
        buttonChrome.RenderMouseOver = false;
        buttonChrome.RenderPressed = false;
    }
}

除此之外,我认为除了您自己指出的解决方案(重新模板化)之外,您没有任何其他方法。

答案 1 :(得分:0)

如果要使用样式切换背景,则无法像在此处那样显式设置Background属性:

<Button Background="LightGreen">

这将覆盖您设置的任何样式。试试这个:

   <Button.Style>
    <Style TargetType="Button">
      <Setter Property = "Background" Value="LightGreen"/>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="true">
          <Setter Property = "Background" Value="Green"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="true">
          <Setter Property = "Foreground" Value="DarkGreen"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Button.Style>

这应该使你可以摆脱按钮模板,除非你想在其中做更多。

如果您想摆脱Aero废话,请更换模板:

   <Button.Template>
    <ControlTemplate TargetType="{x:Type Button}">
      <Border SnapsToDevicePixels="true" x:Name="Chrome" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
        <ContentPresenter Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
      </Border>
    </ControlTemplate>
  </Button.Template>

这里的缺点是如果你不想要Aero,你就不会得到任何Aero,你必须从头开始使用你的模板。