c# WPF中的只读复选框。

时间:2022-09-11 17:27:32

I am having a tricky problem, I want some slightly unusual behaviour from a checkbox and can't seem to figure it out. Any suggestions would be most welcome. The behaviour I want is:

我有一个棘手的问题,我想从复选框中得到一些不寻常的行为,但似乎无法解决。任何建议都将受到欢迎。我想要的行为是:

  1. The CheckBox is enabled and ready for the user to click, IsChecked represents a bound boolean value stored in a data structure
  2. 启用复选框并为用户单击做好准备,IsChecked表示存储在数据结构中的绑定布尔值
  3. The user clicks the CheckBox causing the click event to fire but the bound value in the data structure is NOT updated and the visual representation of the CheckBox is NOT updated but it is disabled to stop further clicking
  4. 用户单击复选框,导致单击事件触发,但数据结构中的绑定值没有更新,复选框的视觉表示没有更新,但禁用该复选框以停止进一步单击
  5. The click event triggers a message to be sent to a remote device which takes some time to respond
  6. 单击事件触发发送到远程设备的消息,该消息需要一些时间进行响应
  7. The remote device responds causing the data structure to be updated with the new value, the binding then updates the isChecked status and the CheckBox gets reenabled for further clicking
  8. 远程设备响应,使数据结构更新为新的值,然后绑定更新isChecked状态,复选框重新启用以进行进一步单击

The problem I have is that although a OneWay data binding works at not updating the data structure when the CheckBox is clicked, the visual representation does change (which I think is odd, shouldn't IsChecked now act like a pointer to the value in the data structure).

我遇到的问题是,虽然单向数据绑定在单击复选框时没有更新数据结构,但可视表示确实发生了变化(我认为这很奇怪,现在不应该像一个指向数据结构中值的指针一样)。

I can reverse the change in the Click() event and do the disable there as well but this is pretty messy. I can also have the set property of the data structure value to set an isEnabled value which is also bound to reenable the CheckBox but that seems messy too.

我可以逆转Click()事件中的更改,并在那里禁用,但这相当混乱。我还可以设置数据结构值的set属性来设置isEnabled值,这个值也被绑定来重新启用复选框,但看起来也很混乱。

Is there a clean way to do this? Perhaps with a derived CheckBox class? How can I stop the visual representation getting updated?

有干净的方法吗?也许用派生的复选框类?我如何停止视觉表示更新?

Thanks

谢谢

Ed

艾德

9 个解决方案

#1


16  

What about data binding to the IsHitTestVisible property?

如何将数据绑定到IsHitTestVisible属性?

For example, assuming an MVVM approach:

例如,假设MVVM方法:

  1. Add a IsReadOnly property to your view model, initially set as true to allow click.
  2. 向视图模型中添加IsReadOnly属性,初始设置为true以允许单击。
  3. Binding this property to CheckBox.IsHitTestVisible.
  4. 将此属性绑定到CheckBox.IsHitTestVisible。
  5. After the first click, update your view model to set this value to false, preventing any further clicks.
  6. 在第一次单击之后,更新视图模型,将该值设置为false,以防止进一步单击。

I don't have this exact requirement, I just needed an always read only checkbox, and it seems to solve the problem nicely. Also note Goran's comment below about the Focusable property.

我没有这个确切的要求,我只需要一个始终只读的复选框,它似乎很好地解决了问题。还请注意下面Goran关于可聚焦性的评论。

#2


4  

I don't think that creating a whole control for this is necessary. The issue that you're running into comes from the fact that the place where you see 'the check' isn't really the checkbox, it's a bullet. If we look at the ControlTemplate for a CheckBox we can see how that happens (Though I like the Blend template better). As a part of that, even though your binding on the IsChecked property is set to OneWay it is still being updated in the UI, even if it is not setting the binding value.

我不认为为此创建一个完整的控件是必要的。你遇到的问题来自于这样一个事实:你看到“支票”的地方并不是真正的复选框,而是一颗子弹。如果我们查看复选框的ControlTemplate,我们可以看到它是如何发生的(尽管我更喜欢Blend模板)。作为其中的一部分,即使您对IsChecked属性的绑定被设置为单向,它仍然在UI中被更新,即使它没有设置绑定值。

As such, a really simple way to fix this, is to just modify the ControlTemplate for the checkbox in question.

因此,解决这个问题的一种非常简单的方法是,只需修改相关复选框的ControlTemplate。

If we use Blend to grab the control template we can see the Bullet inside the ControlTemplate that represents the actual checkbox area.

如果我们使用Blend来获取控件模板,我们可以看到控件模板中表示实际复选框区域的子弹。

        <BulletDecorator SnapsToDevicePixels="true"
                         Background="Transparent">
            <BulletDecorator.Bullet>
                <Microsoft_Windows_Themes:BulletChrome Background="{TemplateBinding Background}"
                                                       BorderBrush="{TemplateBinding BorderBrush}"
                                                       IsChecked="{TemplateBinding IsChecked}"
                                                       RenderMouseOver="{TemplateBinding IsMouseOver}"
                                                       RenderPressed="{TemplateBinding IsPressed}" />
            </BulletDecorator.Bullet>
            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              RecognizesAccessKey="True" />
        </BulletDecorator>

In here, the IsChecked and RenderPressed are what are actually making the 'Check' appear, so to fix it, we can remove the binding from the IsChecked property on the ComboBox and use it to replace the TemplateBinding on the IsChecked property of the Bullet.

在这里,IsChecked和RenderPressed实际上使“Check”出现,因此,要修复它,我们可以从ComboBox上的IsChecked属性中删除绑定,并使用它替换子弹的IsChecked属性上的TemplateBinding。

Here's a small sample demonstrating the desired effect, do note that to maintain the Vista CheckBox look the PresentationFramework.Aero dll needs to be added to the project.

这里有一个小示例演示了所需的效果,请注意,要维护Vista复选框,请查看PresentationFramework。Aero dll需要添加到项目中。

<Window x:Class="Sample.Window1"
    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"
    Title="Window1"
    Height="300"
    Width="300">
<Window.Resources>
    <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 x:Key="CheckBoxStyle1"
           TargetType="{x:Type CheckBox}">
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
        <Setter Property="Background"
                Value="{StaticResource CheckBoxFillNormal}" />
        <Setter Property="BorderBrush"
                Value="{StaticResource CheckBoxStroke}" />
        <Setter Property="BorderThickness"
                Value="1" />
        <Setter Property="FocusVisualStyle"
                Value="{StaticResource EmptyCheckBoxFocusVisual}" />
        <Setter Property="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}" />
                        </BulletDecorator.Bullet>
                        <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          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="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <StackPanel>
        <CheckBox x:Name="uiComboBox"
                  Content="Does not set the backing property, but responds to it.">
            <CheckBox.Style>
                <Style TargetType="{x:Type CheckBox}">
                    <Setter Property="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="{Binding MyBoolean}">
                                        </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="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="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </CheckBox.Style>
        </CheckBox>

        <TextBlock Text="{Binding MyBoolean, StringFormat=Backing property:{0}}" />

        <CheckBox IsChecked="{Binding MyBoolean}"
                  Content="Sets the backing property." />
    </StackPanel>
</Grid>
</Window>

And the code behind, with our backing Boolean value:

后面的代码,用我们的支持布尔值:

public partial class Window1 : Window, INotifyPropertyChanged
{
    public Window1()
    {
        InitializeComponent();

        this.DataContext = this;
    }
    private bool myBoolean;
    public bool MyBoolean
    {
        get
        {
            return this.myBoolean;
        }
        set
        {
            this.myBoolean = value;
            this.NotifyPropertyChanged("MyBoolean");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    #endregion
}

#3


1  

This is the class I've written to do something similar, for similar reasons (still raises all the Click and Command events as normal, but does not alter the binding source by default and does not auto-toggle. Unfortunately it does still have the animated fade-in-out on click, which is a bit strange if the click-handling code doesn't end up changing IsChecked.

这是我编写的用于做类似事情的类,出于类似的原因(仍然像往常一样提升所有单击和命令事件,但默认情况下不会修改绑定源,也不会自动切换。不幸的是,它仍然会在click上有动画效果,这有点奇怪,如果点击处理代码没有改变ischeck。

public class OneWayCheckBox : CheckBox
{
    private class CancelTwoWayMetadata : FrameworkPropertyMetadata
    {
        protected override void Merge(PropertyMetadata baseMetadata,
                                      DependencyProperty dp)
        {
            base.Merge(baseMetadata, dp);

            BindsTwoWayByDefault = false;
        }
    }

    static OneWayCheckBox()
    {
        // Remove BindsTwoWayByDefault
        IsCheckedProperty.OverrideMetadata(typeof(OneWayCheckBox),
                                           new CancelTwoWayMetadata());
    }

    protected override void OnToggle()
    {
        // Do nothing.
    }
}

Usage:

用法:

<yourns:OneWayCheckBox IsChecked="{Binding SomeValue}"
                       Command="{x:Static yourns:YourApp.YourCommand}"
                       Content="Click me!" />

(Note that the IsChecked binding is now one-way by default; you can declare it as TwoWay if you want, but that would defeat part of the point.)

(注意,IsChecked绑定现在默认是单向的;如果你愿意,你可以把它声明为TwoWay,但这将挫败部分观点。

#4


1  

Late answer, but I just came across the question looking for something else. What you want is not a checkbox with unusual behaviour at all, what you want is a button with unusual appearance. It's always easier to change the appearance of a control than its behaviour. Something along these lines ought to do (untested)

答案很晚,但我只是碰巧遇到了另一个问题。你想要的不是一个行为异常的复选框,你想要的是一个外观异常的按钮。改变控件的外观总是比改变其行为更容易。沿着这些线应该做些什么(未经测试)

<Button Command="{Binding CommandThatStartsTask}">
    <Button.Template>
        <ControlTemplate>
            <CheckBox IsChecked="{Binding PropertySetByTask, Mode=OneWay}" />
        </ControlTemplate>
    </Button.Template>
</Button>

#5


1  

I had a need for the AutoCheck functionality be off as well for a Checkbox/RadioButton, where I wanted to handle the Click event without having the control auto-check. I've tried various solutions here and on other threads and was unhappy with the results.

我需要自动检查功能关闭复选框/RadioButton,在这里我想处理单击事件而不需要控件自动检查。我在这里和其他线程上尝试过各种解决方案,但对结果不满意。

So I dug into what WPF is doing (using Reflection), and I noticed:

所以我深入研究了WPF在做什么(使用反射),我注意到:

  1. Both CheckBox & RadioButton inherit from the ToggleButton control primitive. Neither of them have a OnClick function.
  2. 复选框和RadioButton都继承自ToggleButton控件原语。它们都没有OnClick函数。
  3. The ToggleButton inherits from the ButtonBase control primitive.
  4. ToggleButton继承自ButtonBase控件原语。
  5. The ToggleButton overrides the OnClick function and does: this.OnToggle(); base.OnClick();
  6. ToggleButton重写OnClick函数并执行:this.OnToggle();base.OnClick();
  7. ButtonBase.OnClick does 'base.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, this));'
  8. ButtonBase。OnClick的基地。RaiseEvent(新RoutedEventArgs(ButtonBase。ClickEvent,));

So basically, all I needed to do was override the OnClick event, don't call OnToggle, and do base.RaiseEvent

基本上,我需要做的就是覆盖OnClick事件,不要调用OnToggle,然后做base。raiseevent

Here's the complete code (note that this can easily be reworked to do RadioButtons as well):

以下是完整的代码(注意,可以很容易地对其进行修改,以实现RadioButtons):

using System.Windows.Controls.Primitives;

public class AutoCheckBox : CheckBox
{

    private bool _autoCheck = false;
    public bool AutoCheck {
        get { return _autoCheck; }
        set { _autoCheck = value; }
    }

    protected override void OnClick()
    {
        if (_autoCheck) {
            base.OnClick();
        } else {
            base.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, this));
        }
    }

}

I now have a CheckBox that doesn't auto-check, and still fires the Click event. Plus I can still subscribe to the Checked/Unchecked events and handle things there when I programmatically change the IsChecked property.

我现在有一个复选框,它不会自动选中,仍然会触发单击事件。另外,我还可以订阅已检查/未检查的事件,并在以编程方式更改IsChecked属性时处理这些事件。

One final note: unlike other solutions that do something like IsChecked != IsChecked in a Click event, this won't cause the Checked/Unchecked/Indeterminate events to fire until you programmatically set the IsChecked property.

最后要注意的一点是:与在单击事件中执行IsChecked !=的其他解决方案不同,在您以编程方式设置IsChecked属性之前,不会触发已检查/未检查/不确定事件。

#6


0  

Use Validation to block the boolean from getting toggled when you don't want it to - http://www.codeproject.com/KB/WPF/wpfvalidation.aspx

使用验证来阻止布尔值在您不希望它被选中时获得它——http://www.codeproject.com/kb/wpf/wpfvalid.aspx

This is much less scary than the other answer, or hooking Clicked

这比另一个答案或“勾搭”键要简单得多

#7


0  

I've been trying to create my generic ReadOnlyCheckBox style/template but I'm having a problem with the binding to the data. In the example you bind directly to the data from the ControlTemplate definition, but of course this is not really what I want, as I want to be able to declare the new checkbox something like this:

我一直在尝试创建通用的ReadOnlyCheckBox样式/模板,但对数据绑定有问题。在示例中,您直接绑定到ControlTemplate定义中的数据,但当然这并不是我真正想要的,因为我希望能够声明新的复选框如下:

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

Except of course when I do this and then set the event trigger on the bullet to be a TemplateBinding of IsChecked I have exactly what I started with! I guess I don't understand why setting the binding directly in the bullet is different from setting IsChecked and then binding to that, isn't the TemplateBinding just a way of referencing what is set in the properties of the control being created? How is the Click triggering the UI update even tho the data does not get updated? Is there a trigger for Click I can override to stop the update?

当然,除了当我这样做,然后将子弹上的事件触发器设置为IsChecked的模板绑定时,我就有了我开始的东西!我猜我不明白为什么直接在子弹中设置绑定与设置IsChecked然后绑定到它是不同的,难道TemplateBinding不只是引用被创建控件的属性中的设置吗?即使数据没有更新,点击如何触发UI更新?是否有一个触发点击我可以覆盖以停止更新?

I got all the DictionaryResource stuff working fine so I am happy with that, cheers for the pointer.

我得到了所有的字典资源工作正常,所以我很高兴,为指针欢呼。

The other thing I was curious about was if it is possible to reduce my Control/Style template by using the BasedOn parameter in the style, then I would only override the things I actually need to change rather than declaring a lot of stuff that I think is part of the standard template anyway. I might have a play with this.

另一件事我很好奇如果可以减少我的控制/风格模板使用风格中参数的基础之上,那么我只会覆盖的事情我需要改变,而不是宣布很多东西我认为是标准模板的一部分。我可能会玩这个。

Cheers

干杯

ed

艾德

#8


0  

If it helps anyone, a fast and elegant solution I've found is to hook with the Checked and Unchecked events and manipulate the value based on your flag.

如果它对任何人都有帮助,那么我发现了一种快速而优雅的解决方案,即使用已检查和未检查的事件,并基于您的标志操纵值。

    public bool readOnly = false;

    private bool lastValidValue = false;

    public MyConstructor()
    {
        InitializeComponent();

        contentCheckBox.Checked += new
        RoutedEventHandler(contentCheckBox_Checked);
        contentCheckBox.Unchecked += new
        RoutedEventHandler(contentCheckBox_Checked);
    }

    void contentCheckBox_Checked(object sender, RoutedEventArgs e)
    {
        if (readOnly)
            contentCheckBox.IsChecked = lastValidValue;
        else
            lastValidValue = (bool)contentCheckBox.IsChecked;
    }

#9


-1  

Wrap the CheckBox in a ContentControl. Make the ContentControl IsEnabled=False

在ContentControl中包装复选框。继承自ContentControl IsEnabled使= False

#1


16  

What about data binding to the IsHitTestVisible property?

如何将数据绑定到IsHitTestVisible属性?

For example, assuming an MVVM approach:

例如,假设MVVM方法:

  1. Add a IsReadOnly property to your view model, initially set as true to allow click.
  2. 向视图模型中添加IsReadOnly属性,初始设置为true以允许单击。
  3. Binding this property to CheckBox.IsHitTestVisible.
  4. 将此属性绑定到CheckBox.IsHitTestVisible。
  5. After the first click, update your view model to set this value to false, preventing any further clicks.
  6. 在第一次单击之后,更新视图模型,将该值设置为false,以防止进一步单击。

I don't have this exact requirement, I just needed an always read only checkbox, and it seems to solve the problem nicely. Also note Goran's comment below about the Focusable property.

我没有这个确切的要求,我只需要一个始终只读的复选框,它似乎很好地解决了问题。还请注意下面Goran关于可聚焦性的评论。

#2


4  

I don't think that creating a whole control for this is necessary. The issue that you're running into comes from the fact that the place where you see 'the check' isn't really the checkbox, it's a bullet. If we look at the ControlTemplate for a CheckBox we can see how that happens (Though I like the Blend template better). As a part of that, even though your binding on the IsChecked property is set to OneWay it is still being updated in the UI, even if it is not setting the binding value.

我不认为为此创建一个完整的控件是必要的。你遇到的问题来自于这样一个事实:你看到“支票”的地方并不是真正的复选框,而是一颗子弹。如果我们查看复选框的ControlTemplate,我们可以看到它是如何发生的(尽管我更喜欢Blend模板)。作为其中的一部分,即使您对IsChecked属性的绑定被设置为单向,它仍然在UI中被更新,即使它没有设置绑定值。

As such, a really simple way to fix this, is to just modify the ControlTemplate for the checkbox in question.

因此,解决这个问题的一种非常简单的方法是,只需修改相关复选框的ControlTemplate。

If we use Blend to grab the control template we can see the Bullet inside the ControlTemplate that represents the actual checkbox area.

如果我们使用Blend来获取控件模板,我们可以看到控件模板中表示实际复选框区域的子弹。

        <BulletDecorator SnapsToDevicePixels="true"
                         Background="Transparent">
            <BulletDecorator.Bullet>
                <Microsoft_Windows_Themes:BulletChrome Background="{TemplateBinding Background}"
                                                       BorderBrush="{TemplateBinding BorderBrush}"
                                                       IsChecked="{TemplateBinding IsChecked}"
                                                       RenderMouseOver="{TemplateBinding IsMouseOver}"
                                                       RenderPressed="{TemplateBinding IsPressed}" />
            </BulletDecorator.Bullet>
            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              RecognizesAccessKey="True" />
        </BulletDecorator>

In here, the IsChecked and RenderPressed are what are actually making the 'Check' appear, so to fix it, we can remove the binding from the IsChecked property on the ComboBox and use it to replace the TemplateBinding on the IsChecked property of the Bullet.

在这里,IsChecked和RenderPressed实际上使“Check”出现,因此,要修复它,我们可以从ComboBox上的IsChecked属性中删除绑定,并使用它替换子弹的IsChecked属性上的TemplateBinding。

Here's a small sample demonstrating the desired effect, do note that to maintain the Vista CheckBox look the PresentationFramework.Aero dll needs to be added to the project.

这里有一个小示例演示了所需的效果,请注意,要维护Vista复选框,请查看PresentationFramework。Aero dll需要添加到项目中。

<Window x:Class="Sample.Window1"
    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"
    Title="Window1"
    Height="300"
    Width="300">
<Window.Resources>
    <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 x:Key="CheckBoxStyle1"
           TargetType="{x:Type CheckBox}">
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
        <Setter Property="Background"
                Value="{StaticResource CheckBoxFillNormal}" />
        <Setter Property="BorderBrush"
                Value="{StaticResource CheckBoxStroke}" />
        <Setter Property="BorderThickness"
                Value="1" />
        <Setter Property="FocusVisualStyle"
                Value="{StaticResource EmptyCheckBoxFocusVisual}" />
        <Setter Property="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}" />
                        </BulletDecorator.Bullet>
                        <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          Margin="{TemplateBinding Padding}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          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="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <StackPanel>
        <CheckBox x:Name="uiComboBox"
                  Content="Does not set the backing property, but responds to it.">
            <CheckBox.Style>
                <Style TargetType="{x:Type CheckBox}">
                    <Setter Property="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="{Binding MyBoolean}">
                                        </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="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="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </CheckBox.Style>
        </CheckBox>

        <TextBlock Text="{Binding MyBoolean, StringFormat=Backing property:{0}}" />

        <CheckBox IsChecked="{Binding MyBoolean}"
                  Content="Sets the backing property." />
    </StackPanel>
</Grid>
</Window>

And the code behind, with our backing Boolean value:

后面的代码,用我们的支持布尔值:

public partial class Window1 : Window, INotifyPropertyChanged
{
    public Window1()
    {
        InitializeComponent();

        this.DataContext = this;
    }
    private bool myBoolean;
    public bool MyBoolean
    {
        get
        {
            return this.myBoolean;
        }
        set
        {
            this.myBoolean = value;
            this.NotifyPropertyChanged("MyBoolean");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    #endregion
}

#3


1  

This is the class I've written to do something similar, for similar reasons (still raises all the Click and Command events as normal, but does not alter the binding source by default and does not auto-toggle. Unfortunately it does still have the animated fade-in-out on click, which is a bit strange if the click-handling code doesn't end up changing IsChecked.

这是我编写的用于做类似事情的类,出于类似的原因(仍然像往常一样提升所有单击和命令事件,但默认情况下不会修改绑定源,也不会自动切换。不幸的是,它仍然会在click上有动画效果,这有点奇怪,如果点击处理代码没有改变ischeck。

public class OneWayCheckBox : CheckBox
{
    private class CancelTwoWayMetadata : FrameworkPropertyMetadata
    {
        protected override void Merge(PropertyMetadata baseMetadata,
                                      DependencyProperty dp)
        {
            base.Merge(baseMetadata, dp);

            BindsTwoWayByDefault = false;
        }
    }

    static OneWayCheckBox()
    {
        // Remove BindsTwoWayByDefault
        IsCheckedProperty.OverrideMetadata(typeof(OneWayCheckBox),
                                           new CancelTwoWayMetadata());
    }

    protected override void OnToggle()
    {
        // Do nothing.
    }
}

Usage:

用法:

<yourns:OneWayCheckBox IsChecked="{Binding SomeValue}"
                       Command="{x:Static yourns:YourApp.YourCommand}"
                       Content="Click me!" />

(Note that the IsChecked binding is now one-way by default; you can declare it as TwoWay if you want, but that would defeat part of the point.)

(注意,IsChecked绑定现在默认是单向的;如果你愿意,你可以把它声明为TwoWay,但这将挫败部分观点。

#4


1  

Late answer, but I just came across the question looking for something else. What you want is not a checkbox with unusual behaviour at all, what you want is a button with unusual appearance. It's always easier to change the appearance of a control than its behaviour. Something along these lines ought to do (untested)

答案很晚,但我只是碰巧遇到了另一个问题。你想要的不是一个行为异常的复选框,你想要的是一个外观异常的按钮。改变控件的外观总是比改变其行为更容易。沿着这些线应该做些什么(未经测试)

<Button Command="{Binding CommandThatStartsTask}">
    <Button.Template>
        <ControlTemplate>
            <CheckBox IsChecked="{Binding PropertySetByTask, Mode=OneWay}" />
        </ControlTemplate>
    </Button.Template>
</Button>

#5


1  

I had a need for the AutoCheck functionality be off as well for a Checkbox/RadioButton, where I wanted to handle the Click event without having the control auto-check. I've tried various solutions here and on other threads and was unhappy with the results.

我需要自动检查功能关闭复选框/RadioButton,在这里我想处理单击事件而不需要控件自动检查。我在这里和其他线程上尝试过各种解决方案,但对结果不满意。

So I dug into what WPF is doing (using Reflection), and I noticed:

所以我深入研究了WPF在做什么(使用反射),我注意到:

  1. Both CheckBox & RadioButton inherit from the ToggleButton control primitive. Neither of them have a OnClick function.
  2. 复选框和RadioButton都继承自ToggleButton控件原语。它们都没有OnClick函数。
  3. The ToggleButton inherits from the ButtonBase control primitive.
  4. ToggleButton继承自ButtonBase控件原语。
  5. The ToggleButton overrides the OnClick function and does: this.OnToggle(); base.OnClick();
  6. ToggleButton重写OnClick函数并执行:this.OnToggle();base.OnClick();
  7. ButtonBase.OnClick does 'base.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, this));'
  8. ButtonBase。OnClick的基地。RaiseEvent(新RoutedEventArgs(ButtonBase。ClickEvent,));

So basically, all I needed to do was override the OnClick event, don't call OnToggle, and do base.RaiseEvent

基本上,我需要做的就是覆盖OnClick事件,不要调用OnToggle,然后做base。raiseevent

Here's the complete code (note that this can easily be reworked to do RadioButtons as well):

以下是完整的代码(注意,可以很容易地对其进行修改,以实现RadioButtons):

using System.Windows.Controls.Primitives;

public class AutoCheckBox : CheckBox
{

    private bool _autoCheck = false;
    public bool AutoCheck {
        get { return _autoCheck; }
        set { _autoCheck = value; }
    }

    protected override void OnClick()
    {
        if (_autoCheck) {
            base.OnClick();
        } else {
            base.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, this));
        }
    }

}

I now have a CheckBox that doesn't auto-check, and still fires the Click event. Plus I can still subscribe to the Checked/Unchecked events and handle things there when I programmatically change the IsChecked property.

我现在有一个复选框,它不会自动选中,仍然会触发单击事件。另外,我还可以订阅已检查/未检查的事件,并在以编程方式更改IsChecked属性时处理这些事件。

One final note: unlike other solutions that do something like IsChecked != IsChecked in a Click event, this won't cause the Checked/Unchecked/Indeterminate events to fire until you programmatically set the IsChecked property.

最后要注意的一点是:与在单击事件中执行IsChecked !=的其他解决方案不同,在您以编程方式设置IsChecked属性之前,不会触发已检查/未检查/不确定事件。

#6


0  

Use Validation to block the boolean from getting toggled when you don't want it to - http://www.codeproject.com/KB/WPF/wpfvalidation.aspx

使用验证来阻止布尔值在您不希望它被选中时获得它——http://www.codeproject.com/kb/wpf/wpfvalid.aspx

This is much less scary than the other answer, or hooking Clicked

这比另一个答案或“勾搭”键要简单得多

#7


0  

I've been trying to create my generic ReadOnlyCheckBox style/template but I'm having a problem with the binding to the data. In the example you bind directly to the data from the ControlTemplate definition, but of course this is not really what I want, as I want to be able to declare the new checkbox something like this:

我一直在尝试创建通用的ReadOnlyCheckBox样式/模板,但对数据绑定有问题。在示例中,您直接绑定到ControlTemplate定义中的数据,但当然这并不是我真正想要的,因为我希望能够声明新的复选框如下:

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

Except of course when I do this and then set the event trigger on the bullet to be a TemplateBinding of IsChecked I have exactly what I started with! I guess I don't understand why setting the binding directly in the bullet is different from setting IsChecked and then binding to that, isn't the TemplateBinding just a way of referencing what is set in the properties of the control being created? How is the Click triggering the UI update even tho the data does not get updated? Is there a trigger for Click I can override to stop the update?

当然,除了当我这样做,然后将子弹上的事件触发器设置为IsChecked的模板绑定时,我就有了我开始的东西!我猜我不明白为什么直接在子弹中设置绑定与设置IsChecked然后绑定到它是不同的,难道TemplateBinding不只是引用被创建控件的属性中的设置吗?即使数据没有更新,点击如何触发UI更新?是否有一个触发点击我可以覆盖以停止更新?

I got all the DictionaryResource stuff working fine so I am happy with that, cheers for the pointer.

我得到了所有的字典资源工作正常,所以我很高兴,为指针欢呼。

The other thing I was curious about was if it is possible to reduce my Control/Style template by using the BasedOn parameter in the style, then I would only override the things I actually need to change rather than declaring a lot of stuff that I think is part of the standard template anyway. I might have a play with this.

另一件事我很好奇如果可以减少我的控制/风格模板使用风格中参数的基础之上,那么我只会覆盖的事情我需要改变,而不是宣布很多东西我认为是标准模板的一部分。我可能会玩这个。

Cheers

干杯

ed

艾德

#8


0  

If it helps anyone, a fast and elegant solution I've found is to hook with the Checked and Unchecked events and manipulate the value based on your flag.

如果它对任何人都有帮助,那么我发现了一种快速而优雅的解决方案,即使用已检查和未检查的事件,并基于您的标志操纵值。

    public bool readOnly = false;

    private bool lastValidValue = false;

    public MyConstructor()
    {
        InitializeComponent();

        contentCheckBox.Checked += new
        RoutedEventHandler(contentCheckBox_Checked);
        contentCheckBox.Unchecked += new
        RoutedEventHandler(contentCheckBox_Checked);
    }

    void contentCheckBox_Checked(object sender, RoutedEventArgs e)
    {
        if (readOnly)
            contentCheckBox.IsChecked = lastValidValue;
        else
            lastValidValue = (bool)contentCheckBox.IsChecked;
    }

#9


-1  

Wrap the CheckBox in a ContentControl. Make the ContentControl IsEnabled=False

在ContentControl中包装复选框。继承自ContentControl IsEnabled使= False