-2

I am new to XAML and WPF and so far I found a lot of good explanations on SO for different things I needed during my work on the project. I come from the web area where I have a lot of experience making graphic elements layouts for web pages using CSS. What really frustrates me is that I have no idea how make some custom nice looking UI in a WPF page, when I get a mock file (as attached).

In particular to this file, I need to place buttons which have a border on the left and an additional square on the right. The color of both depends on the button state. If the button is pressed, the color of the left border and right square should be green (as depicted in the screenshot).

Is there any way similar or at least near to CSS that I coud use to achieve this?

A box on top with text "States". Below there is a list of buttons with a border to the left, text in the center and a square on the right. The first button's border and square are green, the others are blue.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • A repeated layout like that can be done with an itemscontrol or listbox. A listbox adds selector functionality and one option is not to use a button at all. In itemtemplate, use a datatrigger on isselected to change properties – Andy Aug 29 '22 at 17:21

1 Answers1

1

Your general layout suggests that you have a collection of states that you want ot be represented as a list of buttons. These buttons should be togglable and display green when toggled. In this case, an ItemsControl would be suitable, since you do not want or need selection, but interaction with buttons.

For the buttons itself, there is a lot more to do. The visual representation and state are defined in a control template. You can either create a new one from scratch or copy the default control template and adapt it. Here I have created a control template for ToggleButton that reflects your mockup.

<Style x:Key="FocusVisual">
   <Setter Property="Control.Template">
      <Setter.Value>
         <ControlTemplate>
            <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="LawnGreen"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="DarkGreen"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="Black"/>
<SolidColorBrush x:Key="Button.IsChecked.Background" Color="Green"/>
<SolidColorBrush x:Key="Button.IsChecked.Border" Color="Black"/>
<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 x:Key="ToggleButtonStyle" TargetType="{x:Type ToggleButton}">
   <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
   <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}">
            <Grid>
               <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="Auto"/>
                  <ColumnDefinition/>
                  <ColumnDefinition Width="Auto"/>
               </Grid.ColumnDefinitions>
               <Rectangle x:Name="Lr"  Grid.Column="0" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" Width="20" Height="40"/>
               <Border x:Name="Bd" Grid.Column="1" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                  <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
               </Border>
               <Rectangle x:Name="Rr" Grid.Column="2" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}" Width="40" Height="40" Margin="40, 0, 0, 0"/>
            </Grid>
            <ControlTemplate.Triggers>
               <Trigger Property="Button.IsDefaulted" Value="true">
                  <Setter Property="BorderBrush" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
               </Trigger>
               <Trigger Property="IsMouseOver" Value="true">
                  <Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.MouseOver.Background}"/>
                  <Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.MouseOver.Background}"/>
                  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.MouseOver.Border}"/>
                  <Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.MouseOver.Border}"/>
                  <Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.MouseOver.Border}"/>
               </Trigger>
               <Trigger Property="IsPressed" Value="true">
                  <Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.Pressed.Background}"/>
                  <Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.Pressed.Background}"/>
                  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.Pressed.Border}"/>
                  <Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.Pressed.Border}"/>
                  <Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.Pressed.Border}"/>
               </Trigger>
               <Trigger Property="IsChecked" Value="true">
                  <Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.IsChecked.Background}"/>
                  <Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.IsChecked.Background}"/>
                  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.IsChecked.Border}"/>
                  <Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.IsChecked.Border}"/>
                  <Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.IsChecked.Border}"/>
               </Trigger>
               <Trigger Property="IsEnabled" Value="false">
                  <Setter Property="Fill" TargetName="Lr" Value="{StaticResource Button.Disabled.Background}"/>
                  <Setter Property="Fill" TargetName="Rr" Value="{StaticResource Button.Disabled.Background}"/>
                  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Button.Disabled.Border}"/>
                  <Setter Property="Stroke" TargetName="Lr" Value="{StaticResource Button.Disabled.Border}"/>
                  <Setter Property="Stroke" TargetName="Rr" Value="{StaticResource Button.Disabled.Border}"/>
                  <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
               </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
</Style>

In essence, the control template consists of a Grid that host two rectangles for the state color, and a ContentPresenter in between that shows the content. There are triggers for the different states, that set the color of the rectangles accordingly. The toggled state is active when IsChecked is true, IsPressed is active as you are pressing the mouse button on the button.

For more information on how to create custom control templates, please refer to the documentation.

Now you need to create an ItemsControl, bind your collection and add a DataTemplate. The data template defines how the data items are represented, see Data Templating Overview.

<DockPanel>
   <Border DockPanel.Dock="Top"
           BorderBrush="Black"
           BorderThickness="1"
           Margin="0, 0, 0, 40">
      <TextBlock Text="States"
                 TextAlignment="Center"/>
   </Border>
   <ScrollViewer>
      <ItemsControl ItemsSource="{Binding StringItems}">
         <ItemsControl.ItemTemplate>
            <DataTemplate>
               <ToggleButton Content="{Binding}"
                             Style="{StaticResource ToggleButtonStyle}"/>
            </DataTemplate>
         </ItemsControl.ItemTemplate>
      </ItemsControl>
   </ScrollViewer>
</DockPanel>

That is it and this is what the result looks like:

An ItemsControl showing ToggleButtons with their content centered and left and right status rectangles filled with green when toggled.

thatguy
  • 21,059
  • 6
  • 30
  • 40