devar
devar

Reputation: 95

WPF: Simple MSWord-like color picker button

Is there a quick and easy way to implement a color picker button like the one in MS Word?

I want the user to choose from a number of predefined colors in a dropdown. The selected color should be displayed when the button is closed. When the user clicks the button, an event or command should be triggered. I want to use one button instance to set the text color in a RichTextBox, and another one to set the background color (similar to MS Word).

My first approach was to use a ComboBox with a DataTemplate which contains a button. This does not work: The button's command is not triggered when the user clicks it. Instead, the dropdown of the ComboBox opens. I don't know the exact reason, but maybe the IsHitTestVisible=false attributes in the ComboBox default template? See: http://msdn.microsoft.com/en-us/library/dd334408%28v=vs.95%29.aspx

<UserControl x:Class="MyBrushPicker"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d"              
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Height="Auto" Width="Auto" MinWidth="50">
    <UserControl.Resources>

        <!--
        Provide two different DataTemplates:
        - SelectionBoxTemplate is used to display the selected color when the ComboBox is closed.
          It contains a button which should be clickable (does not work).
        - NormalItemTemplate is used to display a color in the opened ComboBox popup.
        See:
        http://stackoverflow.com/questions/2214696/wpf-how-to-customize-selectionboxitem-in-combobox 
        -->

        <DataTemplate x:Key="NormalItemTemplate">
            <Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
        </DataTemplate>

        <DataTemplate x:Key="SelectionBoxTemplate">
            <!-- This button cannot be clicked: PreviewMouseDown and Button_Click events are never triggered. -->
            <Button Click="Button_Click" PreviewMouseDown="Button_PreviewMouseDown">
                <Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
            </Button>
        </DataTemplate>

        <DataTemplate x:Key="CombinedTemplate">

            <ContentPresenter x:Name="Presenter"
                   Content="{Binding}"                                  
                   ContentTemplate="{StaticResource NormalItemTemplate}" />

            <DataTemplate.Triggers>
                <DataTrigger
                    Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
                    Value="{x:Null}">

                    <Setter TargetName="Presenter" Property="ContentTemplate"
                        Value="{StaticResource SelectionBoxTemplate}" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </UserControl.Resources>

    <ComboBox IsReadOnly="True" IsEditable="False" 
              ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectableBrushes, Mode=OneWay}" 
              SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedBrush, Mode=TwoWay}"
              ScrollViewer.VerticalScrollBarVisibility="Auto"
              MinHeight="50"
              MaxDropDownHeight="200"
              ItemTemplate="{StaticResource CombinedTemplate}"
              >

        <ComboBox.Resources>
            <Style TargetType="ComboBox">
                <Setter Property="ItemsPanel">
                    <Setter.Value>
                        <ItemsPanelTemplate>
                            <WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="200" />
                        </ItemsPanelTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style TargetType="ComboBoxItem">
                <Setter Property="Width" Value="50" />
                <Setter Property="Height" Value="50" />
            </Style>
        </ComboBox.Resources>

    </ComboBox>
</UserControl>

Is there a way to make the button work correctly? Are there any better approaches?

EDIT: Using the Snoop tool revealed that the IsHitTestVisible attribute of the unnamed ContentPresenter inside the ComboBox is actually set to false (ValueSource: ParentTemplate). If I set this property to true using Snoop, the button becomes clickable.

Can I change this property from a style? At least, the following doesn't work:

<ComboBox.Resources>
    <Style TargetType="ContentPresenter">
        <Setter Property="IsHitTestVisible" Value="True" />
    </Style>
</ComboBox.Resources>

Upvotes: 1

Views: 1531

Answers (1)

Tgeo
Tgeo

Reputation: 153

Assuming you are not just doing this as a learning exercise (which make using a library pointless)...

Take a look at: Extended WPF Toolkit - Color Picker

I've used it before for simple a plug-and-play WPF color-picker and it should solve your problem nicely.

Upvotes: 1

Related Questions