同时更改两个列表

时间:2015-06-16 19:10:28

标签: c# wpf

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

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

  1. 为列表创建一个看起来像我的可观察集合的包装器。
  2. 使用模型中的列表初始化一个新的可观察集合,并附加CollectionChanged事件的事件处理程序。
  3. 至于第一种方式:

    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"
    

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

    我应该选择哪种方式?

    编辑:

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

    编辑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();
        } 
    }
    

3 个答案:

答案 0 :(得分:2)

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

  • 模型:重构为更加面向对象并更接近现实世界的情况,它现在是对象之间的父子关系
  • 查看模型:现在实现INotifyPropertyChanged以通知框架元素有关更改然后更新其绑定属性的更改
  • 查看模型:添加了属性SelectedChild,用于存储所选项目。此项目将是按下“删除”按钮时删除的项目。
  • DelegateCommand不是通用的,因为没有参数将传递给命令。相反,它将使用SelectedChild属性
  • 查看:现在还没有关于模型的知识。注意SelectedItem属性指向视图模型中的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();
        }
    }

答案 1 :(得分:2)

你意识到了

  • ObservableCollection<T>Collection<T>的子类型,[明确]实现IList<T>

  • List<T>同样实施IList<T>

这意味着ObservableCollection<T>几乎可以与IList<T>的任何其他实现互换:在两个地方使用ObservableCollection<T>,将其投放到IList<T>其中和必要的。

答案 2 :(得分:0)

请您说明为什么需要模型中的列表和ViewModel中的ObservableCollection?

您可以在模型和视图模型中使用ObservableCollection吗?

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

样品:

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; 
    }
}

也可以在Model中使用ObservableCollection。如果它不会对您的要求造成任何问题,您可以使用相同的概念。