0

I have this code I found on here, that generates me a grid of buttons - I know this may not be the best way to do this, but for now I'd like to do it this way, I am quite new at this. So the button should be either Black(1) or White(0), depending on it's value. This all works, until I hover the button and I can see it's value. If I just add the Visibility property = "false" to the button, it does not even get displayed.

A grid of black and white buttons where one is hovered that shows a black one digit as content and light blue background with a blue border.

This is my code:

<Window.Resources>
    <DataTemplate x:Key="DataTemplate_Level2">
        <Button Content="{Binding}" Height="15" Width="15" Margin="1,1,1,1">
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding}" Value="1">
                            <Setter Property="Background" Value="Black"/>
                            <Setter Property="Foreground" Value="Black" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding}" Value="0">
                            <Setter Property="Background" Value="White"/>
                            <Setter Property="Foreground" Value="White" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
    </DataTemplate>
    <DataTemplate x:Key="DataTemplate_Level1">
        <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>
</Window.Resources>

And then I display the grid here:

<Grid Margin="10,10,10,-636" Grid.Row="3">
    <ItemsControl x:Name="automata" ItemTemplate="{DynamicResource DataTemplate_Level1}" Margin="0,0,0,-119"/>
</Grid>

I would also be happy of a recommendation on how to display these values in another way.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • Give the button an x:Name="YourButtonName" attribute. That will allow you to change properties in code behind. – Paul Sinnema Mar 12 '22 at 23:14
  • 2
    You can also bind your color to a property in the View Model, and manipulate it from there. Probably a better approach than code-behind. – Robert Harvey Mar 12 '22 at 23:24
  • https://stackoverflow.com/questions/20073294/change-color-of-button-when-mouse-is-over – ASh Mar 13 '22 at 09:17
  • If you don't intend to show the number in any case, remove `Content="{Binding}"`. – emoacht Mar 13 '22 at 09:52
  • The correct solution is to override the template of the Button like Ash suggested. This way you can change the mouse over behavior. Don't use data binding or code-behind for this task. You can even move the existing triggers for the background and foreground to the ControlTemplate. – BionicCode Mar 13 '22 at 10:00

1 Answers1

0

If you want to change the appearance or visual states of a control, create a style with a ControlTemplate that fits your requirements. You can also extract the default style using Blen or Visual Studio and adapt it.

In the following, I took the default style with its control template for Button and commented out the triggers for MouseOver, Pressed and other states so they are ineffective, but you still see where to continue since your question certainly is only the starting point for further customization. I adapted and added your triggers to the ControlTemplate. What this does is simply show a black button for 1 and white for 0.

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
   <Style.Resources>
      <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
      <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
      <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
      <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
      <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
      <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
      <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
      <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
      <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
   </Style.Resources>
   <Setter Property="FocusVisualStyle">
      <Setter.Value>
         <Style>
            <Setter Property="Control.Template">
               <Setter.Value>
                  <ControlTemplate>
                     <Rectangle Margin="2" StrokeDashArray="1 2" SnapsToDevicePixels="true" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                  </ControlTemplate>
               </Setter.Value>
            </Setter>
         </Style>
      </Setter.Value>
   </Setter>
   <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
   <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
   <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
   <Setter Property="BorderThickness" Value="1"/>
   <Setter Property="HorizontalContentAlignment" Value="Center"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
   <Setter Property="Padding" Value="1"/>
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type Button}">
            <Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true">
               <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Border>
            <ControlTemplate.Triggers>

               <!-- These are the default triggers, which you might customize to fit your style. -->
               <!--<Trigger Property="IsDefaulted" Value="true">
                  <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
               </Trigger>
               <Trigger Property="IsMouseOver" Value="true">
                  <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                  <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
               </Trigger>
               <Trigger Property="IsPressed" Value="true">
                  <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                  <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
               </Trigger>
               <Trigger Property="IsEnabled" Value="false">
                  <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                  <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                  <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
               </Trigger>-->

               <!-- These are your triggers, which now act on the Content property of the templated button. -->
               <DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" Value="1">
                  <Setter Property="Background" Value="Black"/>
                  <Setter Property="Foreground" Value="Black" />
               </DataTrigger>
               <DataTrigger Binding="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" Value="0">
                  <Setter Property="Background" Value="White"/>
                  <Setter Property="Foreground" Value="White" />
               </DataTrigger>

            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

Use this style in your data template and you should get the expected result.

<DataTemplate x:Key="DataTemplate_Level2">
   <Button Content="{Binding}" Height="15" Width="15" Margin="1,1,1,1" Style="{DynamicResource MyButtonStyle}"/>
</DataTemplate>

Since you try to make the text 1 and 0 invisible by setting the background and foreground to the same color, you could also simply remove the ContentPresenter from the ControlTemplate, so there is no content displayed at all and the Foreground setters are not needed.

Another thing to note is that your binary state maybe suggests to use a ToggleButton instead.

Base class for controls that can switch states, such as CheckBox.

This is an example style for a ToggleButton that does not show content and sets the background like in your question, but dependening on its IsChecked property.

<Style x:Key="MyToggleButtonStyle" TargetType="{x:Type ToggleButton}">
   <Style.Resources>
      <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
      <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
   </Style.Resources>
   <Setter Property="FocusVisualStyle">
      <Setter.Value>
         <Style>
            <Setter Property="Control.Template">
               <Setter.Value>
                  <ControlTemplate>
                     <Rectangle Margin="2" StrokeDashArray="1 2" SnapsToDevicePixels="true" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                  </ControlTemplate>
               </Setter.Value>
            </Setter>
         </Style>
      </Setter.Value>
   </Setter>
   <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
   <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
   <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
   <Setter Property="BorderThickness" Value="1"/>
   <Setter Property="HorizontalContentAlignment" Value="Center"/>
   <Setter Property="VerticalContentAlignment" Value="Center"/>
   <Setter Property="Padding" Value="1"/>
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type ToggleButton}">
            <Border x:Name="border" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true"/>
            <ControlTemplate.Triggers>
               <Trigger Property="IsChecked" Value="True">
                  <Setter Property="Background" Value="Black"/>
               </Trigger>
               <Trigger Property="IsChecked" Value="False">
                  <Setter Property="Background" Value="White"/>
               </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

Change the binding in your data template from Content to IsChecked.

<DataTemplate x:Key="DataTemplate_Level2">
   <ToggleButton IsChecked="{Binding}" Height="15" Width="15" Margin="1,1,1,1" Style="{DynamicResource MyToggleButtonStyle}"/>
</DataTemplate>

This buys you the advantage of toggling the states automatically, but only if you can provide a property for two-way binding (getter and setter), otherwise an exception will be thrown.

thatguy
  • 21,059
  • 6
  • 30
  • 40