【发布时间】:2015-09-01 18:42:01
【问题描述】:
在我的应用程序模型中,我有一个字符串列表。在我的视图模型中,我有一个ObservableCollection<string>,它使用我的模型中的列表进行了初始化。我想将列表与我的 observable 集合同步,所以当我们更改它时,它也会更改列表。
我想出了两种方法来实现这一点:
- 为看起来像我的可观察集合的列表制作一个包装器。
- 使用模型中的列表初始化一个新的 observable 集合,并为
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"
我认为第二种方式不好,因为新的 observable 集合创建并将列表中的所有项目复制到这个新的 observable 集合中。但在第一种情况下,列表元素不会被复制。
我应该选择哪种方式?
编辑:
在我的应用程序模型中,我有一个电子邮件列表(用于简化的字符串列表)。
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();
}
}
【问题讨论】:
-
请说明您想要/需要维护原始列表的原因?我采用第二种方法。
-
如果你正确实现了 mvvm 模式,从视图模型中的 observable 集合中删除项目就足够了。
-
发布您的代码,对于模型、视图模型和视图,我相信您在模式实现中遗漏了一些要点。
-
@E-Bat 代码附加到帖子