简体   繁体   English

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

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

I have a checklist view that has 2 ScrollViewers. 我有一个清单视图,其中包含2个ScrollViewers。 One checklist is for incomplete items, the other is for complete items. 一个清单用于不完整的项目,另一个清单用于完整的项目。 They are populated by 2 separate observable collections and bound to by ItemsControls. 它们由2个独立的可观察集合填充,并由ItemsControls绑定。

The UserControl has a button, when clicked will move that 'check' to the other collection. UserControl有一个按钮,单击该按钮会将“检查”移到另一个集合。

Currently the way I have this setup is in the ViewModel that's the DataContext for the UserControl there is a public event that is subscribed to by the main window's VM by using: 目前,我进行此设置的方式是在ViewModel(即UserControl的DataContext)中,有一个公共事件,它由主窗口的VM通过以下方式订阅:

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

where cli is the checklist item. 其中cli是清单项目。

then the OnCompleteChanged finds the appropriate View object by using: 然后OnCompleteChanged通过使用以下命令找到合适的View对象:

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

It's pretty obvious this breaks MVVM and I'm looking for a way around it (CheckListItem is the View, and CheckItemVM is it's DataContext ViewModel). 很明显,这破坏了MVVM,我正在寻找解决方法(CheckListItem是View,而CheckItemVM是DataContext ViewModel)。 Reasoning for the boxed type is I've got another UserControl that will have instances inside both, which are basically section labels, and I need to be able to sort my observable collections where there is an association between the checklistitem to a specific section by name. 盒装类型的原因是,我还有另一个UserControl,该控件在两个控件中都具有实例,这些实例基本上是节标签,并且我需要能够对可观察的集合进行排序,其中检查列表项与特定节之间的名称相关联。

This can be done in MVVM using commands, and bindings.... 这可以在MVVM中使用命令和绑定来完成。

The idea that I propouse here is to create a command in the Windows view model, that manage the check command, and this command to receive the item view model in the params, then manage the the things in the command. 我在这里提出的想法是在Windows视图模型中创建一个命令,该命令管理check命令,该命令用于在params中接收项目视图模型,然后管理该命令中的内容。 I'm going to show you a simple example, using MvvmLight library: 我将使用MvvmLight库向您展示一个简单的示例:

在此处输入图片说明

The model: 该模型:

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

}

A simple model with two property, one for the name (an identifier) and another for the check status. 一个具有两个属性的简单模型,一个属性用于名称(标识符),另一个属性用于检查状态。

Now in the Main View Model, (or Windows view model like you want).... 现在在“主视图模型”中(或所需的Windows视图模型中)。

First the Collections, one for the checked items, and another for the unchecked items: 首先是集合,一个用于检查的项目,另一个用于未检查的项目:

    #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

And the command: 和命令:

    #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

The magic here could be in the Data binding, in this case I used command parameter and the FindAncestor binding, check the Data Template: 这里的魔术可能在数据绑定中,在这种情况下,我使用了命令参数和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>

One data template for checked items, and another for unchecked items. 一个数据模板用于选中的项目,另一个用于未选中的项目。 Now the usage, this is simpler: 现在的用法,这更简单:

    <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}"/>

This is a cleaner solution, hope is helps. 这是一个更清洁的解决方案,希望对您有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM