WPF MVVM 通过事件将 UserControl 从一个 ObservableCollection 移动到另一个

WPF MVVM moving a UserControl from one ObservableCollection to another by event

我有一个包含 2 个 ScrollViewer 的清单视图。一份清单用于不完整的项目,另一份用于完整的项目。它们由 2 个独立的可观察集合填充并由 ItemsControls 绑定。

UserControl 有一个按钮,单击时会将 'check' 移动到另一个集合。

目前我进行此设置的方式是在 ViewModel 中,它是 UserControl 的 DataContext 有一个 public 事件,主 window 的虚拟机使用以下方式订阅了该事件:

((CheckItemVM) ((CheckListItem) cli).DataContext).CompleteChanged += OnCompleteChanged;

其中 cli 是清单项目。

然后 OnCompleteChanged 通过使用找到合适的视图对象:

foreach (object aCheck in Checks)
        {
            if (aCheck.GetType() != typeof (CheckListItem)) continue;
            if (((CheckListItem) aCheck).DataContext == (CheckItemVM) sender)
            {
                cliToMove = (CheckListItem) aCheck;
                break;
            }
        }

很明显这会破坏 MVVM,我正在寻找解决方法(CheckListItem 是视图,CheckItemVM 是 DataContext ViewModel)。盒装类型的原因是我有另一个 UserControl 将在两者中都有实例,这基本上是部分标签,我需要能够对我的可观察集合进行排序,其中 checklistitem 与按名称的特定部分之间存在关联.

这可以在 MVVM 中使用命令和绑定来完成....

我在这里提出的想法是在 Windows 视图模型中创建一个命令,管理检查命令,这个命令接收参数中的项目视图模型,然后管理这些东西在命令中。我将向您展示一个使用 MvvmLight 库的简单示例:

型号:

public class ItemViewModel : ViewModelBase
{
    #region Name

    public const string NamePropertyName = "Name";

    private string _name = null;

    public string Name
    {
        get
        {
            return _name;
        }

        set
        {
            if (_name == value)
            {
                return;
            }

            RaisePropertyChanging(NamePropertyName);
            _name = value;
            RaisePropertyChanged(NamePropertyName);
        }
    }

    #endregion

    #region IsChecked

    public const string IsCheckedPropertyName = "IsChecked";

    private bool _myIsChecked = false;
    public bool IsChecked
    {
        get
        {
            return _myIsChecked;
        }
        set
        {
            if (_myIsChecked == value)
            {
                return;
            }

            RaisePropertyChanging(IsCheckedPropertyName);
            _myIsChecked = value;
            RaisePropertyChanged(IsCheckedPropertyName);
        }
    }

    #endregion

}

一个简单的模型,有两个 属性,一个用于名称(标识符),另一个用于检查状态。

现在在主视图模型中,(或 Windows 您想要的视图模型)...

第一个Collections,一个是选中的项目,另一个是未选中的项目:

    #region UncheckedItems

    private ObservableCollection<ItemViewModel> _UncheckedItems;

    public ObservableCollection<ItemViewModel> UncheckedItems
    {
        get { return _UncheckedItems ?? (_UncheckedItems = GetAllUncheckedItems()); }
    }

    private ObservableCollection<ItemViewModel> GetAllUncheckedItems()
    {
        var toRet = new ObservableCollection<ItemViewModel>();

        foreach (var i in Enumerable.Range(1,10))
        {
            toRet.Add(new ItemViewModel {Name = string.Format("Name-{0}", i), IsChecked = false});
        }

        return toRet;
    }        

    #endregion

    #region CheckedItems

    private ObservableCollection<ItemViewModel> _CheckedItems;

    public ObservableCollection<ItemViewModel> CheckedItems
    {
        get { return _CheckedItems ?? (_CheckedItems = GetAllCheckedItems()); }
    }

    private ObservableCollection<ItemViewModel> GetAllCheckedItems()
    {
        var toRet = new ObservableCollection<ItemViewModel>();

        foreach (var i in Enumerable.Range(11, 20))
        {
            toRet.Add(new ItemViewModel { Name = string.Format("Name-{0}", i), IsChecked = true });
        }

        return toRet;
    }

    #endregion

和命令:

    #region CheckItem

    private RelayCommand<ItemViewModel> _CheckItemCommand;

    public RelayCommand<ItemViewModel> CheckItemCommand
    {
        get { return _CheckItemCommand ?? (_CheckItemCommand = new RelayCommand<ItemViewModel>(ExecuteCheckItemCommand, CanExecuteCheckItemCommand)); }
    }

    private void ExecuteCheckItemCommand(ItemViewModel item)
    {
        //ComandCode
        item.IsChecked = true;
        UncheckedItems.Remove(item);
        CheckedItems.Add(item);
    }

    private bool CanExecuteCheckItemCommand(ItemViewModel item)
    {
        return true;
    }

    #endregion

这里的魔法可能在于数据绑定,在本例中我使用了命令参数和 FindAncestor 绑定,检查数据模板:

        <DataTemplate x:Key="UncheckedItemDataTemplate">
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top"/>
                    <CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" IsChecked="{Binding IsChecked}" IsEnabled="False"/>
                    <Button Content="Check" Width="75" Command="{Binding DataContext.CheckItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" CommandParameter="{Binding Mode=OneWay}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
        <DataTemplate x:Key="CheckedItemDataTemplate">
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top"/>
                    <CheckBox HorizontalAlignment="Left" VerticalAlignment="Top" IsChecked="{Binding IsChecked}" IsEnabled="False"/>
                </StackPanel>
            </Grid>
        </DataTemplate>

一个数据模板用于选中的项目,另一个用于未选中的项目。现在的用法,这个更简单:

    <ListBox Grid.Row="2" Margin="5" ItemsSource="{Binding UncheckedItems}" ItemTemplate="{DynamicResource UncheckedItemDataTemplate}"/>
    <ListBox Grid.Row="2" Margin="5" Grid.Column="1" ItemsSource="{Binding CheckedItems}" ItemTemplate="{DynamicResource CheckedItemDataTemplate}"/>

这是一个更简洁的解决方案,希望对您有所帮助。