与转换器的两种方式绑定,无需修改源代码

Two way binding with converter without source modification

我想使用 linq 分组作为复选框列表视图的项目源

这是我的数据模型:

public class GroupItem
{
   public string name GroupName { get; set; }
   public string boolean GroupItemFlag { get; set; }
}

它查看模型(数据上下文):

...
IEnumerable<GroupItem> _groupItems;

public IEnumerable<IGrouping<string,GroupItem>> Groups
{
   get { return _groupItems.GroupBy(__item=>__item.GroupName); }
}
...

它的观点:

...
<ListView ItemsSource={Binding Groups}>
   <ListView.ItemTemplate>
        <StackPanel Orientation="Horizontal">
            <CheckBox IsChecked="{Binding Converter={StaticResource groupingToBooleanConverter}, Mode=TwoWay}"/>
            <TextBlock Text="{Binding Key}"/>
        </StackPanel>
   </ListView.ItemTemplate>
</ListView>
...

groupingToBooleanConverter 代码:

public class GroupingToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var group = value as IGrouping<string,GroupItem>;
        return group.Any(__item => __item.GroupItemFlag);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // problem here
        // how to set for all group items GroupItemFlag=(bool)value;
    }
}

我不想为此再创建一个 class "Group",所以我使用 IGrouping 和转换器。

可能是我选错方法了?

不用转换器也能实现:

<CheckBox IsChecked="{Binding Path=GroupItemFlag, Mode=OneWay}"
          Command="{Binding DataContext.ToogleGroupCommand, ElementName=LayoutRoot}" 
          CommandParameter="{Binding}"/>
void ToogleGroupCommand_Execute(IGrouping<string, GroupItem> group){
    bool newValue = !group.First().GroupItemFlag;
    foreach(var item in group) item.GroupItemFlag = newValue; 
}

如果您不使用 MVVM,则只需处理 Checked 和 Unchecked 事件。

但是,如果您使用的是 MVVM,我强烈建议您按照 ViVi 的建议创建组 class:

public MainViewModel()
{
   Groups = _groupItems.GroupBy(i => i.GroupName).Select(i => new GroupViewModel(i.Key, i);
}
public Groups[] Groups {get;}


public class GroupViewModel
{
    public GroupViewModel(string name, IEnumerable<GroupItem> items)
    {
        Items = items;
    }

    public string Name { get; }
    public IEnumerable<GroupItem> Items { get; }


    public bool? IsChecked
    {
        get
        {
            if (Items.All(i => i.GroupItemFlag)) return true;
            if (Items.Any(i => i.GroupItemFlag)) return null;
            return false;
        }
        set
        {
            foreach (var item in Items)
            {
                item.GroupItemFlag = value.GetValueOrDefault();
            }
        }
    }
}