[英]A read-only CheckBox in C# WPF
我遇到了一個棘手的問題,我想從復選框中獲得一些稍微不尋常的行為,但似乎無法弄清楚。 任何建議將是最受歡迎的。 我想要的行為是:
我遇到的問題是,盡管 OneWay 數據綁定在單擊 CheckBox 時不會更新數據結構,但視覺表示確實發生了變化(我認為這很奇怪,IsChecked 現在不應該像指向數據結構)。
我可以反轉 Click() 事件中的更改並在那里禁用,但這非常混亂。 我還可以使用數據結構值的 set 屬性來設置 isEnabled 值,該值也綁定到重新啟用 CheckBox 但這似乎也很混亂。
有沒有干凈的方法來做到這一點? 也許使用派生的 CheckBox 類? 如何停止更新視覺表示?
謝謝
埃德
綁定到IsHitTestVisible屬性的數據怎么樣?
例如,假設采用 MVVM 方法:
我沒有這個確切的要求,我只需要一個始終只讀的復選框,它似乎很好地解決了問題。 還要注意下面 Goran 關於Focusable屬性的評論。
這個答案不是你的問題,但它回答了標題中的問題。
WPF 中的復選框沒有IsReadOnly
屬性。 但是,使用屬性IsHitTestVisible="False"
和Focusable="False"
可以實現類似的行為
<CheckBox IsHitTestVisible="False"
Focusable="False"/>
我認為沒有必要為此創建一個完整的控件。 您遇到的問題來自這樣一個事實,即您看到“支票”的地方並不是真正的復選框,而是一顆子彈。 如果我們查看 CheckBox 的ControlTemplate ,我們可以看到它是如何發生的(盡管我更喜歡 Blend 模板)。 作為其中的一部分,即使您對 IsChecked 屬性的綁定設置為 OneWay,它仍會在 UI 中更新,即使它沒有設置綁定值。
因此,解決此問題的一個非常簡單的方法是修改相關復選框的 ControlTemplate。
如果我們使用 Blend 抓取控件模板,我們可以在 ControlTemplate 中看到代表實際復選框區域的 Bullet。
<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>
在這里,IsChecked 和 RenderPressed 實際上是使“Check”出現的原因,因此要修復它,我們可以從 ComboBox 上的 IsChecked 屬性中刪除綁定,並使用它來替換 Bullet 的 IsChecked 屬性上的 TemplateBinding。
這是一個演示所需效果的小示例,請注意,要保持 Vista CheckBox 外觀,需要將 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>
以及后面的代碼,以及我們支持的布爾值:
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
}
我需要關閉自動檢查功能以及復選框/單選按鈕,我想在沒有控件自動檢查的情況下處理 Click 事件。 我在這里和其他線程上嘗試了各種解決方案,但對結果不滿意。
所以我深入研究了 WPF 正在做什么(使用反射),我注意到:
所以基本上,我需要做的就是覆蓋 OnClick 事件,不要調用 OnToggle,然后執行 base.RaiseEvent
這是完整的代碼(請注意,這也可以很容易地重新編寫以執行 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));
}
}
}
我現在有一個不會自動檢查的 CheckBox,並且仍然會觸發 Click 事件。 另外,當我以編程方式更改 IsChecked 屬性時,我仍然可以訂閱 Checked/Unchecked 事件並處理那里的事情。
最后一點:與在 Click 事件中執行 IsChecked != IsChecked 之類的其他解決方案不同,這不會導致 Checked/Unchecked/Indeterminate 事件觸發,直到您以編程方式設置 IsChecked 屬性。
這是我編寫的類,用於執行類似的操作,出於類似的原因(仍然像往常一樣引發所有 Click 和 Command 事件,但默認情況下不會更改綁定源並且不會自動切換。不幸的是,它仍然具有單擊時動畫淡入淡出,如果單擊處理代碼最終沒有更改 IsChecked,這有點奇怪。
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.
}
}
用法:
<yourns:OneWayCheckBox IsChecked="{Binding SomeValue}"
Command="{x:Static yourns:YourApp.YourCommand}"
Content="Click me!" />
(請注意,IsChecked 綁定現在默認是單向的;如果需要,您可以將其聲明為 TwoWay,但這會部分說明問題。)
遲到的答案,但我只是遇到了這個問題,正在尋找其他東西。 你想要的根本不是一個行為異常的復選框,你想要的是一個外觀異常的按鈕。 更改控件的外觀總是比更改其行為更容易。 沿着這些路線應該做的事情(未經測試)
<Button Command="{Binding CommandThatStartsTask}">
<Button.Template>
<ControlTemplate>
<CheckBox IsChecked="{Binding PropertySetByTask, Mode=OneWay}" />
</ControlTemplate>
</Button.Template>
</Button>
如果它對任何人有幫助,我發現的一個快速而優雅的解決方案是掛鈎 Checked 和 Unchecked 事件並根據您的標志操縱值。
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; }
使用驗證來阻止布爾值在您不希望它被切換時被切換 - http://www.codeproject.com/KB/WPF/wpfvalidation.aspx
這遠沒有其他答案那么可怕,或者鈎住 Clicked
我一直在嘗試創建我的通用 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"/>
當然,除了當我這樣做然后將項目符號上的事件觸發器設置為 IsChecked 的 TemplateBinding 時,我完全擁有我開始的內容! 我想我不明白為什么直接在項目符號中設置綁定與設置 IsChecked 然后綁定到它不同,TemplateBinding 不只是一種引用正在創建的控件的屬性中設置的內容的方法嗎? 即使數據沒有更新,Click 如何觸發 UI 更新? 單擊我可以覆蓋以停止更新是否有觸發器?
我讓所有 DictionaryResource 的東西都工作正常,所以我很高興,為指針歡呼。
我好奇的另一件事是,是否可以通過在樣式中使用 BasedOn 參數來減少我的 Control/Style 模板,那么我只會覆蓋我實際需要更改的內容,而不是聲明很多我認為的內容無論如何都是標准模板的一部分。 我可能會玩這個。
干杯
編輯
將 CheckBox 包裝在 ContentControl 中。 使 ContentControl IsEnabled=False
這兩個屬性是 – IsHitTestVisible 和 Focusable 使這兩個屬性為 False。 這使得 WPF 中的只讀復選框。 因此,對於 WPF 中的只讀復選框,最終的 XAML 語句將如下所示 –
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.