从Null到Boolean的IValueConverter无法工作

8

如何使用IValueConverter将null转换为布尔值?

我正在使用wpf尝试显示一堆布尔值(在复选框中)。当创建新记录时,这些值为null,并显示为复选框中的“不确定”状态。我希望将null显示并保存为“false”值。

我尝试创建一个NullToBoolean转换器,它从数据库中获取null值并将其显示为false,然后在用户点击保存时将其保存为false。(基本上,我试图避免用户在复选框中点击两次(一次使其为true,再一次使其为false)。这似乎在导入时起作用-即null值显示为false-但除非我做两次点击操作,否则在保存时数据库中的值不会改变。

我的转换器:

[ValueConversion(typeof(bool), typeof(bool))]
public class NullBooleanConverter : IValueConverter
{

  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value != null)
    {
      return value;
    }
    return false;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    if (value != null)
    {
      return value;
    }
    return null;
  }
}

我正在尝试让转换器处理的复选框之一:

    <CheckBox Grid.Column="1" Grid.Row="0" Padding="5" Margin="5" VerticalAlignment="Center" Name="chkVarianceDescriptionProvided" IsThreeState="False">
      <CheckBox.IsChecked>
        <Binding Path="VarianceDescriptionProvided" Mode="TwoWay">
          <Binding.Converter>
            <utils:NullBooleanConverter />
          </Binding.Converter>
        </Binding>
      </CheckBox.IsChecked>
    </CheckBox>

我不知道问题是因为我的代码有误,还是Converter认为没有任何改变,因此不需要ConvertBack。我尝试了所有的Mode,并且交换了Convert和ConvertBack的代码,但似乎都不起作用。

是否有人可以指出我需要怎么做才能解决这个问题?


1
那看起来很丑。你是将空转换为false,非空转换为true吗?因为你并没有这样做。而且在转换回来时,你正在将bool(永远不会为空)转换回bool。 - Meirion Hughes
1
你检查过你的转换器代码是否起作用了吗? - Sandeep Singh Rawat
3
顺便说一下,“当创建新记录时,这些值为空…”是问题所在;要解决这个问题。你应该制作“友好”的ViewModels/DataModels以便查看。 - Meirion Hughes
@MeirionHughes,我认为你的评论是解决这个问题比我在答案中提供的方法更好的方式。我认为你可以将其作为自己的答案。 - user743382
1
@MeirionHughes。将Null转换为false,其他所有内容(始终为布尔值)转换为给定的值。欢迎任何美化建议 :-)。 评论2)它们在数据库中是可空的(我无法更改),EF正在处理所有创建工作。我认为可以使用ValueConverter来创建一个友好的ViewModel。对Sandeep Singh Rawat:是的,我已经删除了我的MessageBox / debug代码,但肯定会调用方法。 - mcalex
为什么不在数据层将值默认为“false”呢? - Dan Puzey
3个回答

24

嗯,如果你可以直接拥有它,为什么要使用转换器呢?

<CheckBox IsChecked="{Binding VarianceDescriptionProvided, TargetNullValue=False}" />

更多信息,请查看此处


我之前不知道TargetNullValue,非常感谢。然而,这并没有将“False”值保存回数据库(即,在保存后它们仍然是NULL)。它与当前的转换器执行相同的任务,所以在我修复更大的问题之前,我会使用它。 - mcalex
好的,这只是一次尝试。我不确定值是否传递给了虚拟机。谢谢您让我知道。 :) - DHN
很有趣,因为文档(http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.targetnullvalue.aspx)说它是一个getter和setter - 但是却没有给出设置示例。 - mcalex
是的,TargetNullValue属性本身具有getter和setter。这就是为什么您可以向其传递一个值。但这仍然不能确保该值通过绑定传递给VM。仔细思考一下,这是有道理的。使用TargetNullValue,您可以让视图有机会解释NULL,而不干扰底层的VM或数据,这些数据可能在复杂的逻辑中进行处理。它遵循解耦原则。 :) - DHN

1
真正的问题在于你没有在一开始初始化你的数据对象。不要“修复”,一开始就做正确的事情;建造者模式很好(例如)。你还应该创建 ViewModel/DataModel 而不是直接使用你的 Models(数据库等)。
public class MyObjectBuilder
{
     Checked _checked;

     public  MyObjectBuilder()
     {
          Reset()
     }

     private void Reset()
     { 
          _checked = new Checked(true); //etc
     }

     public MyObjectBuilder WithChecked(bool checked)
     {
          _checked = new Checked(checked);
     }

     public MyObject Build()
     {
         var built = new MyObject(){Checked = _checked;} 
         Reset();
         return built;
     }
}

那么始终使用构建器进行初始化。

myObjects.Add(new MyObjectBuilder().Build());

或者

myObjects.Add(_injectedBuilder.Build()); // Initialises Checked to default 
myObjects.Add(_injectedBuilder.WithChecked(true).Build()); //True

虽然这不能解决你提出的问题,但它可以通过一种方式解决你的潜在问题,以便你可以进行单元测试。也就是说,你可以测试确保添加到对象列表中的值始终被初始化。

谢谢@Meirion-Hughes。我明白你的意思,并且可以按照你的想法来实现我的目标。对象构造中的默认值-处理中。当我开始这个项目时,我不知道mvvm,所以它目前是用旧式的wpf完成(事件处理程序,在代码后台中有一些混乱的数据验证等),随着我学习如何将它们融入其中,添加了mvvm功能。下一个项目的目标是完全使用mvvm,但必须先解决当前混合模式的问题。再次感谢。 - mcalex
谷歌,Caliburn Micro。它真的很棒。 - Meirion Hughes
我调查了几个框架。不幸的是,它们都没有详细说明如何将意大利面条式代码转换为MVVM模式。Caliburn确实看起来非常令人印象深刻,可能是我开始新项目的选择。谢谢! - mcalex

0

在执行数据绑定之前,只需简单地纠正您的数据。这是唯一的选择。转换器仅在您与控件交互时才会使复选框显示为“未选中”,并更新您的数据。例如:

foreach (var item in items)
{
    if (item.VarianceDescriptionProvided == null)
        item.VarianceDescriptionProvided = false;
}

如果您随机添加了项目,它将会失败;您需要添加一个可观察列表,监听它并修复任何新添加的项目。您也可以在创建它们时确保它们是正确的。 - Meirion Hughes
每次从EF获取结果时,只需运行此代码即可。无需复杂的包装器等。 - Eli Arbel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接