我正在使用MVVM创建一个UWP应用程序。在我的xaml视图中,我有一个3状态复选框。我的复选框位于ListView中,此列表视图还有一个子列表视图,带有更多复选框 - 所以基本上是嵌套的复选框列表。如果选中内部列表视图中的任何复选框,我试图让外部列表视图中的复选框处于空状态。如果选择了所有内部复选框,我希望外部复选框处于true状态,如果没有选中,我希望它处于false状态。我正在使用委托命令来实现这些事件处理程序。
我成功地能够获得真假案例,但是我无法获得在UI上显示状态null的复选框,因为它在我的ViewModel中的值成功更改为null。
<ListView ItemsSource="{Binding ObxList, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{x:Bind IsSelected, Mode=TwoWay}" IsThreeState="True" Command="{Binding DataContext.ClickCommand, ElementName=Y}">
<TextBlock Text="{x:Bind Value}" />
</CheckBox>
<ListView ItemsSource="{Binding Children}" Visibility="{Binding visibility, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
IsThreeState="False" Command="{Binding DataContext.ChildCommand, ElementName=Y}">
<TextBlock Text="{Binding Value}"/>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView
以下是在我的委托命令中调用的ViewModel方法的代码:
public void ChildCheckboxHandler()
{
// gets all of the lowest level children
foreach (Object item in AllObjectsList.Where(t =>
{
return t.Children.Count() == 0;
}))
{
if (item.IsSelected == true)
{
var parent = item.GetParent(AllObjectsList);
if (parent.IsSelected == null || parent.IsSelected == false)
{
// checks if all children are selected
// and changes the parent to true if so
// null if only some are selected
bool allChildrenSelected = true;
foreach (var child in parent.GetChildren(AllObjectsList))
{
if (child.IsSelected == false)
{
allChildrenSelected = false;
break;
}
}
if (allChildrenSelected)
{
parent.IsSelected = true;
}
else
{
parent.IsSelected = new Nullable<Boolean>();
}
}
}
else if (item.IsSelected == false)
{
var parent = item.GetParent(AllObjectsList);
if (parent.IsSelected == null)
{
// checks if all the children are unchecked
// if they are, then sets the parent to false
// if they are not, then sets the parent to null
bool noChildrenSelected = true;
foreach (var child in parent.GetChildren(AllObjectsList))
{
if (child.IsSelected == true)
{
noChildrenSelected = false;
break;
}
}
if (noChildrenSelected)
{
parent.IsSelected = false;
}
}
else if (parent.IsSelected == true)
{
parent.IsSelected = null;
}
}
}
}
答案 0 :(得分:3)
这是因为WinRT不支持绑定到可空类型。
答案 1 :(得分:0)
最终的问题是 DependencyProperty
的 CheckBox.IsChecked
实现。 WinRT 不隐式支持可空基本类型绑定,在 WinRT 中,底层类型是 IReference1<Boolean>
,这是一个内部定义,因此我们不能使用简单的类型转换器来解决此问题。
previous answer 在技术上是正确的,@Jerry Nixon 在 linked article 中提供了一些有用的背景信息,但它仅表明存在 解决方法,而不是具体_how实施它们。
这篇博文中的建议在近 10 年的 UWP 中仍然适用:Windows 8 Dev Tip: Nullable Dependency Properties and Binding。最简单的解决方法是创建一个类型为 Object
的新依赖属性。
这是许多 3rd 方控件库中的常见解决方案,通常会找到一个 object
类型化的 Value
属性和一个单独的 类型化 属性。
这可以通过附加属性实现,但我更喜欢扩展 CheckBox
类:
/// <summary>
/// Check Box Control with support for binding with an indeterminate state
/// </summary>
/// <remarks>https://www.danrigby.com/2012/07/24/windows-8-dev-tip-nullable-dependency-properties-and-binding/</remarks>
public class BindableCheckBox : CheckBox
{
public BindableCheckBox()
{
this.IsThreeState = true;
// start in the nullable state!
this.IsChecked = null;
// hook up UI events to manually set the value appropriately
this.Checked += BindableCheckBox_Checked;
this.Unchecked += BindableCheckBox_Unchecked;
this.Indeterminate += BindableCheckBox_Indeterminate;
// Hookup property changed event, in case some moron sets the IsChecked some other way
this.RegisterPropertyChangedCallback(CheckBox.IsCheckedProperty, (s, e) =>
{
if (s is BindableCheckBox cb)
{
if (!cb._ValueIsChanging)
{
var newValue = (bool?)s.GetValue(e);
if (cb.State != newValue)
cb.State = newValue;
}
}
});
}
private bool _ValueIsChanging = false;
private void BindableCheckBox_Indeterminate(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if(!_ValueIsChanging)
Value = null;
}
private void BindableCheckBox_Unchecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (!_ValueIsChanging)
Value = false;
}
private void BindableCheckBox_Checked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
if (!_ValueIsChanging)
Value = true;
}
/// <summary>
/// DP For the Three States of this checkbox, Null implies that it is in the Indeterminate state
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
nameof(Value),
typeof(object),
typeof(BindableCheckBox), new PropertyMetadata(null, (s, e) =>
{
if (s is BindableCheckBox cb)
{
try
{
cb._ValueIsChanging = true;
var newValue = (bool?)e.NewValue;
if (cb.IsChecked != newValue)
cb.IsChecked = newValue;
}
finally
{
cb._ValueIsChanging = false;
}
}
}));
/// <summary>
/// Value property, a bindable implementation of IsChecked
/// </summary>
/// <remarks>You should bind this property INSTEAD of IsChecked, if you want to bind to the indeterminate state</remarks>
public bool? Value
{
get { return (bool?)GetValue(ValueProperty); }
private set { SetValue(ValueProperty, value); }
}
}
现在您可以使用 MVVM 风格的 TwoWay 绑定绑定到 Value
属性