Подборка выбора списка данных в WPF - PullRequest
1 голос
/ 24 марта 2012

Хорошо, я мог бы задавать старый вопрос, но я не описал свой сценарий ни в одном из них.

У меня есть объект, который может содержать несколько дочерних объектов.Например.Объект Project может иметь несколько объектов Resource.У меня есть ObservaleCollection с супер набором дочерних объектов (в моем случае объекты Resource).У меня также есть другой объект ObservableCollection в объекте Project, содержащий существующие дочерние элементы.

Каков наилучший способ представить это пользователю в приложении Windows WPF?Я также должен предоставить им способ изменить отображение.

Моя первоначальная идея состояла в том, чтобы использовать классический подход двойного списка с двумя списками, но я не уверен, насколько легко было бы манипулировать представлением.один слой.

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

Мне нужен подобный интерфейс для еще 4 похожих сопоставлений разных объектов.Я пытался переместить это в пользовательский элемент управления, но, похоже, у меня не может быть общей коллекции (private ObservableCollection) в UserControl.

Есть идеи от опытных участников?

/ ************************************************** /Изменить: это то, что я получил до сих пор, обратите внимание, что я иду с UserControl, так как мне нужен один и тот же интерфейс на нескольких экранах, и я чувствую, что 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>'

Ответы [ 2 ]

1 голос
/ 24 марта 2012

Это не изящно и не отполировано, но вот работающий образец.Если бы я сделал это отполированным, я бы реализовал настоящий 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 голосов
/ 26 марта 2012

Хорошо, благодаря Reflection и идее представления модели XCalibur, я могу выполнить свой пользовательский элемент управления, который может отображать содержимое двух общих наблюдаемых коллекций одного типа. Потребитель элемента управления может указать свойство списков объектов, которые должны отображаться. Данные коллекции будут скопированы в локальную коллекцию, и, следовательно, любые изменения, внесенные в коллекции, не изменят входные коллекции.

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

ViewModel

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