【问题标题】:Return null in boolean to checkbox state converter in XAML将布尔值中的 null 返回到 XAML 中的复选框状态转换器
【发布时间】:2014-07-14 15:51:10
【问题描述】:

我有一个 TaskStatus 到布尔转换器,它在 XAML 中为 Windows 应用商店应用程序(通用应用程序)实现了 IValueConverter 接口。

我有三个任务状态,我使用 IsThreeState="true" 在复选框中启用了不确定状态。

现在虽然 IsChecked 属性似乎是 Boolean?,但转换器始终将 System.Boolean 作为目标类型。无论我返回什么(例如 null)总是转换为 false,因此我无法在我的复选框中获得第三个状态。

有没有办法在我的转换器中指定 TargetType 或返回 null 以便 IsChecked 将 null 作为输入并因此显示第三个状态?

这是转换器:

public class TaskStatusToCheckBoxStateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var taskStatus = (TaskStatus) value;
        switch (taskStatus)
        {
            case TaskStatus.Open:
                return false;
            case TaskStatus.InProgress:
                return null;
            case TaskStatus.Done:
                return true;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        var checkBoxState = (Boolean?) value;
        if (checkBoxState == null)
            return TaskStatus.InProgress;
        if (checkBoxState.Value)
            return TaskStatus.Done;
        return TaskStatus.Open;
    }
}

复选框的 XAML 代码

<CheckBox x:Name="CheckBoxTaskState" 
    IsThreeState="True"
    IsChecked="{Binding Status, 
                Converter={StaticResource TaskStatusToCheckBoxStateConverter}, 
                Mode=TwoWay}">
</CheckBox>

【问题讨论】:

  • 尝试返回 Nullable&lt;Boolean&gt; 而不是 null。 (注意 Nullable 是一个结构体,它会被装箱为一个对象。因此,返回 null 与返回 Nullable 不同。)
  • 也不起作用。不幸的是同样的结果。或者也许我做错了。我现在有return new Nullable&lt;Boolean&gt;();。这是你的意思吗?
  • 我刚刚检查了三态复选框和一个 System.Windows.Data.IValueConverter(手头没有 VS2013/WindowsStore 项目),其 Convert 方法返回nullnew Nullable。两种变体都像一个魅力。您是否尝试在 ConvertConvertBack 方法中设置断点来观察绑定和转换器正在处理的具体值是什么?
  • 是的。我检查了那个。当值为 TaskStatus.InProgress 时,代码正确地进入适当的 switch case。目标类型始终为 System.Boolean。 ConvertBack 就像一个魅力。 Just Convert 不会返回正确的空值。我也在 WPF 应用程序中尝试了相同的方法,就像在您这边一样,它按预期工作(null 和 Nullable)。
  • 这很奇怪。我不相信 CheckBox 或与转换器的绑定在 WindowsStore 应用程序中被破坏。如果您看到 ConvertBack 正常工作而 Convert 似乎不起作用,我只能想到以下可能导致您的问题的情况:(1)代码中的某个地方-在 XAML 中的触发器定义之后或作为触发器定义的一部分,CheckBox 属性 IsCheckedIsThreeState 的值在某些情况下会发生更改。 (2) 您的代码有另一个双向数据绑定,其中 IsCheckedIsThreeState 属性作为其更改属性的绑定源。

标签: c# xaml checkbox windows-store-apps win-universal-app


【解决方案1】:

根据 [this][1]:目前不支持绑定到 WinRT 中的可为空类型。一个无证的规则怎么样?现在你知道了。

从这里开始

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    public MainPage()
    {
        this.InitializeComponent();
        this.DataContext = this;
    }

    private void NullButton_Click(object sender, RoutedEventArgs e)
    { this.State = null; }

    private void FalseButton_Click(object sender, RoutedEventArgs e)
    { this.State = false; }

    private void TrueButton_Click(object sender, RoutedEventArgs e)
    { this.State = true; }

    bool? _State = null;
    public bool? State { get { return _State; } set { SetProperty(ref _State, value); } }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (!object.Equals(storage, value))
        {
            storage = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}

还有这个

<Grid x:Name="grid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel Orientation="Horizontal">
            <Button Click="TrueButton_Click" Content="True" />
            <Button Click="FalseButton_Click" Content="False" />
            <Button Click="NullButton_Click" Content="Null" />
        </StackPanel>
        <TextBlock Text="{Binding State}" />
        <CheckBox x:Name="checkBox"
                  Content="Hello three-state"
                  IsThreeState="True"
                  IsChecked="{Binding State, Mode=TwoWay}" />
    </StackPanel>
</Grid>

您可以在“输出”窗口中验证此错误。它读起来是这样的:

错误:转换器无法将“布尔”类型的值转换为“IReference1&lt;Boolean&gt;'; BindingExpression: Path='State' DataItem='App4.MainPage'; target element is 'Windows.UI.Xaml.Controls.CheckBox' (Name='checkBox'); target property is 'IsChecked' (type 'IReference1”类型)。

我对此不满意。因此,让我们使用附加属性来解决它。

public class NullableCheckbox : DependencyObject
{
    public static bool GetEnabled(DependencyObject obj)
    { return (bool)obj.GetValue(EnabledProperty); }
    public static void SetEnabled(DependencyObject obj, bool value)
    { obj.SetValue(EnabledProperty, value); }
    public static readonly DependencyProperty EnabledProperty =
        DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(NullableCheckbox), new PropertyMetadata(false, EnabledChanged));
    private static void EnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as CheckBox;
        if ((bool)e.NewValue)
        {
            var binding = new Binding
            {
                Path = new PropertyPath("IsChecked"),
                Mode = BindingMode.TwoWay,
                Source = checkbox,
            };
            checkbox.SetBinding(NullableCheckbox.InternalStateProperty, binding);
        }
    }

    private static object GetInternalState(DependencyObject obj)
    { return (object)obj.GetValue(InternalStateProperty); }
    private static void SetInternalState(DependencyObject obj, object value)
    { obj.SetValue(InternalStateProperty, value); }
    private static readonly DependencyProperty InternalStateProperty =
        DependencyProperty.RegisterAttached("InternalState", typeof(object),
        typeof(NullableCheckbox), new PropertyMetadata(null, InternalStateChanged));
    private static void InternalStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    { SetIsChecked(d, (object)e.NewValue); }

    public static object GetIsChecked(DependencyObject obj)
    { return (object)obj.GetValue(IsCheckedProperty); }
    public static void SetIsChecked(DependencyObject obj, object value)
    { obj.SetValue(IsCheckedProperty, value); }
    public static readonly DependencyProperty IsCheckedProperty =
        DependencyProperty.RegisterAttached("IsChecked", typeof(object),
        typeof(NullableCheckbox), new PropertyMetadata(default(object), IsCheckedChanged));
    private static void IsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var checkbox = d as CheckBox;
        bool? newvalue = null;
        if (e.NewValue is bool?)
            newvalue = (bool?)e.NewValue;
        else if (e.NewValue != null)
        {
            bool newbool;
            if (!bool.TryParse(e.NewValue.ToString(), out newbool))
                return;
            newvalue = newbool;
        }
        if (!checkbox.IsChecked.Equals(newvalue))
            checkbox.IsChecked = newvalue;
    }
}

您的 XAML 只会像这样改变:

<CheckBox Content="Hello three-state"
          IsThreeState="True"
          local:NullableCheckbox.Enabled="true"
          local:NullableCheckbox.IsChecked="{Binding State, Mode=TwoWay}" />

常规的 IsChecked 属性无关紧要,它会被附加属性覆盖。您的视图模型可以保持不变。真的很神奇吧?

祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 2014-06-10
    • 1970-01-01
    • 2021-12-15
    • 2016-09-21
    相关资源
    最近更新 更多