4
votes

I have managed to get further with my read only check box after a bit of a break and now have the functionality I want in a reasonably elegant form. The problem is I have used a bit of a hack to make it work, although this is not a disaster it would be nice to do it better.

To recap: I want a regular looking checkbox that does not self check when it is clicked, instead the click event triggers a background worker that later on causes a variable to be updated. This variable is bound to checkbox.ischecked and it is then updated with the new value.

I would like to use a control template based on the idea here:

A read-only CheckBox in C# WPF

I have modified this and stripped out stuff I thought I didn't need (perhaps unwisely) and ended up with:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
<!-- -->
<Style x:Key="ReadOnlyCheckBoxStyle" TargetType="{x:Type CheckBox}" >
        <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type CheckBox}">
                <BulletDecorator SnapsToDevicePixels="true" Background="Transparent">
                    <BulletDecorator.Bullet>
                        <Microsoft_Windows_Themes:BulletChrome Background="{TemplateBinding Background}"
                                                               BorderBrush="{TemplateBinding BorderBrush}"
                                                               RenderMouseOver="{TemplateBinding IsMouseOver}"
                                                               IsChecked="{TemplateBinding Tag}">
                        </Microsoft_Windows_Themes:BulletChrome>
                    </BulletDecorator.Bullet>
                    <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                      Margin="{TemplateBinding Padding}"
                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                      RecognizesAccessKey="True" />
                </BulletDecorator>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

This checkbox works as described above and I call it like this:

<CheckBox x:Name="uiComboBox" Content="Does not set the backing property, but responds to it." 
                  Style="{StaticResource ReadOnlyCheckBoxStyle}" Tag="{Binding MyBoolean}" Click="uiComboBox_Click"/>

The hack I made was to use the 'Tag' DependencyProperty to carry the data binding into the control template. This bypasses whatever mechanism is normally causing the checkbox to self check. To revert to a normal acting checkbox just change binding to Tag to a binding to IsChecked and inside the BulletDecorator set the TemplateBinding to IsChecked instead of Tag.

So I guess my questions are:

  1. Have I got the wrong end of the stick? Is there a place where I can override whatever mechanism causes the box to self check? Perhaps in ControlTemplate Triggers?
  2. Is it a good idea to go around eliminating any spare XAML that I think is just being brought in from the default CheckBox or should I try and keep a complete replacement for all styles?
  3. If what I am doing is not too crazy, can I add a dependency property in XAML so that I don't have to use the Tag property?
  4. It also occurs to me that perhaps what I really want is a button control that looks like a checkbox, maybe an invisible button with the usual animated checkbox on top which I bind data to the graphic of. Any thoughts on that plan would also be very welcome.

Thanks very much

Ed

1

1 Answers

6
votes

I managed to sort out this problem and my ReadOnlyCheckBox idea, in the end I created a custom control based around Button and then applied a style to make it look like a CheckBox. I added my own IsChecked property that does not get set when the user clicks but is bound to the data so the displayed check only appears when the data changes.

C#:

    public class ReadOnlyCheckBoxControl : System.Windows.Controls.Button
{
    public static DependencyProperty IsCheckedProperty;

    public ReadOnlyCheckBoxControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ReadOnlyCheckBoxControl), new FrameworkPropertyMetadata(typeof(ReadOnlyCheckBoxControl)));
    }

    public bool IsChecked
    {
        get { return (bool)GetValue(IsCheckedProperty); }
        set { SetValue(IsCheckedProperty, value); }
    }

    static ReadOnlyCheckBoxControl()
    {
        IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(ReadOnlyCheckBoxControl));
    }
}

XAML:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:ReadOnlyCheckBoxControlNS;assembly="
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">

<SolidColorBrush x:Key="CheckBoxFillNormal" Color="#F4F4F4" />
<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F" />

<Style x:Key="EmptyCheckBoxFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle SnapsToDevicePixels="true"
                           Margin="1"
                           Stroke="Black"
                           StrokeDashArray="1 2"
                           StrokeThickness="1" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="CheckRadioFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle SnapsToDevicePixels="true"
                           Margin="14,0,0,0"
                           Stroke="Black"
                           StrokeDashArray="1 2"
                           StrokeThickness="1" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="{x:Type y:ReadOnlyCheckBoxControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type y:ReadOnlyCheckBoxControl}">
                <BulletDecorator SnapsToDevicePixels="true" Background="Transparent">
                    <BulletDecorator.Bullet>
                        <Microsoft_Windows_Themes:BulletChrome Background="{StaticResource CheckBoxFillNormal}"
                                                               BorderBrush="{StaticResource CheckBoxStroke}"
                                                               RenderMouseOver="{TemplateBinding IsMouseOver}"
                                                               IsChecked="{TemplateBinding IsChecked}">
                        </Microsoft_Windows_Themes:BulletChrome>
                    </BulletDecorator.Bullet>
                    <ContentPresenter SnapsToDevicePixels="True"
                                      HorizontalAlignment="Left"
                                      Margin="4,0,0,0"
                                      VerticalAlignment="Center"
                                      RecognizesAccessKey="True" />
                </BulletDecorator>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasContent" Value="true">
                        <Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}" />
                        <Setter Property="Padding" Value="4,0,0,0" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>