wpf 如何将ItemsControl成员得ICommand得依赖属性绑定到父视图模型?

new9mtju  于 2022-11-18  发布在  其他
关注(0)|答案(1)|浏览(152)

我有一个自定义用户控件,它有一个复选框和一个带有支持依赖项属性的按钮。
我还有一个包含ItemsControl的窗口,我将该控件的“ItemsSource”绑定到包含所述用户控件的ObservableCollection。
我的目标是能够访问窗口的视图模型中的依赖项属性,这样我就可以检查集合中每个成员的复选框是否被选中,以及当用户单击按钮时-从集合中删除包含该按钮的用户控件。

这是我的代码

用户控件XAML:

<CheckBox IsChecked="{Binding cbChecked,
            Mode=OneWayToSource,
            UpdateSourceTrigger=PropertyChanged,
            RelativeSource={RelativeSource FindAncestor,
            AncestorType=UserControl}}"/>

<Button Content="X"
        Command="{Binding RemoveCommand,
                    RelativeSource={RelativeSource FindAncestor,
                    AncestorType=UserControl}}">

(note我不确定是否需要“UpdateSourceTrigger”)
用户控件代码隐藏:

public ICommand RemoveCommand
    {
        get { return (ICommand)GetValue(RemoveCommandProperty); }
        set { SetValue(RemoveCommandProperty, value); }
    }

    // Using a DependencyProperty as the backing store for RemoveCommand.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RemoveCommandProperty =
        DependencyProperty.Register("RemoveCommand", typeof(ICommand), typeof(CustomUserControl), new PropertyMetadata(OnAnyPropertyChanged));

    public bool cbChecked
    {
        get { return (bool)GetValue(cbCheckedProperty); }
        set { SetValue(cbCheckedProperty, value); }
    }

    // Using a DependencyProperty as the backing store for cbChecked.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty cbCheckedProperty =
        DependencyProperty.Register("cbChecked", typeof(bool), typeof(CustomUserControl), new PropertyMetadata(OnAnyPropertyChanged));

    static void OnAnyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        => (obj as CustomUserControl).OnAnyPropertyChanged(args);

    void OnAnyPropertyChanged(DependencyPropertyChangedEventArgs args)
        => AnyPropertyChanged?.Invoke(this, args);

Windows XAML:

<ScrollViewer VerticalScrollBarVisibility="Visible" Grid.Row="0">
            <ItemsControl ItemsSource="{Binding ControlsCollection}" HorizontalAlignment="Stretch" VerticalAlignment="Top">
                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="FrameworkElement.Margin" Value="5,20,0,0"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
            </ItemsControl>
        </ScrollViewer>

当用户单击“X”按钮时,如何使保存该按钮的用户控件从集合中移除,并且能够从窗口的视图模型中检查复选框的“选中”状态?

c7rzv4ha

c7rzv4ha1#

第一,视图模型中不能包含视图的元素,ObservableCollection的项应该是项的视图模型,而不是UserControl。
第二,在这种情况下,您不需要使用UserControl。使用具有相同元素的DataTemplate就足够了。
例如,最小项目的视图模型如下所示。

using CommunityToolkit.Mvvm.ComponentModel;

public partial class ItemViewModel : ObservableObject
{
    [ObservableProperty]
    public bool _checked;
}

然后,窗口的视图模型。

using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public class MainWindowViewModel : ObservableObject
{
    public ObservableCollection<ItemViewModel> ItemsCollection { get; } = new();

    public MainWindowViewModel()
    {
        ItemsCollection.Add(new ItemViewModel());
        ItemsCollection.Add(new ItemViewModel());
        ItemsCollection.Add(new ItemViewModel());
    }

    public ICommand RemoveCommand => _removeCommand ??= new RelayCommand<ItemViewModel>(item => Remove(item));
    private RelayCommand<ItemViewModel>? _removeCommand;

    private void Remove(ItemViewModel? item)
    {
        if (item is not null)
        {
            Debug.WriteLine($"Checked:{item.Checked}");

            ItemsCollection.Remove(item);
        }
    }
}

窗口的视图。ItemsControl.ItemTemplate的DataTemplate元素与UserControl相同,但绑定已修改。

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApp"
        Title="MainWindow"
        Width="600" Height="300">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <ScrollViewer VerticalScrollBarVisibility="Visible">
        <ItemsControl ItemsSource="{Binding ItemsCollection}"
                      HorizontalAlignment="Stretch" VerticalAlignment="Top">
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="FrameworkElement.Margin" Value="5,20,0,0"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <CheckBox IsChecked="{Binding Checked, Mode=OneWayToSource}"/>
                        <Button Content="X"
                                Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
                                CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>

相关问题