How do you change TemplateBindings to Bindings in a button's control template, without repeating the whole control template

久未见 提交于 2019-12-23 04:52:10

问题


I want to change the background color of a button in its normal, moused-over and pressed states to various shades of green. I am forced to add the following grotesquely verbose Button.Template to my button so that I can modify the RenderMouseOver and RenderPressed attributes to use Binding instead of TemplateBinding, so that my triggers (in the Button.Style) actually take effect instead of being ignored due to the compile time nature of TemplateBinding. Is there anyway to override these two attributes more cleanly for an Aero theme than to repeat the entire template binding in its entirety? XAML follows:

<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>

Important note: The control template is necessary only for the Aero theme. For other themes, just having the Button.Style alone will do the job.


回答1:


The best I can think of is to add an attached behavior which disables this with reflection once the Button has Loaded. I don't know if you like this better than to re-template it, but you won't have to include PresentationFramework.Aero and its re-usable

<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>

The 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); 
    } 
}

Update
There's also the shorter non re-usable way, just doing this in code behind.

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);
    }
}

Or (if you don't mind including 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;
    }
}

Other than that, I think you don't have any other outs than the solution you pointed yourself (re-templating).




回答2:


If you want to use the style to toggle the Background, you can't explicitly set the Background property as you have done here:

<Button Background="LightGreen">

This will override any style you set. Try this instead:

   <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>

This should make it so you can get rid of the button template, unless you want to do more in it.

If you would like to get rid of the Aero nonsense, replace the template like so:

   <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>

The drawback here is if you don't want Aero, you don't get any of it and you have to start from scratch with your template.



来源:https://stackoverflow.com/questions/4432671/how-do-you-change-templatebindings-to-bindings-in-a-buttons-control-template-w

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!