2015-06-16 76 views
2

在我的應用程序模型中,我有一個字符串列表。在我的視圖模型中,我有一個ObservableCollection<string>,它使用我的模型列表進行初始化。我想將列表與我的可觀察集合同步,所以當我們更改它時,它也會更改列表。同時更改兩個列表

我想出了兩種方法來實現這一目標:

  1. 做出看起來像我的觀察的集合名單的包裝。
  2. 使用模型中的列表初始化新的可觀察集合,並附加CollectionChanged事件的事件處理程序。

對於第一種方式:

​​

對於第二:

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(); 
    } 
} 
+1

給你一個理由說明你爲什麼需要/需要保持原始列表?我採用第二種方法。 –

+0

查看更新後的帖子 – snipervld

+0

如果您正確執行mvvm模式,則從視圖模型中的可觀察集合中刪除項目應該足夠了。 –

回答

2

這裏是你的代碼的變化,需要注意的要點是:

  • 型號:重構爲更加面向對象,更接近現實世界的情況下,現在是對象
  • 之間的父子關係
  • 查看模型:現在實現INotifyPropertyChanged以通知框架元素有關導致然後更新其綁定屬性的更改
  • 查看模型:已添加屬性SelectedChild存儲所選項目。這個項目將是刪除按鈕時刪除的項目。
  • DelegateCommand不是通用的,因爲沒有參數將傳遞給命令。相反,它將使用SelectedChild屬性
  • 查看:現在還沒有關於Model的知識。注意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(); 
     } 
    } 
+0

不要在列表中大量顯示項目中的性能?從msdn:ObservableCollection 構造函數(IEnumerable )「初始化包含從指定集合複製的元素的ObservableCollection 類的新實例。 – snipervld

+0

對於大型集合,您不希望直接公開ObserbableCollection,而是可以使用ICollectionView和CollectionViewSource來頁面結果。 (請參閱http://stackoverflow.com/questions/5305883/mvvm-paging-sorting)。您也可以使用UI虛擬化(來自msdn:這意味着項目的容器生成和關聯的佈局計算被推遲到項目可見時),Listbox支持UI虛擬化(https://msdn.microsoft.com/en- us/library/cc716879.aspx) –

0

能否請你說明爲什麼你需要在模型的列表,並在視圖模型一個ObservableCollection?

您是否可以在模型和視圖模型中使用ObservableCollection?

在這種情況下,您可以要求viewmodel observable集合返回模型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; 
    } 
} 

這是正常的模型中使用的ObservableCollection也。如果它不會對您的要求造成任何問題,您可以使用相同的概念。

+0

不同意,模型必須儘可能POCO。我寧願使用一個接口,可能ICollection

+0

我看到人們主要使用列表而不是模型中的observablecollection。 dot Net 4.0中的 – snipervld

+0

ObservableCollection已從WindowsBase移至System Dll,MS明確指示ObservableCollections適用於您的模型。 –

2

你知道那

  • ObservableCollection<T>Collection<T>,其中[明確]實現IList<T>一個亞型,那

  • List<T>同樣執行IList<T>

這意味着ObservableCollection<T>可以幾乎互換與任何其他實施IList<T>:使用ObservableCollection<T>在這兩個地方,它轉換成一IList<T>其中,如果必要的。