拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 将MenuItem.IsEnabled系结到视图模型的属性

将MenuItem.IsEnabled系结到视图模型的属性

白鹭 - 2022-03-04 2212 0 0

我有一个带有以下代码的 MVVM WPF 项目:
MultiplexerVM.cs

public class MultiplexerVM : BaseViewModel
{
    public ObservableCollection<MultiplexVM> Multiplexes { get; set; } = new();
    public MultiplexVM SelectedMultiplex { get; set; }
    public ICommand CheckAll => new CheckBoxCommand(Multiplexes);
}

MultiplexVM.cs

public class MultiplexVM : BaseViewModel
{
    public bool IsChecked { get; set; }
}

多路复用器V.xaml

<UserControl x:Class="MKVStudio.Views.MultiplexerV"
             xmlns:vm="clr-namespace:MKVStudio.ViewModels"
             xmlns:s="clr-namespace:System;assembly=mscorlib">
    <UserControl.Resources>
        <s:Boolean x:Key="True">True</s:Boolean>
        <s:Boolean x:Key="False">False</s:Boolean>
    </UserControl.Resources>
    <Grid>
        <ListView ItemsSource="{Binding Multiplexes}"
                  SelectedItem="{Binding SelectedMultiplex}">
            <ListView.View>
                <GridView>
                    <GridViewColumn>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding IsChecked}"Margin="3"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                           ...
                </GridView>
            </ListView.View>                
            <ListView.ContextMenu>
                <ContextMenu>
                    <MenuItem Command="{Binding CheckAll}"
                              CommandParameter="{StaticResource True}">
                        <MenuItem.Header>
                            <TextBlock Text="Check all"/>
                        </MenuItem.Header>
                    </MenuItem>
                    <MenuItem Command="{Binding CheckAll}"
                              CommandParameter="{StaticResource False}">
                        <MenuItem.Header>
                            <TextBlock Text="Uncheck all"/>
                        </MenuItem.Header>
                    </MenuItem>
                </ContextMenu>
            </ListView.ContextMenu>
        </ListView>
    </Grid>
</UserControl>

我的目标是结合IsEnabled背景关系选单项的财产IsCheckedMultiplexVM.cs这个想法是实作一个IValueConverter(passing Multiplexesasvalue和 bool as parameter)。转换器回传value.Where(m => m.IsChecked == parameter).Count > 0本质上,当所有Multiplexes都取消选中时,选单项Check all被启用而选单项Uncheck all被禁用。当所有Multiplexes都被检查时,相反的事情正在发生这里的问题是转换器在基本宣告时只被呼叫一次,检查和取消选中项目不会触发转换器查看发生了什么。

我试图实作一个IMultiValueConverter(但未能正确使用它)并传递三个这样的值:

<MenuItem.IsEnabled>
    <MultiBinding>
        <Binding Source="{Binding Multiplexes.Count}" />
        <Binding Source="{Binding Multiplexes}" />
        <Binding Source="{StaticResource True}" /> <!--respectivly False to the other menu item-->
    </MultiBinding>
</MenuItem.IsEnabled>

这不起作用。我试过<Binding Path="Multiplexes.Count" /><Binding Path="Multiplexes" />,但也不起作用(传递给转换器的值为Unset)。

MultiBinding使用它的想法是否可行,使用它时我做错了什么?

uj5u.com热心网友回复:

为什么需要同时将 IsChecked 系结到 IsChecked 和 IsEnabled?如果从单一职责原则来看,这很奇怪。如果你确定你做对了,你可以这样做:

<CheckBox IsChecked="{Binding IsChecked}"
          IsEnabled="{Binding IsEnabled}" />

让你的班级看起来像这样:

public class MultiplexVM : BaseViewModel
{
    public bool IsChecked 
    { 
        get => isChecked; 
        set
        {
            isChecked = value;
            isEnabled = value;
            RaisePropertyChanged(nameof(IsChecked));
            RaisePropertyChanged(nameof(IsEnabled));
        }; 
    }

    private bool isChecked;

    public bool IsEnabled
    { 
        get => isEnabled; 
        set
        {
            isChecked = value;
            isEnabled = value;
            RaisePropertyChanged(nameof(IsChecked));
            RaisePropertyChanged(nameof(IsEnabled));
        }; 
    }

    private bool isChecked;
}

uj5u.com热心网友回复:

据我了解,您想让系结到“父”的物件(MenuItem => MultiplexerVM)依赖于其子集合的属性(CheckBox => MultiplexVM.IsChecked,它是 中的一个项目MultiplexerVM.Multiplexes

在这种情况下,子级必须以某种方式知道其父级(当子级更改时,它必须将更改“推送”给父级;换句话说,必须在发生更改时通知父级)。

我可以想到两种方法来做到这一点:

  • 在 VM 级别:在 every 中MultiplexVM,设定对父视图模型或集合的参考,然后您可以在CanCheckAll / CanUncheckAll每次孩子IsChecked更改更新功能(无论您如何实作它)(乏味;我想您也可以使用事件来执行此操作,但是将PropertyChanged处理程序附加到每个子项也有点多)

  • 通过使用 GUI 级别作弊:您可以CanCheckAll / CanUncheckAllCheckBox单击更新功能

下面是如何实作第二个版本的示例。

在您的MultiplexerVM

public bool CanCheckAll => Multiplexes.Any(a => !a.IsChecked);
public bool CanUncheckAll => Multiplexes.Any(a => a.IsChecked);

public void RefreshCheckUncheckAll()
{
    NotifyPropertyChanged(nameof(CanCheckAll));
    NotifyPropertyChanged(nameof(CanUncheckAll));
}

然后,呼叫RefreshCheckUncheckAll()CheckAll命令执行情况和:

private void CheckBox_Click(object sender, RoutedEventArgs e)
{
    ((MultiplexerVM)this.DataContext).RefreshCheckUncheckAll();
}

然后,xaml 将如下所示:

<ListView ItemsSource="{Binding Multiplexes}" SelectedItem="{Binding SelectedMultiplex}">
        <ListView.ContextMenu>
            <ContextMenu>
                <MenuItem
                    Command="{Binding CheckAll}"
                    CommandParameter="{StaticResource True}"
                    IsEnabled="{Binding CanCheck}">
                    <MenuItem.Header>
                        <TextBlock Text="Check all" />
                    </MenuItem.Header>
                </MenuItem>
                <MenuItem
                    Command="{Binding CheckAll}"
                    CommandParameter="{StaticResource False}"
                    IsEnabled="{Binding CanUncheck}">
                    <MenuItem.Header>
                        <TextBlock Text="Uncheck all" />
                    </MenuItem.Header>
                </MenuItem>
            </ContextMenu>
        </ListView.ContextMenu>
        <ListView.View>
            <GridView>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Margin="3" Text="{Binding Name}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox Margin="3" IsChecked="{Binding IsChecked}" Click="CheckBox_Click" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>

    </ListView>
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *