Флажок TriState - как изменить порядок состояний
У меня есть CheckBox в моем приложении, которое использует режим TriState. Нормальное поведение для этого режима, по-видимому, циклически меняется между нулевым, ложным, истинным.
Я бы хотел изменить это поведение так, чтобы оно менялось между нулевым, истинным, ложным.
Какой лучший способ сделать это?
Я попытался добавить обработчик кликов, подобный этому:
void cb_Click(object sender, RoutedEventArgs e)
{
if (((CheckBox)e.Source).IsChecked.HasValue == false)
{
((CheckBox)e.Source).IsChecked = true;
return;
}
if (((CheckBox)e.Source).IsChecked == true)
{
((CheckBox)e.Source).IsChecked = false;
return;
}
if (((CheckBox)e.Source).IsChecked == false)
{
((CheckBox)e.Source).IsChecked = null;
return;
}
}
Но это, кажется, полностью отключает флажок. Я почти уверен, что мне не хватает чего-то, что должно быть очевидно.
Ответы
Ответ 1
Я думаю, обработчик событий и поведение по умолчанию просто отменяют друг друга, поэтому флажок отключен...
На самом деле мне недавно приходилось делать то же самое. Мне пришлось наследовать от CheckBox
и переопределить OnToggle
:
public class MyCheckBox : CheckBox
{
public bool InvertCheckStateOrder
{
get { return (bool)GetValue(InvertCheckStateOrderProperty); }
set { SetValue(InvertCheckStateOrderProperty, value); }
}
// Using a DependencyProperty as the backing store for InvertCheckStateOrder. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InvertCheckStateOrderProperty =
DependencyProperty.Register("InvertCheckStateOrder", typeof(bool), typeof(MyCheckBox), new UIPropertyMetadata(false));
protected override void OnToggle()
{
if (this.InvertCheckStateOrder)
{
if (this.IsChecked == true)
{
this.IsChecked = false;
}
else if (this.IsChecked == false)
{
this.IsChecked = this.IsThreeState ? null : (bool?)true;
}
else
{
this.IsChecked = true;
}
}
else
{
base.OnToggle();
}
}
}
Ответ 2
Только для полноты здесь дополнительно используется решение для Windows-форм. Мы должны переопределить метод OnClick
, переопределяя результаты OnStateChanged
при отображении ошибки (и stackoverflow, если сделано неправильно).
/// <summary>
/// A <see cref="System.Windows.Forms.CheckBox"/> with the ability to reverse the checkstate order.
/// </summary>
/// <seealso cref="System.Windows.Forms.CheckBox" />
public class CheckBoxReversible : CheckBox
{
private bool FInvertCheckStateOrder;
/// <summary>
/// Gets or sets a value indicating whether to invert the check state order from [Indeterminate->Unchecked->Checked] to [Indeterminate->Checked->Unchecked].
/// </summary>
/// <value>
/// <c>true</c> to invert the check state order; otherwise, <c>false</c>.
/// </value>
public bool InvertCheckStateOrder
{
get { return FInvertCheckStateOrder; }
set { FInvertCheckStateOrder = value; }
}
/// <summary>
/// Löst das <see cref="E:System.Windows.Forms.Control.Click" />-Ereignis aus.
/// </summary>
/// <param name="e">Eine Instanz der <see cref="T:System.EventArgs" />-Klasse, die die Ereignisdaten enthält.</param>
protected override void OnClick(EventArgs e)
{
if (this.InvertCheckStateOrder)
{
if (this.CheckState == CheckState.Indeterminate)
this.CheckState = CheckState.Checked;
else
if (this.CheckState == CheckState.Checked)
this.CheckState = CheckState.Unchecked;
else
if (this.CheckState == CheckState.Unchecked)
this.CheckState = this.ThreeState ? CheckState.Indeterminate : CheckState.Checked;
}
else
base.OnClick(e);
}
}
Ответ 3
Большое спасибо, было очень полезно решить аналогичное поведение с ToggleButton.
class InvertedToggleButton : ToggleButton
{
public bool InvertCheckStateOrder
{
get { return (bool)GetValue(InvertCheckStateOrderProperty); }
set { SetValue(InvertCheckStateOrderProperty, value); }
}
// Using a DependencyProperty as the backing store for InvertCheckStateOrder. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InvertCheckStateOrderProperty = DependencyProperty.Register("InvertCheckStateOrder", typeof(bool), typeof(ToggleButtonInvertido), new UIPropertyMetadata(false));
protected override void OnToggle()
{
if (this.InvertCheckStateOrder)
{
if (this.IsChecked == true)
{
this.IsChecked = false;
}
else if (this.IsChecked == false)
{
this.IsChecked = this.IsThreeState ? null : (bool?)true;
}
else
{
this.IsChecked = true;
}
}
else
{
if (!this.IsChecked.HasValue)
this.IsChecked = true;
else
base.OnToggle();
}
}
}
Ответ 4
Как уже отмечалось, CheckBox.OnToggle()
переопределяется в WinForms, поэтому в этом случае CheckBox.OnClick()
должен искать.
Здесь минимальная реализация флажка WinForms, который отключает Unchecked → Indeterminate → Checked → Unchecked... вместо стандартного Unchecked → Checked → Indeterminate → Unchecked...
using System;
using System.Windows.Forms;
public class MyCheckBox : CheckBox
{
protected override void OnClick(EventArgs e)
{
switch(this.CheckState)
{
case CheckState.Indeterminate:
this.CheckState = CheckState.Checked;
break;
case CheckState.Checked:
this.CheckState = CheckState.Unchecked;
break;
case CheckState.Unchecked:
this.CheckState = this.ThreeState ? CheckState.Indeterminate : CheckState.Checked;
break;
default:
throw new ApplicationException("Unexpected CheckState: " + this.CheckState.ToString());
}
}
}
Или если вы счастливы представить зависимость от того, какие значения int, которые Windows назначает для состояний штата CheckState, это также работает:
using System;
using System.Windows.Forms;
public class MyCheckBox : CheckBox
{
protected override void OnClick(EventArgs e)
{
CheckState newState = this.CheckState - 1;
if (newState < 0)
{
newState = this.ThreeState ? CheckState.Indeterminate : CheckState.Checked;
}
this.CheckState = newState;
}
}