Sunday, June 23, 2013

Set Brush for ScrollViewer Thumb

In WPF/Silverlight ScrollViewer and ScrollBar control doesn't have any property to change color of Thumb dynamically from XAML by setting property. We have to edit the Style of ScrollBar and set the Brush for it according to our requirement. Based on requirement of one of the user on MSDN Forum I decided to create a Custom ScrollViewer which has a property to set Brush for Thumb.

Public Class CustomScrollViewer
    Inherits ScrollViewer
  
    Shared Sub New()
        DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomScrollViewer), New FrameworkPropertyMetadata(GetType(CustomScrollViewer)))
    End Sub
  
    Public Property ScrollBarThumbBrush As Brush
        Get
            Return GetValue(ScrollBarThumbBrushProperty)
        End Get
  
        Set(ByVal value As Brush)
            SetValue(ScrollBarThumbBrushProperty, value)
        End Set
    End Property
  
    Public Shared ReadOnly ScrollBarThumbBrushProperty As DependencyProperty = DependencyProperty.Register("ScrollBarThumbBrush", GetType(Brush), GetType(CustomScrollBar), New PropertyMetadata(Brushes.Gray))
  
End Class


In the above class, I have created a ScrollBarThumbBrush property which we can use to set Brush for Thumb in ScrollBar. And following in the XAML style for CustomScrollViewer control

<Style TargetType="my:CustomScrollViewer">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="my:CustomScrollViewer">
                <Grid x:Name="Grid" Background="{TemplateBinding Background}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
                    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
                    <ScrollBar x:Name="PART_VerticalScrollBar"   AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
                    <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now to allow Thumb change color dynamically we need to change the Style of ScrollBar and read ScrollBarThumbBrush property. As you can see in above XAML Style CustomScrollViewer contains a ScrollBar control, so CustomScrollViewer is Ancestor of ScrollBar. So we are going to use following binding code to read ScrollBarThumbBrush property value in ScrollBar.

Fill="{Binding Path=ScrollBarThumbBrush, RelativeSource={RelativeSource AncestorType=my:CustomScrollViewer}}"

Below is the complete Style of ScrollBar control

<Style TargetType="ScrollBar">
    <Setter Property="Stylus.IsPressAndHoldEnabled" Value="False"/>
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
    <Setter Property="Background" Value="#FFF0F0F0"/>
    <Setter Property="BorderBrush" Value="#FFF0F0F0"/>
    <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="ScrollBar">
                <Grid x:Name="Bg" SnapsToDevicePixels="True">
                    <Grid.RowDefinitions>
                        <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
                        <RowDefinition Height="1E-05*"/>
                        <RowDefinition MaxHeight="{DynamicResource {x:Static SystemParameters.VerticalScrollBarButtonHeightKey}}"/>
                    </Grid.RowDefinitions>
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Row="1"/>
                    <RepeatButton x:Name="PART_LineUpButton" Command="ScrollBar.LineUpCommand" IsEnabled="{TemplateBinding IsMouseOver}">
                        <RepeatButton.Style>
                            <Style TargetType="{x:Type RepeatButton}">
                                <Setter Property="FocusVisualStyle">
                                    <Setter.Value>
                                        <Style>
                                            <Setter Property="Control.Template">
                                                <Setter.Value>
                                                    <ControlTemplate>
                                                        <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </Setter.Value>
                                </Setter>
                                <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" BorderBrush="#FFF0F0F0" BorderThickness="1" Background="#FFF0F0F0" SnapsToDevicePixels="True">
                                                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" 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="#FFDADADA"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FFDADADA"/>
                                                </Trigger>
                                                <Trigger Property="IsPressed" Value="True">
                                                    <Setter Property="Background" TargetName="border" Value="#FF606060"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FF606060"/>
                                                </Trigger>
                                                <Trigger Property="IsEnabled" Value="False">
                                                    <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
                                                    <Setter Property="Background" TargetName="border" Value="#FFF0F0F0"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FFF0F0F0"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </RepeatButton.Style>
                        <Path x:Name="ArrowTop" Data="M0,4C0,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,4z" Fill="#FF606060" Margin="3,4,3,3" Stretch="Uniform"/>
                    </RepeatButton>
                    <Track x:Name="PART_Track" IsDirectionReversed="True" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="1">
                        <Track.DecreaseRepeatButton>
                            <RepeatButton Command="ScrollBar.PageUpCommand">
                                <RepeatButton.Style>
                                    <Style 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>
                                </RepeatButton.Style>
                            </RepeatButton>
                        </Track.DecreaseRepeatButton>
                        <Track.IncreaseRepeatButton>
                            <RepeatButton Command="ScrollBar.PageDownCommand">
                                <RepeatButton.Style>
                                    <Style 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>
                                </RepeatButton.Style>
                            </RepeatButton>
                        </Track.IncreaseRepeatButton>
                        <Track.Thumb>
                            <Thumb>
                                <Thumb.Style>
                                    <Style 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="{Binding Path=ScrollBarThumbBrush, RelativeSource={RelativeSource AncestorType=my:CustomScrollViewer}}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
                                                    <ControlTemplate.Triggers>
                                                        <Trigger Property="IsMouseOver" Value="True">
                                                            <Setter Property="Fill" TargetName="rectangle" Value="#FFA6A6A6"/>
                                                        </Trigger>
                                                        <Trigger Property="IsDragging" Value="True">
                                                            <Setter Property="Fill" TargetName="rectangle" Value="#FF606060"/>
                                                        </Trigger>
                                                    </ControlTemplate.Triggers>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Thumb.Style>
                            </Thumb>
                        </Track.Thumb>
                    </Track>
                    <RepeatButton x:Name="PART_LineDownButton" Command="ScrollBar.LineDownCommand" IsEnabled="{TemplateBinding IsMouseOver}" Grid.Row="2">
                        <RepeatButton.Style>
                            <Style TargetType="{x:Type RepeatButton}">
                                <Setter Property="FocusVisualStyle">
                                    <Setter.Value>
                                        <Style>
                                            <Setter Property="Control.Template">
                                                <Setter.Value>
                                                    <ControlTemplate>
                                                        <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </Setter.Value>
                                </Setter>
                                <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" BorderBrush="#FFF0F0F0" BorderThickness="1" Background="#FFF0F0F0" SnapsToDevicePixels="True">
                                                <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" 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="#FFDADADA"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FFDADADA"/>
                                                </Trigger>
                                                <Trigger Property="IsPressed" Value="True">
                                                    <Setter Property="Background" TargetName="border" Value="#FF606060"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FF606060"/>
                                                </Trigger>
                                                <Trigger Property="IsEnabled" Value="False">
                                                    <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
                                                    <Setter Property="Background" TargetName="border" Value="#FFF0F0F0"/>
                                                    <Setter Property="BorderBrush" TargetName="border" Value="#FFF0F0F0"/>
                                                </Trigger>
                                            </ControlTemplate.Triggers>
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </Style>
                        </RepeatButton.Style>
                        <Path x:Name="ArrowBottom" Data="M0,2.5C0,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.5z" Fill="#FF606060" 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="White"/>
                    </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="White"/>
                    </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="Black"/>
                    </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="Black"/>
                    </MultiDataTrigger>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Fill" TargetName="ArrowTop" Value="#FFBFBFBF"/>
                        <Setter Property="Fill" TargetName="ArrowBottom" Value="#FFBFBFBF"/>
                    </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="1E-05*"/>
                                <ColumnDefinition MaxWidth="{DynamicResource {x:Static SystemParameters.HorizontalScrollBarButtonWidthKey}}"/>
                            </Grid.ColumnDefinitions>
                            <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1"/>
                            <RepeatButton x:Name="PART_LineLeftButton" Command="ScrollBar.LineLeftCommand" IsEnabled="{TemplateBinding IsMouseOver}">
                                <RepeatButton.Style>
                                    <Style TargetType="{x:Type RepeatButton}">
                                        <Setter Property="FocusVisualStyle">
                                            <Setter.Value>
                                                <Style>
                                                    <Setter Property="Control.Template">
                                                        <Setter.Value>
                                                            <ControlTemplate>
                                                                <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                            </ControlTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </Style>
                                            </Setter.Value>
                                        </Setter>
                                        <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" BorderBrush="#FFF0F0F0" BorderThickness="1" Background="#FFF0F0F0" SnapsToDevicePixels="True">
                                                        <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" 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="#FFDADADA"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FFDADADA"/>
                                                        </Trigger>
                                                        <Trigger Property="IsPressed" Value="True">
                                                            <Setter Property="Background" TargetName="border" Value="#FF606060"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FF606060"/>
                                                        </Trigger>
                                                        <Trigger Property="IsEnabled" Value="False">
                                                            <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
                                                            <Setter Property="Background" TargetName="border" Value="#FFF0F0F0"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FFF0F0F0"/>
                                                        </Trigger>
                                                    </ControlTemplate.Triggers>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </RepeatButton.Style>
                                <Path x:Name="ArrowLeft" Data="M3.18,7C3.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,7z" Fill="#FF606060" Margin="3" Stretch="Uniform"/>
                            </RepeatButton>
                            <Track x:Name="PART_Track" Grid.Column="1" IsEnabled="{TemplateBinding IsMouseOver}">
                                <Track.DecreaseRepeatButton>
                                    <RepeatButton Command="ScrollBar.PageLeftCommand">
                                        <RepeatButton.Style>
                                            <Style 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>
                                        </RepeatButton.Style>
                                    </RepeatButton>
                                </Track.DecreaseRepeatButton>
                                <Track.IncreaseRepeatButton>
                                    <RepeatButton Command="ScrollBar.PageRightCommand">
                                        <RepeatButton.Style>
                                            <Style 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>
                                        </RepeatButton.Style>
                                    </RepeatButton>
                                </Track.IncreaseRepeatButton>
                                <Track.Thumb>
                                    <Thumb>
                                        <Thumb.Style>
                                            <Style 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="{Binding Path=ScrollBarThumbBrush, RelativeSource={RelativeSource AncestorType=my:CustomScrollViewer}}" Height="{TemplateBinding Height}" SnapsToDevicePixels="True" Width="{TemplateBinding Width}"/>
                                                            <ControlTemplate.Triggers>
                                                                <Trigger Property="IsMouseOver" Value="True">
                                                                    <Setter Property="Fill" TargetName="rectangle" Value="#FFA6A6A6"/>
                                                                </Trigger>
                                                                <Trigger Property="IsDragging" Value="True">
                                                                    <Setter Property="Fill" TargetName="rectangle" Value="#FF606060"/>
                                                                </Trigger>
                                                            </ControlTemplate.Triggers>
                                                        </ControlTemplate>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </Thumb.Style>
                                    </Thumb>
                                </Track.Thumb>
                            </Track>
                            <RepeatButton x:Name="PART_LineRightButton" Grid.Column="2" Command="ScrollBar.LineRightCommand" IsEnabled="{TemplateBinding IsMouseOver}">
                                <RepeatButton.Style>
                                    <Style TargetType="{x:Type RepeatButton}">
                                        <Setter Property="FocusVisualStyle">
                                            <Setter.Value>
                                                <Style>
                                                    <Setter Property="Control.Template">
                                                        <Setter.Value>
                                                            <ControlTemplate>
                                                                <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                            </ControlTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </Style>
                                            </Setter.Value>
                                        </Setter>
                                        <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" BorderBrush="#FFF0F0F0" BorderThickness="1" Background="#FFF0F0F0" SnapsToDevicePixels="True">
                                                        <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" 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="#FFDADADA"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FFDADADA"/>
                                                        </Trigger>
                                                        <Trigger Property="IsPressed" Value="True">
                                                            <Setter Property="Background" TargetName="border" Value="#FF606060"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FF606060"/>
                                                        </Trigger>
                                                        <Trigger Property="IsEnabled" Value="False">
                                                            <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
                                                            <Setter Property="Background" TargetName="border" Value="#FFF0F0F0"/>
                                                            <Setter Property="BorderBrush" TargetName="border" Value="#FFF0F0F0"/>
                                                        </Trigger>
                                                    </ControlTemplate.Triggers>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </RepeatButton.Style>
                                <Path x:Name="ArrowRight" Data="M1.81,7C1.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,7z" Fill="#FF606060" 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="White"/>
                            </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="White"/>
                            </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="Black"/>
                            </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="Black"/>
                            </MultiDataTrigger>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Fill" TargetName="ArrowLeft" Value="#FFBFBFBF"/>
                                <Setter Property="Fill" TargetName="ArrowRight" Value="#FFBFBFBF"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Trigger>
    </Style.Triggers>
</Style>

Now we can use the CustomScrollViewer control and set ScrollBarThumbBrush property as shown in following code in the project where we need Thumb color as per our requirement.

<my:CustomScrollViewer ScrollBarThumbBrush="Blue"  HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
     <Grid Width="600" Height="600"/>
</my:CustomScrollViewer>

Monday, May 6, 2013

MultiColumn ComboBox in WPF DataGrid

Recently one of the user on MSDN forum wanted to have MultiColumn ComboBox in WPF DataGrid. Although there are many articles for creating Simple MultiColumn Combobox but not many article for having it inside Datagrid. So I decided to write a small snippet which can work for Datagrid.
 
DataGrid has DataGridTemplateColumn where we can host a Control. So to show Multi Column ComboBox in dropdown area, we can change the style of ComboBox as in below code.
<DataGrid x:Name="DGOrders" Margin="30" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding OrderID}" Header="Order ID" />
<DataGridTemplateColumn Header="User" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding UserID}" SelectedValuePath="UserID" DisplayMemberPath="CompanyName" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}" >
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="5" Grid.Column="0" Text="{Binding UserID}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding CompanyName}"/>
<TextBlock Margin="5" Grid.Column="2" Text="{Binding UserName}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>






In this example, we are changing the style by setting ItemContainerStyle property of ComboBox. So now when dropdown for ComboBox will get open, we will see UserID, CompanyName and UserName details. Here ComboBox selection will show what we have set in DisplayMemberPath like in following image

Example1

Another way to have MultiColumn is to change ItemTemplate of ComboBox and display result from multiple columns.

        <DataGrid x:Name="DGOrders" Margin="30" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding OrderID}" Header="Order ID" />
<DataGridTemplateColumn Header="User" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding UserID}" SelectedValuePath="UserID" HorizontalContentAlignment="Stretch" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

<TextBlock Margin="5" Grid.Column="0" Text="{Binding UserID}"/>
<TextBlock Margin="5" Grid.Column="1" Text="{Binding CompanyName}"/>
<TextBlock Margin="5" Grid.Column="2" Text="{Binding UserName}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>




In this example, we are changing the ItemTemplate, so now we will see UserID, CompanyName and UserName details in ComboBox Selection and Dropdown result as in following image.

Example2

Now when you have a requirement of showing Multiple Column in DataGridColumn you can try one of the above two methods.

Another way to show Multiple Column in DataGridColumn is to host a DataGrid in DataGridTemplateColumn. I will have an example of it in one of my future articles.

Tuesday, October 16, 2012

Microsoft Community Contributor

Contributor

I am proud to announce that I am recognized with Microsoft Community Contributor badge by Microsoft for my contribution in MSDN Forum. I have answered above 2000 answers with 29K points on MSDN Forum so far. Microsoft Community Contributor is a quarterly recognition by Microsoft.

Capture

More information about Microsoft Community Contributor is available on following link

https://www.microsoftcommunitycontributor.com/faq.aspx

Sunday, October 7, 2012

MultiPanel Control in WPF/Silverlight

Many time during designing we require different pages similar to a TabControl but without displaying Tabs. There are few article to create similar control for Windows Form and ASP.NET but not much for WPF or Silverlight. So I decide the create my own Custom MultiPanel.
MultiPanel is a simple control which inherits an ItemsControl and has SelectedIndex and SelectedPanel property to change the selected panel.
Public Class MultiPanel
    Inherits ItemsControl

    Public Sub New()
        MyBase.New()
        Me.DefaultStyleKey = GetType(MultiPanel)
    End Sub

    Public Property SelectedIndex() As Integer
        Get
            Return CInt(GetValue(SelectedIndexProperty))
        End Get

        Set(value As Integer)
            SetValue(SelectedIndexProperty, value)
        End Set
    End Property

    Public Event SelectionChanged As EventHandler(Of EventArgs)

    Public Shared ReadOnly SelectedIndexProperty As DependencyProperty = DependencyProperty.Register("SelectedIndex", GetType(Integer), GetType(MultiPanel), New PropertyMetadata(-1, New PropertyChangedCallback(AddressOf SelectedIndexChanged)))

    Public Property SelectedPanel() As Object
        Get
            Return GetValue(SelectedPanelProperty)
        End Get

        Set(value As Object)
            SetValue(SelectedPanelProperty, value)
        End Set
    End Property


    Public Shared ReadOnly SelectedPanelProperty As DependencyProperty = DependencyProperty.Register("SelectedPanel", GetType(Object), GetType(MultiPanel), New PropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf SelectedPanelChanged)))

    Private Shared Sub SelectedIndexChanged(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim mp As MultiPanel = DirectCast(sender, MultiPanel)
        Dim index As Integer = Integer.Parse(e.NewValue.ToString())
        Dim oldIndex As Integer = Integer.Parse(e.OldValue.ToString())

        If index <> -1 AndAlso index <> oldIndex Then
            mp.SelectedPanel = mp.Items(index)
        ElseIf index = -1 Then
            mp.SelectedPanel = Nothing
        End If

        mp.RaiseSelectionChanged()
    End Sub

    Private Sub RaiseSelectionChanged()
        RaiseEvent SelectionChanged(Me, New EventArgs())
    End Sub

    Private Shared Sub SelectedPanelChanged(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim mp As MultiPanel = DirectCast(sender, MultiPanel)
        Dim pnl As Panel = DirectCast(e.NewValue, Panel)

        If pnl Is Nothing OrElse mp.Items.IndexOf(pnl) = -1 Then
            mp.SelectedPanel = Nothing
            mp.SelectedIndex = -1
        Else
            mp.SelectedPanel = pnl
            mp.SelectedIndex = mp.Items.IndexOf(pnl)
        End If
    End Sub

   
End Class



And below is the MultiPanel Style

    <Style TargetType="local:MultiPanel">
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MultiPanel">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" >

                        <ContentPresenter Content="{TemplateBinding SelectedPanel}"
                                                Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>



Below is a simple XAML Code example of using this control

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:WpfApplication2"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <my:MultiPanel x:Name="MainMultiPanel">
            <my:MultiPanel.Items>
                <Grid Name="grdCustomer">
                    <my:CustomerDetailControl />
                </Grid>
                <Grid Name="grdEmployee">
                    <my:EmployeeDetailControl />
                </Grid>
                <Grid Name="grdOrder">
                    <my:OrderDetailControl />
                </Grid>
            </my:MultiPanel.Items>
        </my:MultiPanel>
    </Grid>
</Window>



Now you can use SelectedIndex or SelectedPanel property of MultiPanel to display the required grid

MainMultiPanel.SelectedIndex = 2


Hope this control help you in your designing task. Look forward to your feedbacks.