2012-03-24 104 views
1

好吧,我可能會問一個古老的問題,但我沒有得到我的場景中描述的任何一個。WPF中的DataBind Listbox SubSelection

我有一個可以包含多個子對象的Oject。例如。一個項目obejct可以有幾個資源對象。我有一個具有超級子對象的ObservaleCollection(在我的例子中是Resource對象)。我也有Project對象中的另一個ObservableCollection包含現有的孩子。

在WPF窗口應用程序中向用戶展示這一點的最佳方式是什麼?我還需要爲他們提供一種方法來繪製映射。

我最初的想法是使用經典雙列表方法,有兩個列表框,但我不確定單獨操作視圖層是多麼容易。

[Resoure Collection]    [Resoure Collection in a Project] 
--------------------    --------------------------------- 
|Resource 1  |  >  |Resource 3      | 
|Resource 2  |  >>  |Resource 4      | 
|Resource 5  |  <  |        | 
|Resource 6  |  <<  |        | 
|Resource 7  |    |        | 

我需要4名類似的不同對象的映射類似的UI。我試圖將此移到用戶控件,但看起來像我不能在UserControl中具有通用集合(私人ObservableCollection)。

來自體驗會員的任何想法?

/* ** * ** * ** * ** * ** * ** * ** * ** * * * * ** * ** * ** * ** * ** * ** * ****/ 編輯:這是我走到這一步,請注意,我用用戶控件去,因爲我需要在多個屏幕上相同的用戶界面,我覺得UserCOntrol會給我更多可管理的代碼。

XAML用戶控件

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      mc:Ignorable="d" 
      x:Class="TimeTracker.ItemsSelectionLists" 
      x:Name="ItemsSelectionControl"> 
    <Grid x:Name="LayoutRoot"> 
     <Grid Background="#FFF9FDFD" 
       Margin="0,0,0,0"> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition /> 
       <ColumnDefinition Width="Auto" /> 
       <ColumnDefinition /> 
      </Grid.ColumnDefinitions> 
      <TextBlock x:Name="SourceHeading" 
         Grid.Column="0" 
         Margin="8,8,0,0" 
         TextWrapping="Wrap" 
         Text="Whole Team" 
         VerticalAlignment="Top" /> 
      <ListBox x:Name="SourceItemsList" 
        Grid.Column="0" 
        Margin="8,30,8,8" 
        MinWidth="150" 
        SelectionMode="Multiple" 
        ItemsSource="{Binding Path=Collection1}"/> 
      <StackPanel Grid.Column="1" 
         Margin="0" 
         Orientation="Vertical" 
         VerticalAlignment="Center"> 
       <Button Content="&gt;" 
         Height="25" 
         Width="25" /> 
       <Button Content="&gt;&gt;" 
         Height="25" 
         Width="25" /> 
       <Button Content="&lt;" 
         Height="25" 
         Width="25" /> 
       <Button Content="&lt;&lt;" 
         Height="25" 
         Width="25" /> 
      </StackPanel> 
      <TextBlock x:Name="TargetHeading" 
         Grid.Column="2" 
         Margin="8,8,8,0" 
         TextWrapping="Wrap" 
         Text="Current Team" 
         VerticalAlignment="Top" /> 
      <ListBox x:Name="SelectedItemsList" 
        Grid.Column="2" 
        Margin="8,30,8,8" 
        MinWidth="150" 
        ItemsSource="{Binding Path=Collection2}"/> 
     </Grid> 
    </Grid> 
</UserControl> 

代碼:

/// <summary> 
    /// Interaction logic for ItemsSelectionLists.xaml 
    /// </summary> 
    public partial class ItemsSelectionLists: UserControl 
    { 
     [Bindable(true)] 

     internal ObservableCollection<TrackerItem> SourceList 
     { 
      get 
      { 
       return _vm.Collection1; 
      } 
      set 
      { 
       _vm.Collection1 = value; 
      } 
     } 

     private readonly ViewModel _vm; 

     public ItemsSelectionLists() 
     { 
      this.InitializeComponent(); 
      _vm = new ViewModel(); 
      this.DataContext = _vm; 
     } 
    } 

    public class ViewModel : INotifyPropertyChanged 
    { 
     #region Properties 

     private ObservableCollection<TrackerItem> _collection1; 

     /// <summary> 
     /// This is the first collection. 
     /// </summary> 
     internal ObservableCollection<TrackerItem> Collection1 
     { 
      get 
      { 
       return _collection1; 
      } 

      set 
      { 
       if (value != _collection1) 
       { 
        _collection1 = value; 
        NotifyPropertyChanged("Collection1"); 
       } 
      } 
     } 

     private ObservableCollection<TrackerItem> _collection2; 

     /// <summary> 
     /// This is the second collection. 
     /// </summary> 
     internal ObservableCollection<TrackerItem> Collection2 
     { 
      get 
      { 
       return _collection2; 
      } 

      set 
      { 
       if (value != _collection2) 
       { 
        _collection2 = value; 
        NotifyPropertyChanged("Collection2"); 
       } 
      } 
     } 

     #endregion 

     #region Constructors 

     /// <summary> 
     /// Default constructor. 
     /// </summary> 
     public ViewModel() 
     { 
      // Create initial collections. 

      // Populate first collection with sample data 
      _collection1 = new ObservableCollection<TrackerItem>(); 
      // Seconf collection is empty 
      _collection2 = new ObservableCollection<TrackerItem>(); 
     } 

     #endregion 

     #region INotifyPropertyChanged 

     public event PropertyChangedEventHandler PropertyChanged; 

     private void NotifyPropertyChanged(String info) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(info)); 
      } 
     } 

     #endregion 
    } 

主窗口

<TabItem Header="Resource Allocation"> 
    <local:ItemsSelectionLists x:Name="ResourceSelection"/> 
</TabItem> 

代碼

ResourceSelection.SourceList = MainObject.Resources; 
//error CS0029: Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection<TimeTracker.Resource>' to 'System.Collections.ObjectModel.ObservableCollection<TimeTracker.TrackerItem>' 
+1

我正在做一個樣本。我會很快得到它。 – Xcalibur37 2012-03-24 01:11:27

+0

謝謝,期待它.. – Adarsha 2012-03-24 01:14:09

回答

1

這不是優雅或拋光,但這是一個可行的示例。如果我做了這個拋光,我會實現真正的MVVM,但作爲一個例子,這會讓你開始。

XAML:

<Window x:Class="TwoListboxes.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="800"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="*" /> 
      <ColumnDefinition Width="120" /> 
      <ColumnDefinition Width="*" /> 
     </Grid.ColumnDefinitions> 

     <ListBox x:Name="List1" Grid.Column="0" 
       Height="200" Margin="10" 
       SelectionMode="Multiple" 
       ItemsSource="{Binding Path=Collection1}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Path=Name}" /> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

     <StackPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center"> 
      <Button Content="&gt;" Width="60" Click="MoveRightEvent" /> 
      <Button Content="&gt;&gt;" Width="60" Click="MoveAllRightEvent" /> 
      <Button Content="&lt;&lt;" Width="60" Click="MoveAllLeftEvent" /> 
      <Button Content="&lt;" Width="60" Click="MoveLeftEvent" /> 
     </StackPanel> 

     <ListBox x:Name="List2" Grid.Column="2" 
       Height="200" Margin="10" 
       ItemsSource="{Binding Path=Collection2}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Path=Name}" /> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
    </Grid> 
</Window> 

代碼隱藏:

/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    #region Members 

    private readonly ViewModel<TrackerItem> _vm; 

    #endregion 

    public MainWindow() 
    { 
     // Get viewmodel and set context 
     _vm = new ViewModel<TrackerItem>(); 
     _vm.Collection1 = new ObservableCollection<TrackerItem> 
      { 
       new TrackerItem { Name = "Item1", Value = "1"}, 
       new TrackerItem { Name = "Item2", Value = "2"}, 
       new TrackerItem { Name = "Item3", Value = "3"}, 
       new TrackerItem { Name = "Item4", Value = "4"}, 
       new TrackerItem { Name = "Item5", Value = "5"}, 
       new TrackerItem { Name = "Item6", Value = "6"}, 
       new TrackerItem { Name = "Item7", Value = "7"}, 
       new TrackerItem { Name = "Item8", Value = "8"}, 
       new TrackerItem { Name = "Item9", Value = "9"}, 
       new TrackerItem { Name = "Item10", Value = "10"} 
      }; 
     this.DataContext = _vm; 

     // Initialize UI 
     InitializeComponent(); 
    } 

    /// <summary> 
    /// Moves selected items in a list from one collection to another. 
    /// </summary> 
    /// <param name="list"></param> 
    /// <param name="source"></param> 
    /// <param name="destination"></param> 
    private void MoveItems(ListBox list, 
     ObservableCollection<TrackerItem> source, 
     ObservableCollection<TrackerItem> destination) 
    { 
     if (list.SelectedItems.Count > 0) 
     { 
      // List for items to be removed. 
      var hitList = new List<TrackerItem>(); 

      // Move items 
      foreach (var selectedItem in list.SelectedItems) 
      { 
       var item = selectedItem as TrackerItem; 
       if (item != null) 
       { 
        // Tag item for removal 
        hitList.Add(item); 

        // Check if item is in target list 
        var targetList = (from p in destination 
             where p == item 
             select p).ToList(); 

        // Add to destination 
        if (!targetList.Any()) 
        { 
         destination.Add(item); 
        } 
       } 
      } 

      // Remove items 
      foreach (var hitItem in hitList) 
      { 
       // Remove item 
       source.Remove(hitItem); 
      } 
     } 
    } 

    /// <summary> 
    /// Moves all items from one list to another. 
    /// </summary> 
    /// <param name="source"></param> 
    /// <param name="destination"></param> 
    private void MoveAllItems(
     ObservableCollection<TrackerItem> source, 
     ObservableCollection<TrackerItem> destination) 
    { 
     // List for items to be removed. 
     var hitList = new List<TrackerItem>(); 

     // Move items 
     foreach (var item in source) 
     { 
      if (item != null) 
      { 
       // Tag item for removal 
       hitList.Add(item); 

       // Check if item is in target list 
       var targetList = (from p in destination 
            where p == item 
            select p).ToList(); 

       // Add to destination 
       if (!targetList.Any()) 
       { 
        destination.Add(item); 
       } 
      } 
     } 

     // Remove items 
     foreach (var hitItem in hitList) 
     { 
      // Remove item 
      source.Remove(hitItem); 
     } 
    } 

    /// <summary> 
    /// Click event: moves selected items to the right. 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void MoveRightEvent(object sender, RoutedEventArgs e) 
    { 
     MoveItems(List1, _vm.Collection1, _vm.Collection2); 
    } 

    /// <summary> 
    /// Click event: moves all items to the right.. 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void MoveAllRightEvent(object sender, RoutedEventArgs e) 
    { 
     MoveAllItems(_vm.Collection1, _vm.Collection2); 
    } 

    /// <summary> 
    /// Click event: moves all items to the left. 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void MoveAllLeftEvent(object sender, RoutedEventArgs e) 
    { 
     MoveAllItems(_vm.Collection2, _vm.Collection1); 
    } 

    /// <summary> 
    /// Click event: moves selected items to the left. 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    private void MoveLeftEvent(object sender, RoutedEventArgs e) 
    { 
     MoveItems(List2, _vm.Collection2, _vm.Collection1); 
    } 
} 

的視圖模型:

public class ViewModel<T> : INotifyPropertyChanged 
{ 
    #region Properties 

    private ObservableCollection<T> _collection1; 

    /// <summary> 
    /// This is the first collection. 
    /// </summary> 
    public ObservableCollection<T> Collection1 
    { 
     get { return _collection1; } 

     set 
     { 
      if (value != _collection1) 
      { 
       _collection1 = value; 
       NotifyPropertyChanged("Collection1"); 
      } 
     } 
    } 

    private ObservableCollection<T> _collection2; 

    /// <summary> 
    /// This is the second collection. 
    /// </summary> 
    public ObservableCollection<T> Collection2 
    { 
     get { return _collection2; } 

     set 
     { 
      if (value != _collection2) 
      { 
       _collection2 = value; 
       NotifyPropertyChanged("Collection2"); 
      } 
     } 
    } 

    #endregion 

    #region Constructors 

    /// <summary> 
    /// Default constructor. 
    /// </summary> 
    public ViewModel() 
    { 
     // Create initial collections. 

     // Populate first collection with sample data 
     _collection1 = new ObservableCollection<T>(); 

     // Seconf collection is empty 
     _collection2 = new ObservableCollection<T>(); 
    } 

    #endregion 

    #region INotifyPropertyChanged 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 

    #endregion 
} 

TrackerItem

public class TrackerItem : INotifyPropertyChanged 
{ 
    private string _name; 

    /// <summary> 
    /// A name. 
    /// </summary> 
    public string Name 
    { 
     get { return _name; } 

     set 
     { 
      if (value != _name) 
      { 
       _name = value; 
       NotifyPropertyChanged("Name"); 
      } 
     } 
    } 

    private string _value; 

    /// <summary> 
    /// A value. 
    /// </summary> 
    public string Value 
    { 
     get { return _value; } 

     set 
     { 
      if (value != _value) 
      { 
       _value = value; 
       NotifyPropertyChanged("Value"); 
      } 
     } 
    } 

    #region INotifyPropertyChanged 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 

    #endregion 
} 
+0

謝謝你的努力Xcalibur!除了視圖模型之外,我在提問之前已經嘗試了類似的東西。我遇到的問題是在每個需要使用此控件的屏幕中,對象的類型將有所不同。因此需要在用戶控件中擁有ObservableCollection和ObservableCollection 。我會嘗試這個解決方案,然後恢復。 – Adarsha 2012-03-24 01:43:10

+0

在那種情況下,爲什麼不在所有的自定義對象下使用抽象類類而不是T?然後你可以使用Liskov替換。 – Xcalibur37 2012-03-24 01:46:17

+0

我有一個名爲TrackerItem的抽象類,然後由所有其他類派生出來。現在我嘗試了ObservableCollection SourceList,並在主窗口中試過xxx.SourceList = xxxx.Resources; 這給出了錯誤CS0029:不能隱式地將類型'System.Collections.ObjectModel.ObservableCollection '轉換爲'System.Collections.ObjectModel.ObservableCollection 我做錯了什麼? – Adarsha 2012-03-24 02:50:51

0

好的,由於Reflection和XCalibur的viewmodel思想,我能夠完成我的用戶控件,它可以顯示兩個相同類型的通用可觀察集合的內容。控件的使用者可以指定對象列表框應顯示的屬性。集合數據將被複制到本地集合,因此對集合所做的任何更改都不會更改輸入集合。

XAML:

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:TimeTracker" 
     x:Class="TimeTracker.ItemsSelectionLists" 
     x:Name="ItemsSelectionControl"> 
<UserControl.Resources> 
    <local:DummyConverter x:Key="DummyConverter" /> 
</UserControl.Resources> 
<Grid x:Name="LayoutRoot"> 
    <Grid Background="#FFF9FDFD" 
      Margin="0,0,0,0"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition /> 
      <ColumnDefinition Width="Auto" /> 
      <ColumnDefinition /> 
     </Grid.ColumnDefinitions> 
     <TextBlock Grid.Column="0" 
        Margin="8,8,0,0" 
        TextWrapping="Wrap" 
        Text="{Binding Path=LeftHeader, RelativeSource={RelativeSource AncestorType=UserControl}}" 
        VerticalAlignment="Top" /> 
     <ListBox x:Name="LeftItemsList" 
       Grid.Column="0" 
       Margin="8,30,8,8" 
       MinWidth="150" 
       SelectionMode="Multiple" 
       ItemsSource="{Binding Path=LeftCollection}" /> 
     <StackPanel Grid.Column="1" 
        Margin="0" 
        Orientation="Vertical" 
        VerticalAlignment="Center"> 
      <Button Content="&gt;" 
        Height="25" 
        Width="25" 
        Click="Button_Click" /> 
      <Button Content="&gt;&gt;" 
        Height="25" 
        Width="25" 
        Click="Button_Click" /> 
      <Button Content="&lt;" 
        Height="25" 
        Width="25" 
        Click="Button_Click" /> 
      <Button Content="&lt;&lt;" 
        Height="25" 
        Width="25" 
        Click="Button_Click" /> 
     </StackPanel> 
     <TextBlock Grid.Column="2" 
        Margin="8,8,8,0" 
        TextWrapping="Wrap" 
        Text="{Binding Path=RightHeader, RelativeSource={RelativeSource AncestorType=UserControl}}" 
        VerticalAlignment="Top" /> 
     <ListBox x:Name="RightItemsList" 
       Grid.Column="2" 
       Margin="8,30,8,8" 
       MinWidth="150" 
       SelectionMode="Multiple" 
       ItemsSource="{Binding Path=RightCollection}" /> 
    </Grid> 
</Grid> 
</UserControl> 

用戶控制代碼

public partial class ItemsSelectionLists : UserControl 
{ 
    #region properties 
    public string LeftHeader 
    { 
     get 
     { 
      return (string) GetValue(LeftHeaderProperty); 
     } 
     set 
     { 
      SetValue(LeftHeaderProperty, value); 
     } 
    } 
    // Using a DependencyProperty as the backing store for LeftHeader. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty LeftHeaderProperty = 
     DependencyProperty.Register("LeftHeader", typeof(string), typeof(ItemsSelectionLists), new UIPropertyMetadata("Left List Header")); 

    public string RightHeader 
    { 
     get 
     { 
      return (string) GetValue(RightHeaderProperty); 
     } 
     set 
     { 
      SetValue(RightHeaderProperty, value); 
     } 
    } 

    // Using a DependencyProperty as the backing store for RightHeader. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty RightHeaderProperty = 
     DependencyProperty.Register("RightHeader", typeof(string), typeof(ItemsSelectionLists), new UIPropertyMetadata("Right List Header")); 

    private object dataSource; 
    public object DataSource 
    { 
     get 
     { 
      return dataSource; 
     } 
     set 
     { 
      if (!value.GetType().FullName.StartsWith("TimeTracker.ViewModel")) 
       throw new ArgumentException("DataSource is not an instance of ViewModel"); 
      if (dataSource != value) 
      { 
       dataSource = value; 
       this.DataContext = this.DataSource; 
       DataTemplateSelector templateSelector = dataSource as DataTemplateSelector; 
       this.LeftItemsList.ItemTemplateSelector = templateSelector; 
       this.RightItemsList.ItemTemplateSelector = templateSelector; 
      } 
     } 
    } 

    #endregion 

    public ItemsSelectionLists() 
     : base() 
    { 
     this.InitializeComponent(); 

    } 

    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     var button = sender as Button; 
     var type = dataSource.GetType(); 
     var MoveItems = type.GetMethod("MoveItems"); 
     var MoveAllItems = type.GetMethod("MoveAllItems"); 
     switch (button.Content.ToString()) 
     { 
      case ">": 
      MoveItems.Invoke(dataSource, new object[] { LeftItemsList, true }); 
      break; 
      case ">>": 
      MoveAllItems.Invoke(dataSource, new object[] { true }); 
      break; 
      case "<": 
      MoveItems.Invoke(dataSource, new object[] { RightItemsList, false }); 
      break; 
      case "<<": 
      MoveAllItems.Invoke(dataSource, new object[] { false }); 
      break; 
     } 
    } 
} 

視圖模型

public class ViewModel<T> : DataTemplateSelector, INotifyPropertyChanged 
{ 
    #region Properties 

    //this is just a placeholder for the collection, no changes will be made to this collection 
    private ObservableCollection<T> leftCollectionRef; 
    //local collection 
    private ObservableCollection<T> leftCollection; 
    public ObservableCollection<T> LeftCollection 
    { 
     get 
     { 
      return leftCollection; 
     } 

     set 
     { 
      if (value != leftCollectionRef) 
      { 
       //remove subscription to previous collection 
       if (leftCollectionRef != null) 
        leftCollectionRef.CollectionChanged -= new NotifyCollectionChangedEventHandler(Ref_CollectionChanged); 
       leftCollectionRef = value; 
       leftCollection.Clear(); 
       foreach (var item in leftCollectionRef) 
       { 
        if (rightCollection.IndexOf(item) == -1) 
         leftCollection.Add(item); 
       } 

       NotifyPropertyChanged("LeftCollection"); 
       //subscribe to chnages in new collection 
       leftCollectionRef.CollectionChanged += new NotifyCollectionChangedEventHandler(Ref_CollectionChanged); 
      } 
     } 
    } 


    //this is just a placeholder for the collection, no changes will be made to this collection 
    private ObservableCollection<T> rightCollectionRef; 
    private ObservableCollection<T> rightCollection; 
    public ObservableCollection<T> RightCollection 
    { 
     get 
     { 
      return rightCollection; 
     } 

     set 
     { 
      if (value != rightCollectionRef) 
      { 
       //remove subscription to previous collection 
       if (rightCollectionRef != null) 
        rightCollectionRef.CollectionChanged -= new NotifyCollectionChangedEventHandler(Ref_CollectionChanged); 
       rightCollectionRef = value; 
       rightCollection.Clear(); 
       foreach (var item in rightCollectionRef) 
       { 
        if (leftCollection.IndexOf(item) == -1) 
         rightCollection.Add(item); 
       } 

       NotifyPropertyChanged("RightCollection"); 
       rightCollectionRef.CollectionChanged += new NotifyCollectionChangedEventHandler(Ref_CollectionChanged); 
      } 
     } 
    } 

    private string bindingMember; 
    public string BindingMember 
    { 
     get 
     { 
      return bindingMember; 
     } 
     set 
     { 
      var mem = typeof(T).GetProperty(value); 
      if (mem == null) 
       throw new ArgumentException("No Member " + value + " found in " + this.GetType().FullName); 
      if (bindingMember != value) 
      { 
       bindingMember = value; 
       NotifyPropertyChanged("BindingMember"); 
      } 
     } 
    } 

    #endregion 
    #region Constructors 

    public ViewModel() 
     : base() 
    { 
     // internal collection, this will get items copied over from reference source collection 
     leftCollection = new ObservableCollection<T>(); 
     // internal collection, this will get items copied over from reference target collection 
     rightCollection = new ObservableCollection<T>(); 
     bindingMember = ""; 
    } 

    #endregion 
    #region Movements 
    public void MoveItems(ListBox list, bool LeftToRight) 
    { 
     var source = leftCollection; 
     var target = rightCollection; 
     if (!LeftToRight) 
     { 
      target = leftCollection; 
      source = rightCollection; 
     } 

     if (list.SelectedItems.Count > 0) 
     { 
      // List for items to be removed. 
      var hitList = new List<T>(); 

      // Move items 
      foreach (T item in list.SelectedItems) 
      { 
       if (item != null) 
       { 
        // Tag item for removal 
        hitList.Add(item); 
        // Check if item is in target list 
        if (target.IndexOf(item) == -1) 
        { 
         target.Add(item); 
        } 
       } 
      } 

      // Remove items 
      foreach (var hitItem in hitList) 
      { 
       source.Remove(hitItem); 
      } 
     } 

    } 
    public void MoveAllItems(bool LeftToRight) 
    { 
     if (LeftToRight) 
     { 
      rightCollection.Clear(); 
      foreach (var item in leftCollection) 
      { 
       RightCollection.Add(item); 
      } 
      leftCollection.Clear(); 
     } 
     else 
     { 
      leftCollection.Clear(); 
      foreach (var item in rightCollection) 
      { 
       leftCollection.Add(item); 
      } 
      rightCollection.Clear(); 
     } 
    } 

    #endregion 
    #region collection-monitor 
    private void Ref_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.NewItems != null && e.NewItems.Count > 0) 
     { 
      var target = leftCollection; 
      if (sender == leftCollectionRef) 
       target = leftCollection; 
      else 
       target = rightCollection; 

      foreach (T item in e.NewItems) 
      { 
       target.Add(item); 
      } 
     } 

     //try remove from both collections, since the item may have moved to right or left collections 
     if (e.OldItems != null && e.OldItems.Count > 0) 
     { 
      foreach (T item in e.OldItems) 
      { 
       leftCollection.Remove(item); 
      } 

      foreach (T item in e.OldItems) 
      { 
       rightCollection.Remove(item); 
      } 
     } 
    } 
    #endregion 
    #region INotifyPropertyChanged 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged(String info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 

    #endregion 
    #region templateselector 
    public override DataTemplate SelectTemplate(object item, DependencyObject container) 
    { 

     string dataTemplate = 
      @"<DataTemplate 
      xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""> 
       <TextBlock Margin=""2"" TextWrapping=""Wrap"" Text=""{Binding Path=" + this.bindingMember + @", Mode=OneWay}""/> 
      </DataTemplate>"; 
     StringReader stringReader = new StringReader(dataTemplate); 
     XmlReader xmlReader = XmlReader.Create(stringReader); 
     return XamlReader.Load(xmlReader) as DataTemplate; 
    } 
    #endregion 
} 

使用控制:

<TabItem Header="Resource Allocation"> 
<local:ItemsSelectionLists x:Name="ProjectResourceMap" LeftHeader="Whole Team" RightHeader="Current Project Team"/> 
</TabItem> 
<TabItem Header="Tasks for the Project"> 
<local:ItemsSelectionLists x:Name="ProjectTaskMap" Margin="0" d:LayoutOverrides="Width" LeftHeader="All Tasks" RightHeader="Current Project Tasks"/> 
</TabItem> 

ViewModel<Resource> ProjectResource = new ViewModel<Resource>(); 
ProjectResource.BindingMember = "ResourceName"; 
this.ProjectResourceMap.DataSource = ProjectResource; 
ProjectResource.LeftCollection = timeTracker.Resources; 

ViewModel<Task> ProjectTasks = new ViewModel<Task>(); 
ProjectTasks.BindingMember = "TaskName"; 
this.ProjectTaskMap.DataSource = ProjectTasks; 
ProjectTasks.LeftCollection = timeTracker.Tasks;