WPF中的DataBind Listbox SubSelection

时间:2012-03-24 00:14:36

标签: c# wpf user-controls listbox

好吧,我可能会问一个古老的问题,但我没有在任何一个问题中描述我的情景。

我有一个可以包含多个子对象的Oject。例如。项目obejct可以有多个Resource对象。我有一个带有超级子对象集的ObservaleCollection(在我的案例中是Resource对象)。我在Project对象中还有另一个包含现有子项的ObservableCollection。

在WPF Windows应用程序中向用户显示此内容的最佳方法是什么?我还需要为他们提供一种方法来定位映射。

我最初的想法是使用经典的双列表方法,有两个列表框,但我不确定单独操作视图层是多么容易。

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

我需要类似的UI来进行4个不同对象的类似映射。我试图将其移动到用户控件,但看起来我在UserControl中没有Generic集合(私有ObservableCollection)。

来自经验成员的任何想法?

/ * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * ** * **** / 编辑:这是我到目前为止所做的,请注意我将使用UserControl,因为我需要在多个屏幕中使用相同的UI,我觉得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 个答案:

答案 0 :(得分: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);
    }
}

ViewModel:

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
}

答案 1 :(得分: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;