-1

I have a row of buttons where I'm using the background to represent a color for a simple color swatch. The issue is when the button is disabled, it overrides the background via the style completely replacing the color with the same gray so not only don't you see the colors, but they all look like placeholders since they don't have any other content.

Here they are all enabled...

enter image description here

And here they all are disabled...

enter image description here

In a perfect world, I'd have hoped they dimmed the background, not removed/overwrote it, but I'd be happy if it simply didn't do anything.

Now I know I can re-style the button's template, but I'm wondering if there's a way to say 'Don't update the visual style based on enabled/disabled' without having to do all of that. Simply dimming them would be appropriate too, but replacing them entirely like that is an issue.

So is there such a thing, or am I going to have to re-template the buttons?

For now, my hack is going to be to remove the command's CanExecute check so it's always enabled, then just do the processing--or not--in the command's Executed handler.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • Have you tried using converter on background property of those buttons and check if it will trigger when can execute changed? – XAMlMAX Dec 08 '20 at 10:24
  • You don't want the behaviour of the default Template, so replace it. Anything else is a hack. – Clemens Dec 08 '20 at 11:28

2 Answers2

0

As you have already said, setting the Background of the button will not work, because the default style and control template will override the background in states like Mouse Over, Pressed or Disabled.

A more lightweight variant than creating a custom template would be to create a Button style that displays the color as Content using a content template. A Border would display the color if the content type is a Brush. A trigger ensures that the Opacity is adapted depending on IsEnabled of the parent Button.

<Style x:Key="ColorSwatchButtonStyle"  TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
   <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
   <Setter Property="VerticalContentAlignment" Value="Stretch"/>
   <Setter Property="ContentTemplate">
      <Setter.Value>
         <DataTemplate DataType="{x:Type Brush}">
            <Border Background="{Binding}">
               <Border.Style>
                  <Style TargetType="{x:Type Border}">
                     <Setter Property="Opacity" Value="1.0"/>
                     <Style.Triggers>
                        <DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="False">
                           <Setter Property="Opacity" Value="0.3"/>
                        </DataTrigger>
                     </Style.Triggers>
                  </Style>
               </Border.Style>
            </Border>
         </DataTemplate>
      </Setter.Value>
   </Setter>
</Style>

If you do not want to put a style in the Border, use data template triggers instead.

<Style x:Key="ColorSwatchButtonStyle"  TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
   <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
   <Setter Property="VerticalContentAlignment" Value="Stretch"/>
   <Setter Property="ContentTemplate">
      <Setter.Value>
         <DataTemplate DataType="{x:Type Brush}">
            <Border x:Name="ColorBorder"  Background="{Binding}"/>
            <DataTemplate.Triggers>
               <DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="False">
                  <Setter TargetName="ColorBorder" Property="Opacity" Value="0.3"/>
               </DataTrigger>
               <DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
                  <Setter TargetName="ColorBorder" Property="Opacity" Value="0.8"/>
               </DataTrigger>
               <DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
                  <Setter TargetName="ColorBorder" Property="Opacity" Value="0.5"/>
               </DataTrigger>
            </DataTemplate.Triggers>
         </DataTemplate>
      </Setter.Value>
   </Setter>
</Style>

Apply the style explicitly or remove the x:Key to make the style implicit and apply it to all buttons that are in scope. Set the color as Content of the Button.

<Button Style="{StaticResource ColorSwatchButtonStyle}"
        Content="{x:Static Brushes.Red}"/>

As a note on this approach, since the content Border occludes the background of the button, you will not see the effects in the aforementioned states. However, you can add triggers to this style to display your colors in the Mouse Over and Pressed states appropriately as well.

<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
   <Setter Property="Opacity" Value="0.8"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" Value="True">
   <Setter Property="Opacity" Value="0.5"/>
</DataTrigger>

This is just an example, of course, you could also change the BorderBrush and BorderThickness or add other elements to the template as well. You are not limited to change Opacity only.


Alternatively, you can always copy the default style and control template using Blend or Visual Studio. Then you can adapt the affected states of the button to display the Background in a different manner.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • This looks really good! Question though... why do you have the nested style against `Border`? Couldn't you just add those triggers in the `ContentTemplate.Triggers` section instead? Just give the border a name, then you can target it directly. – Mark A. Donohoe Dec 09 '20 at 02:04
  • @MarkA.Donohoe You are right, but there is no particular reason, you can do it either way. I have a added an alternative using the data template triggers section. – thatguy Dec 09 '20 at 07:09
  • Awesome. I think it's pretty clever that you templated a `Brush` itself like that. I keep forgetting you can have a `DataTemplate` target *any* type. This really is a cool idea! :) – Mark A. Donohoe Dec 09 '20 at 16:34
0

If you want to disable a Button without changing its appearance, you could set the IsHitTestVisible to false instead of setting the IsEnabled property.

You can then use the Opacity property for the dimming.

The other option would be to create a custom ControlTemplate.

mm8
  • 163,881
  • 10
  • 57
  • 88