1

I finally made the switch to WPF from winforms, even though I rarely even use winforms. Added a ListBox control and this XAML code in the Main Window to change the color of its ScrollBar Foreground/Background.

<ListBox.Resources>
    <Style x:Key="ScrollBarColor" TargetType="ScrollBar">
        <Setter Property="Background" Value="#FF343434" />
        <Setter Property="Foreground" Value="#FF8E9195" />
    </Style>
</ListBox.Resources>

How would I go about changing these properties programmatically? Also how about in another window and with the ability to alter them at runtime? I've tried numerous approaches using FindResource and Resources.FindName using the key as the arg but no luck so far. Any point in the right direction would be very much appreciated.

Doing this in WinForms would be a bit more time-consuming and more familiar for me, but I'm trying to make that switch.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • If you approach WPF like WinForms you are in for a world of pain. Use xaml when possible as pesky code behind will only bite you back. That style that you have there has a key which needs to referenced through xaml. [Here is an SO example](https://stackoverflow.com/questions/10487285/custom-listbox-scrollbar-style-issue). Good luck – XAMlMAX Apr 07 '21 at 07:25
  • 1
    Ya I'm beginning to realize that lol and thank you for that link, that question was the first I was brought to. It was a bit much xaml so i skimmed over the comments more than the code lol –  Apr 08 '21 at 12:55

1 Answers1

0

How would I go about changing these properties programmatically?

Controls can have multiple states like Disabled, Mouse Over or Pressed which all have different visual representations. Have a look at the style and template documentation for ScollBar.

You can in theory get the ScrollBar of a ListBox in code by searching all children like this:

In your code you can get the ScrollBar and apply a different color to it, but this will not work for all the different states. Of course you could create a custom style to handle all states and assign it here, too.

<ListBox x:Name="MyListBox" ... />
var scrollbar = FindChild<ScrollBar>(MyListBox, null);
scrollbar.Background = Brushes.Red;
scrollbar.Foreground = Brushes.Blue;

However, I do not recommend using code here. Instead you could extract the default style and control template of ScrollBar using Blend or Visual Studio. In this copy of the style, you could change the default colors to your needs (look for SolidColorBrushes in the code below). Then create a ListBox style based on the default style and add the implict styles of the ScrollBar to its Resources section. That is it, the styles for the ScrollBar will be applied automatically to the ListBox scrollbar.

If you do not want the ListBox style to be applied to all ListBoxes in scope, add an x:Key to make it explicit and reference it manually using StaticResource or DynamicResource.

<Style TargetType="{x:Type ListBox}"
       BasedOn="{StaticResource {x:Type ListBox}}">
   <Style.Resources>
      <Style x:Key="FocusVisual">
         <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>
      <SolidColorBrush x:Key="ScrollBar.Static.Background"
                       Color="#F0F0F0" />
      <SolidColorBrush x:Key="ScrollBar.Static.Border"
                       Color="#F0F0F0" />
      <SolidColorBrush x:Key="ScrollBar.Static.Glyph"
                       Color="#606060" />
      <SolidColorBrush x:Key="ScrollBar.Static.Thumb"
                       Color="#CDCDCD" />
      <SolidColorBrush x:Key="ScrollBar.MouseOver.Background"
                       Color="#DADADA" />
      <SolidColorBrush x:Key="ScrollBar.MouseOver.Border"
                       Color="#DADADA" />
      <SolidColorBrush x:Key="ScrollBar.MouseOver.Glyph"
                       Color="#000000" />
      <SolidColorBrush x:Key="ScrollBar.MouseOver.Thumb"
                       Color="#A6A6A6" />
      <SolidColorBrush x:Key="ScrollBar.Pressed.Background"
                       Color="#606060" />
      <SolidColorBrush x:Key="ScrollBar.Pressed.Border"
                       Color="#606060" />
      <SolidColorBrush x:Key="ScrollBar.Pressed.Thumb"
                       Color="#606060" />
      <SolidColorBrush x:Key="ScrollBar.Pressed.Glyph"
                       Color="#FFFFFF" />
      <SolidColorBrush x:Key="ScrollBar.Disabled.Background"
                       Color="#F0F0F0" />
      <SolidColorBrush x:Key="ScrollBar.Disabled.Border"
                       Color="#F0F0F0" />
      <SolidColorBrush x:Key="ScrollBar.Disabled.Glyph"
                       Color="#BFBFBF" />
      <Style x:Key="RepeatButtonTransparent"
             TargetType="{x:Type RepeatButton}">
         <Setter Property="OverridesDefaultStyle"
                 Value="true" />
         <Setter Property="Background"
                 Value="Transparent" />
         <Setter Property="Focusable"
                 Value="false" />
         <Setter Property="IsTabStop"
                 Value="false" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type RepeatButton}">
                  <Rectangle Fill="{TemplateBinding Background}"
                             Height="{TemplateBinding Height}"
                             Width="{TemplateBinding Width}" />
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style x:Key="ScrollBarButton"
             TargetType="{x:Type RepeatButton}">
         <Setter Property="FocusVisualStyle"
                 Value="{StaticResource FocusVisual}" />
         <Setter Property="BorderThickness"
                 Value="1" />
         <Setter Property="HorizontalContentAlignment"
                 Value="Center" />
         <Setter Property="VerticalContentAlignment"
                 Value="Center" />
         <Setter Property="Padding"
                 Value="1" />
         <Setter Property="Focusable"
                 Value="false" />
         <Setter Property="IsTabStop"
                 Value="false" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type RepeatButton}">
                  <Border x:Name="border"
                          Background="{StaticResource ScrollBar.Static.Background}"
                          BorderThickness="1"
                          BorderBrush="{StaticResource ScrollBar.Static.Border}"
                          SnapsToDevicePixels="true">
                     <ContentPresenter x:Name="contentPresenter"
                                       Focusable="False"
                                       HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                       Margin="{TemplateBinding Padding}"
                                       SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                       VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                  </Border>
                  <ControlTemplate.Triggers>
                     <Trigger Property="IsMouseOver"
                              Value="true">
                        <Setter Property="Background"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.MouseOver.Background}" />
                        <Setter Property="BorderBrush"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.MouseOver.Border}" />
                     </Trigger>
                     <Trigger Property="IsPressed"
                              Value="true">
                        <Setter Property="Background"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.Pressed.Background}" />
                        <Setter Property="BorderBrush"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.Pressed.Border}" />
                     </Trigger>
                     <Trigger Property="IsEnabled"
                              Value="false">
                        <Setter Property="Opacity"
                                TargetName="contentPresenter"
                                Value="0.56" />
                        <Setter Property="Background"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.Disabled.Background}" />
                        <Setter Property="BorderBrush"
                                TargetName="border"
                                Value="{StaticResource ScrollBar.Disabled.Border}" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style x:Key="ScrollBarThumbVertical"
             TargetType="{x:Type Thumb}">
         <Setter Property="OverridesDefaultStyle"
                 Value="true" />
         <Setter Property="IsTabStop"
                 Value="false" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type Thumb}">
                  <Rectangle x:Name="rectangle"
                             Fill="{StaticResource ScrollBar.Static.Thumb}"
                             Height="{TemplateBinding Height}"
                             SnapsToDevicePixels="True"
                             Width="{TemplateBinding Width}" />
                  <ControlTemplate.Triggers>
                     <Trigger Property="IsMouseOver"
                              Value="true">
                        <Setter Property="Fill"
                                TargetName="rectangle"
                                Value="{StaticResource ScrollBar.MouseOver.Thumb}" />
                     </Trigger>
                     <Trigger Property="IsDragging"
                              Value="true">
                        <Setter Property="Fill"
                                TargetName="rectangle"
                                Value="{StaticResource ScrollBar.Pressed.Thumb}" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style x:Key="ScrollBarThumbHorizontal"
             TargetType="{x:Type Thumb}">
         <Setter Property="OverridesDefaultStyle"
                 Value="true" />
         <Setter Property="IsTabStop"
                 Value="false" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type Thumb}">
                  <Rectangle x:Name="rectangle"
                             Fill="{StaticResource ScrollBar.Static.Thumb}"
                             Height="{TemplateBinding Height}"
                             SnapsToDevicePixels="True"
                             Width="{TemplateBinding Width}" />
                  <ControlTemplate.Triggers>
                     <Trigger Property="IsMouseOver"
                              Value="true">
                        <Setter Property="Fill"
                                TargetName="rectangle"
                                Value="{StaticResource ScrollBar.MouseOver.Thumb}" />
                     </Trigger>
                     <Trigger Property="IsDragging"
                              Value="true">
                        <Setter Property="Fill"
                                TargetName="rectangle"
                                Value="{StaticResource ScrollBar.Pressed.Thumb}" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
      </Style>
      <Style TargetType="{x:Type ScrollBar}">
         <Setter Property="Stylus.IsPressAndHoldEnabled"
                 Value="false" />
         <Setter Property="Stylus.IsFlicksEnabled"
                 Value="false" />
         <Setter Property="Background"
                 Value="{StaticResource ScrollBar.Static.Background}" />
         <Setter Property="BorderBrush"
                 Value="{StaticResource ScrollBar.Static.Border}" />
         <Setter Property="Foreground"
                 Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
         <Setter Property="BorderThickness"
                 Value="1,0" />
         <Setter Property="Width"
                 Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
         <Setter Property="MinWidth"
                 Value="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type ScrollBar}">
                  <Grid x:Name="Bg"
                        SnapsToDevicePixels="true">
                     <Grid.RowDefinitions>
                        <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}" />
                        <RowDefinition Height="0.00001*" />
                        <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}" />
                     </Grid.RowDefinitions>
                     <Border Background="{TemplateBinding Background}"
                             BorderThickness="{TemplateBinding BorderThickness}"
                             BorderBrush="{TemplateBinding BorderBrush}"
                             Grid.Row="1" />
                     <RepeatButton x:Name="PART_LineUpButton"
                                   Command="{x:Static ScrollBar.LineUpCommand}"
                                   IsEnabled="{TemplateBinding IsMouseOver}"
                                   Style="{StaticResource ScrollBarButton}">
                        <Path x:Name="ArrowTop"
                              Data="M 0,4 C0,4 0,6 0,6 0,6 3.5,2.5 3.5,2.5 3.5,2.5 7,6 7,6 7,6 7,4 7,4 7,4 3.5,0.5 3.5,0.5 3.5,0.5 0,4 0,4 z"
                              Fill="{StaticResource ScrollBar.Static.Glyph}"
                              Margin="3,4,3,3"
                              Stretch="Uniform" />
                     </RepeatButton>
                     <Track x:Name="PART_Track"
                            IsEnabled="{TemplateBinding IsMouseOver}"
                            IsDirectionReversed="true"
                            Grid.Row="1">
                        <Track.DecreaseRepeatButton>
                           <RepeatButton Command="{x:Static ScrollBar.PageUpCommand}"
                                         Style="{StaticResource RepeatButtonTransparent}" />
                        </Track.DecreaseRepeatButton>
                        <Track.IncreaseRepeatButton>
                           <RepeatButton Command="{x:Static ScrollBar.PageDownCommand}"
                                         Style="{StaticResource RepeatButtonTransparent}" />
                        </Track.IncreaseRepeatButton>
                        <Track.Thumb>
                           <Thumb Style="{StaticResource ScrollBarThumbVertical}" />
                        </Track.Thumb>
                     </Track>
                     <RepeatButton x:Name="PART_LineDownButton"
                                   Command="{x:Static ScrollBar.LineDownCommand}"
                                   IsEnabled="{TemplateBinding IsMouseOver}"
                                   Grid.Row="2"
                                   Style="{StaticResource ScrollBarButton}">
                        <Path x:Name="ArrowBottom"
                              Data="M 0,2.5 C0,2.5 0,0.5 0,0.5 0,0.5 3.5,4 3.5,4 3.5,4 7,0.5 7,0.5 7,0.5 7,2.5 7,2.5 7,2.5 3.5,6 3.5,6 3.5,6 0,2.5 0,2.5 z"
                              Fill="{StaticResource ScrollBar.Static.Glyph}"
                              Margin="3,4,3,3"
                              Stretch="Uniform" />
                     </RepeatButton>
                  </Grid>
                  <ControlTemplate.Triggers>
                     <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                           <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}"
                                      Value="true" />
                           <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}"
                                      Value="true" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Fill"
                                TargetName="ArrowBottom"
                                Value="{StaticResource ScrollBar.Pressed.Glyph}" />
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                           <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}"
                                      Value="true" />
                           <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}"
                                      Value="true" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Fill"
                                TargetName="ArrowTop"
                                Value="{StaticResource ScrollBar.Pressed.Glyph}" />
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                           <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineDownButton}"
                                      Value="true" />
                           <Condition Binding="{Binding IsPressed, ElementName=PART_LineDownButton}"
                                      Value="false" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Fill"
                                TargetName="ArrowBottom"
                                Value="{StaticResource ScrollBar.MouseOver.Glyph}" />
                     </MultiDataTrigger>
                     <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                           <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineUpButton}"
                                      Value="true" />
                           <Condition Binding="{Binding IsPressed, ElementName=PART_LineUpButton}"
                                      Value="false" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Fill"
                                TargetName="ArrowTop"
                                Value="{StaticResource ScrollBar.MouseOver.Glyph}" />
                     </MultiDataTrigger>
                     <Trigger Property="IsEnabled"
                              Value="false">
                        <Setter Property="Fill"
                                TargetName="ArrowTop"
                                Value="{StaticResource ScrollBar.Disabled.Glyph}" />
                        <Setter Property="Fill"
                                TargetName="ArrowBottom"
                                Value="{StaticResource ScrollBar.Disabled.Glyph}" />
                     </Trigger>
                  </ControlTemplate.Triggers>
               </ControlTemplate>
            </Setter.Value>
         </Setter>
         <Style.Triggers>
            <Trigger Property="Orientation"
                     Value="Horizontal">
               <Setter Property="Width"
                       Value="Auto" />
               <Setter Property="MinWidth"
                       Value="0" />
               <Setter Property="Height"
                       Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}" />
               <Setter Property="MinHeight"
                       Value="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarHeightKey}}" />
               <Setter Property="BorderThickness"
                       Value="0,1" />
               <Setter Property="Template">
                  <Setter.Value>
                     <ControlTemplate TargetType="{x:Type ScrollBar}">
                        <Grid x:Name="Bg"
                              SnapsToDevicePixels="true">
                           <Grid.ColumnDefinitions>
                              <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}" />
                              <ColumnDefinition Width="0.00001*" />
                              <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}" />
                           </Grid.ColumnDefinitions>
                           <Border Background="{TemplateBinding Background}"
                                   BorderThickness="{TemplateBinding BorderThickness}"
                                   BorderBrush="{TemplateBinding BorderBrush}"
                                   Grid.Column="1" />
                           <RepeatButton x:Name="PART_LineLeftButton"
                                         Command="{x:Static ScrollBar.LineLeftCommand}"
                                         IsEnabled="{TemplateBinding IsMouseOver}"
                                         Style="{StaticResource ScrollBarButton}">
                              <Path x:Name="ArrowLeft"
                                    Data="M 3.18,7 C3.18,7 5,7 5,7 5,7 1.81,3.5 1.81,3.5 1.81,3.5 5,0 5,0 5,0 3.18,0 3.18,0 3.18,0 0,3.5 0,3.5 0,3.5 3.18,7 3.18,7 z"
                                    Fill="{StaticResource ScrollBar.Static.Glyph}"
                                    Margin="3"
                                    Stretch="Uniform" />
                           </RepeatButton>
                           <Track x:Name="PART_Track"
                                  Grid.Column="1"
                                  IsEnabled="{TemplateBinding IsMouseOver}">
                              <Track.DecreaseRepeatButton>
                                 <RepeatButton Command="{x:Static ScrollBar.PageLeftCommand}"
                                               Style="{StaticResource RepeatButtonTransparent}" />
                              </Track.DecreaseRepeatButton>
                              <Track.IncreaseRepeatButton>
                                 <RepeatButton Command="{x:Static ScrollBar.PageRightCommand}"
                                               Style="{StaticResource RepeatButtonTransparent}" />
                              </Track.IncreaseRepeatButton>
                              <Track.Thumb>
                                 <Thumb Style="{StaticResource ScrollBarThumbHorizontal}" />
                              </Track.Thumb>
                           </Track>
                           <RepeatButton x:Name="PART_LineRightButton"
                                         Command="{x:Static ScrollBar.LineRightCommand}"
                                         Grid.Column="2"
                                         IsEnabled="{TemplateBinding IsMouseOver}"
                                         Style="{StaticResource ScrollBarButton}">
                              <Path x:Name="ArrowRight"
                                    Data="M 1.81,7 C1.81,7 0,7 0,7 0,7 3.18,3.5 3.18,3.5 3.18,3.5 0,0 0,0 0,0 1.81,0 1.81,0 1.81,0 5,3.5 5,3.5 5,3.5 1.81,7 1.81,7 z"
                                    Fill="{StaticResource ScrollBar.Static.Glyph}"
                                    Margin="3"
                                    Stretch="Uniform" />
                           </RepeatButton>
                        </Grid>
                        <ControlTemplate.Triggers>
                           <MultiDataTrigger>
                              <MultiDataTrigger.Conditions>
                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}"
                                            Value="true" />
                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}"
                                            Value="true" />
                              </MultiDataTrigger.Conditions>
                              <Setter Property="Fill"
                                      TargetName="ArrowRight"
                                      Value="{StaticResource ScrollBar.Pressed.Glyph}" />
                           </MultiDataTrigger>
                           <MultiDataTrigger>
                              <MultiDataTrigger.Conditions>
                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}"
                                            Value="true" />
                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}"
                                            Value="true" />
                              </MultiDataTrigger.Conditions>
                              <Setter Property="Fill"
                                      TargetName="ArrowLeft"
                                      Value="{StaticResource ScrollBar.Pressed.Glyph}" />
                           </MultiDataTrigger>
                           <MultiDataTrigger>
                              <MultiDataTrigger.Conditions>
                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineRightButton}"
                                            Value="true" />
                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineRightButton}"
                                            Value="false" />
                              </MultiDataTrigger.Conditions>
                              <Setter Property="Fill"
                                      TargetName="ArrowRight"
                                      Value="{StaticResource ScrollBar.MouseOver.Glyph}" />
                           </MultiDataTrigger>
                           <MultiDataTrigger>
                              <MultiDataTrigger.Conditions>
                                 <Condition Binding="{Binding IsMouseOver, ElementName=PART_LineLeftButton}"
                                            Value="true" />
                                 <Condition Binding="{Binding IsPressed, ElementName=PART_LineLeftButton}"
                                            Value="false" />
                              </MultiDataTrigger.Conditions>
                              <Setter Property="Fill"
                                      TargetName="ArrowLeft"
                                      Value="{StaticResource ScrollBar.MouseOver.Glyph}" />
                           </MultiDataTrigger>
                           <Trigger Property="IsEnabled"
                                    Value="false">
                              <Setter Property="Fill"
                                      TargetName="ArrowLeft"
                                      Value="{StaticResource ScrollBar.Disabled.Glyph}" />
                              <Setter Property="Fill"
                                      TargetName="ArrowRight"
                                      Value="{StaticResource ScrollBar.Disabled.Glyph}" />
                           </Trigger>
                        </ControlTemplate.Triggers>
                     </ControlTemplate>
                  </Setter.Value>
               </Setter>
            </Trigger>
         </Style.Triggers>
      </Style>
   </Style.Resources>
</Style>

If there are (for whatever reason) nested scrollbars in the ListBox, the styles would be applied there, too, since they are implicit. If you run into an issue there, extract the full ListBox style and create an explicit style for the contained ScrollViewer that you can customize the same way.

Also how about in another window and with the ability to alter them at runtime? I've tried numerous approaches using FindResource and Resources.FindName [...]

In order to change resources or themes at runtime, you could add the resources in question to a ResourceDicitonary and add it to the applicaiton resource dictionary (in App.xaml). You have to reference the resources using DynamicResource, otherwise they will not be updated when the resources are replaced. Then at runtime you can relace a resource dictionary with another to change styles. See these related questions and answers.

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • What if I used a value as {DynamicResource} for BGColor FGColor? What would need to be added in my second window for it to have access to this resource? I probably shouldn't have decided my first WPF attempt, be a winform port. –  Apr 07 '21 at 17:38