同时更改两个列表

本文关键字:两个 列表 | 更新日期: 2023-09-27 17:56:48

在我的应用程序模型中,我有一个字符串列表。在我的视图模型中,我有一个ObservableCollection<string>,它使用模型中的列表进行初始化。我想将列表与我的可观察集合同步,因此当我们更改它时,它也会更改列表。

我想出了两种方法来实现这一目标:

  1. 为列表制作一个包装器,看起来像我的可观察集合。
  2. 使用模型中的列表初始化新的可观察集合,并为CollectionChanged事件附加事件处理程序。

至于第一种方式:

public class ObservableWrapper<T> : IList<T>, IEnumerator<T>,
INotifyCollectionChanged, INotifyPropertyChanged
{
    IList<T> _list;
    IEnumerator<T> enumer;
    public ObservableWrapper(IList<T> list)
    {
        _list = list;
    }
    public T this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, _list[index]));
            _list[index] = value;
        }
    }
    public int Count => _list.Count;
    public bool IsReadOnly => false;
    public T Current => enumer.Current;
    object IEnumerator.Current => Current;
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;
    public void Add(T item)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _list.Count));
        _list.Add(item);
    }
    public void Clear()
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        _list.Clear();
    }
    public bool Contains(T item) => _list.Contains(item);
    public void CopyTo(T[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }
    public void Dispose() { }
    public IEnumerator<T> GetEnumerator()
    {
        enumer = _list.GetEnumerator();
        return enumer;
    }
    public int IndexOf(T item) => _list.IndexOf(item);
    public void Insert(int index, T item)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        _list.Insert(index, item);
    }
    public bool MoveNext() => enumer.MoveNext();
    public bool Remove(T item) => _list.Remove(item);
    public void RemoveAt(int index)
    {
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _list[index], index));
        _list.RemoveAt(index);
    }
    public void Reset()
    {
        enumer.Reset();
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

对于第二个:

var list = new List<string>{ "aba", "abacaba" };
var obscol = new ObservableCollection<string>(list);
obscol.CollectionChanged += (s,e) =>
                            {
                                if (e.Action == NotifyCollectionChangedAction.Add)
                                {
                                    list.Add((string)e.NewItems[0]);
                                }
                                // etc
                            }
obscol.Add("test");
//now list has "aba", "abacaba", "test"

我认为第二种方式是不好的,因为新的可观察集合创建并将列表中的所有项目复制到这个新的可观察集合中。但在第一种情况下,列表元素不会被复制。

我应该选择哪种方式?

编辑:

在我的应用程序模型中,我有一个电子邮件列表(用于简化的字符串列表)。视图具有列表框,该列表框与列表中的项绑定到可观察集合。
当用户按下某个按钮时,电子邮件将从可观察集合以及应用程序模型的列表中删除。

编辑2:

public class Model
{
    public List<string> List { get; set; }
}

视图模型

public class VM
{
    public Model _model;
    public ObservableWrapper<string> WrapperList { get; set; }
    public VM(Model model)
    {
        _model = model;
        WrapperList = new ObservableWrapper<string>(_model.List);
        Command = new DelegateCommand<object>(CommandExecuted);
    }
    public DelegateCommand<object> Command { get; }
    private void CommandExecuted(object obj)
    {
        WrapperList.RemoveAt(0); //0 for example and simplify
    }
}
public class DelegateCommand<T> : System.Windows.Input.ICommand
{
    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;
    public DelegateCommand(Action<T> execute) : this(execute, null) { }
    public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }
    public bool CanExecute(object parameter)
    {
        return _canExecute?.Invoke((T)parameter) ?? true;
    }
    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
    public event EventHandler CanExecuteChanged;
    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

视图/xaml

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <ListBox ItemsSource="{Binding WrapperList}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>        
    <Button Command="{Binding Command}"
            Grid.Row="1"/>
</Grid>

视图/代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        var model = new Model();
        model.List = new List<string> { "11", "22","33","44" };
        DataContext = new VM(model);
        InitializeComponent();
    } 
}

同时更改两个列表

这是您的代码的变体,需要注意的要点是:

  • 模型:重构为更加面向对象,更接近现实世界的情况,现在是对象之间的父子关系
  • 视图模型:现在实现 INotifyPropertyChanged 以通知框架元素有关导致然后更新其绑定属性的更改
  • 视图模型:添加了属性 SelectedChild,用于存储所选项目。此项目将是点击删除按钮时删除的项目。
  • 委托命令不是通用的,因为不会将任何参数传递给命令。相反,它将使用 SelectedChild 属性
  • 观点:现在对模型一无所知。注释 从视图模型指向选定子项的属性

代码:

    public class Model
    {
        public Model() 
        {
            ChildList = new HashSet<Child>();
        }
        public ICollection<Child> ChildList { get; set; }
    }
    public class Child
    {
        public string Name { get; set; }
    }

    //View Model, now implements INotifyPropertyChanged    
    public class VM: INotifyPropertyChanged{
        private Model _model; 
        public VM()
        {
            var model = new Model();
            model.ChildList.Add(new Child { Name = "Child 1" });
            model.ChildList.Add(new Child { Name = "Child 2" });
            model.ChildList.Add(new Child { Name = "Child 3" });
            model.ChildList.Add(new Child { Name = "Child 4" });
            _model = model;
            Command = new DelegateCommand(CommandExecuted);
        }
        public ObservableCollection<Child> ChildCollection
        {
            get
            {
                return new ObservableCollection<Child>(_model.ChildList);
            }
        }
        public DelegateCommand Command { get; set; }
        private void CommandExecuted()
        {
            _model.ChildList.Remove(SelectedChild);
            OnPropertyChanged("ChildCollection");
        }
        public Child SelectedChild { get; set; }   
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
       {
           var eventHandler = this.PropertyChanged;
           if (eventHandler != null)
           {
               eventHandler(this, new PropertyChangedEventArgs(propertyName));
           }
       }
    }

    public class DelegateCommand : ICommand
    {
        private readonly Action _execute;    
        public DelegateCommand(Action execute)
        {
            _execute = execute;
        }
        public void Execute(object parameter)
        {
            _execute();
        }
        public bool CanExecute(object parameter)
        {
            return true;
        }
        public event EventHandler CanExecuteChanged;    
        }
    //View    
<ListBox ItemsSource="{Binding Path = ChildCollection}" SelectedItem="{Binding SelectedChild}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding Name}"/>
         </DataTemplate>
     </ListBox.ItemTemplate>
</ListBox>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new VM();
        }
    }

你确实意识到

  • ObservableCollection<T>Collection<T>的一个子类型,它[显式地]实现IList<T>,并且

  • List<T>同样实现了IList<T>

这意味着ObservableCollection<T>几乎可以与任何其他IList<T>实现互换:在两个地方都使用该ObservableCollection<T>,并在必要时将其转换为IList<T>

您能否说明为什么您需要模型中的列表和视图模型中的可观察集合?

是否可以在模型和视图模型中使用 ObservableCollection?

在这种情况下,您可以要求视图模型可观察集合返回模型可观察集合。

样本:

class Model
{
    //model's property. Instead of your list
    public ObservableCollection<string> ModelCol { get; set; }
}
class ViewModel
{
    Model model;
    public ViewModel ()
    {
        model = new Model();
    }
    //view model property returning model property. If you want, you can do
    //the customization here.
    public ObservableCollection<string> ViewModelCol {
    get 
    {
        return model.ModelCol; 
    }
    set 
    {
        //return collection from your model.
        model.ModelCol = value; 
    }
}
也可以在

模型中使用 ObservableCollection。如果它不会对您的需求造成任何问题,则可以使用相同的概念。