关于在Silverlight中同步列表的建议

时间:2010-09-06 05:14:08

标签: silverlight user-controls datagrid synchronization listbox

我们正在我们公司创建一个业务应用程序(Silverlight 4),在我们的几个视图中,我们使用这些功能来同步两种类型的列表。

目前,我们更精确地使用两个列表框(一个源和一个目标)和两个按钮(一个添加和一个删除)。源列表和目标列表都绑定到具有相同数据类型的集合。

用户可以选择源列表中的一个或多个项目,然后按添加按钮将项目移动到目标列表(即项目从源中删除并添加到目标)。

同样,用户可以选择目标列表中的一个或多个项目,然后按删除将项目从目标移回源列表。

还可以添加验证规则,表明用户必须至少将一个项目添加到目标列表。

很简单......

到目前为止,我们正在使用我们自己创建的用户控件,该控件包含这4个控件(2个列表框和2个按钮)以及保持列表同步的逻辑。依赖属性用于绑定源集合和目标集合。

现在问题。我们的客户现在希望我们的用户控制更加灵活。 他们希望能够在源列表和目标列表中具有任意数量的列(即,源列表可以具有与目标列表不同的列和不同数量的列)。客户还希望能够对任何列进行排序。

我的第一个想法是将列表框替换为数据网格。但后来我意识到我不知道如何以简单的方式让消费者(开发者)定义他或她的列和绑定。这可能是因为我对SL的了解有限。也许自定义用户控件不是要走的路?

我会感激任何帮助。现在我们在我们的观点中一遍又一遍地实施相同的逻辑,但感觉不对。必须有一些方法可以使它成为一个易于使用的可重用组件。

谢谢!

3 个答案:

答案 0 :(得分:1)

答案的“网格版”:

(参见下面的列表版本)

问题已经改变(澄清)我正在添加一个新答案。第一个对于那些只想要列表的人来说仍然有用,所以我会留在那里。

要使用网格执行类似操作,您不要公开模板,因为datagrid列不是模板化的(它们是控件列表,可以单独模板化)alt text

而是将左网格和右网格列集合显示为属性,并在父页面中设置控件的LeftColumns和RightColumns属性。唯一的技巧是datagrid-columns集合没有setter ,因此您需要更新新属性集合中的网格集合项目:

代码隐藏现在是:

using System.Collections.ObjectModel;
using System.Windows.Controls;

namespace SilverlightApplication1
{
    public partial class GridSelectionControl : UserControl
    {
        public GridSelectionControl()
        {
            InitializeComponent();
        }

        public ObservableCollection<DataGridColumn> LeftColumns
        {
            get
            {
                return leftDataGrid.Columns;
            }
            set
            {
                leftDataGrid.Columns.Clear();
                foreach (var col in value)
                {
                    leftDataGrid.Columns.Add(col);
                }
            }
        }

        public ObservableCollection<DataGridColumn> RightColumns
        {
            get
            {
                return rightDataGrid.Columns;
            }
            set
            {
                rightDataGrid.Columns.Clear();
                foreach (var col in value)
                {
                    rightDataGrid.Columns.Add(col);
                }
            }
        }
    }
}

控件的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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.GridSelectionControl"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <sdk:DataGrid x:Name="leftDataGrid" Margin="10" AutoGenerateColumns="False"/>
        <sdk:DataGrid x:Name="rightDataGrid" Margin="10" Grid.Column="2" AutoGenerateColumns="False"/>
        <StackPanel Grid.Column="1" Orientation="Vertical">
            <Button Content="Add &gt;" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
            <Button Content="&lt; Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
        </StackPanel>

    </Grid>
</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"
    xmlns:local="clr-namespace:SilverlightApplication1" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    x:Class="SilverlightApplication1.TestGridSelectionControl"
    d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">
        <local:GridSelectionControl x:Name="SelectionControl">
            <local:GridSelectionControl.LeftColumns>
                <sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
                <sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
                <sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
            </local:GridSelectionControl.LeftColumns>
            <local:GridSelectionControl.RightColumns>
                <sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
                <sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
                <sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
            </local:GridSelectionControl.RightColumns>
        </local:GridSelectionControl>
    </Grid>
</UserControl>

现在测试代码隐藏了:

using System.Collections.ObjectModel;
using System.Windows.Controls;

namespace SilverlightApplication1
{
    public partial class TestGridSelectionControl : UserControl
    {
        public TestGridSelectionControl()
        {
            // Required to initialize variables
            InitializeComponent();
            SelectionControl.leftDataGrid.ItemsSource = Person.People;
            SelectionControl.rightDataGrid.ItemsSource = Person.Machines;
        }

        public class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public bool IsMachine { get; set; }

            public Person(string firstname, string lastname, bool robot)
            {
                this.FirstName = firstname;
                this.LastName = lastname;
                this.IsMachine = robot;
            }

            public static ObservableCollection<Person> People = new ObservableCollection<Person>()
                                                                {
                                                                    new Person("Tom", "Jones", false),
                                                                    new Person("Elis", "Presley", false),
                                                                    new Person("Joe", "Blogs", false)
                                                                };

            public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
                                                                {
                                                                    new Person("Marvin", "Android", true),
                                                                    new Person("Hal", "9000", true),
                                                                    new Person("B", "9", true)
                                                                };
        }
    }
}

答案 1 :(得分:1)

由于您正在创建一个供其他开发人员使用的控件,因此通常最好使用模板控件而不是UserControl。在这种情况下,开发人员可以为控件指定自定义模板。然而,这并不像你可能那样有用,特别是如果两个网格的标题集是相同的。

您可以采取的一种方法是提供名为“ListTemplate”的DataTemplate类型的依赖项属性。在控件Xaml的两个点上,您将显示列表,使用两个ControlPresenter元素。一个名为“SourceContent”的另一个“TargetContent”。对于绑定ContentTemplate和新的“ListTemplate”。

在这些演示者上编码Content属性的分配,然后为演示者加载的ItemsControlDataGrid的ItemsSource分配相应的集合。

如果您包含一个简单的ListBox基础数据模板作为“ListTemplate”属性的默认值,那么如果开发人员想要使用各种DataGrid,那么您应该以最简单的形式使用它。他们可以在列中定义一个列 ListTemplate财产。

当然,您需要在控件中编写代码以处理可能是DataGrid元素的列表。

答案 2 :(得分:0)

答案的“列表版本”

(另见网格版)

自定义控件听起来是正确的,但您希望模板化两个列表,以便开发人员可以定义每个项目视图。我假设你不需要标题,所以坚持使用列表框。下面的测试代码导致:

alt text

由于列表框已经有项目模板,您真的希望在自定义用户控件中公开这些属性。然后,您可以单独编辑2个模板(下面的示例只是将 Left Right 模板设置为相同的FirstName / LastName堆栈面板,这是您定义格式的位置你的列表框):

<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:SilverlightApplication1"
    mc:Ignorable="d"
    x:Class="SilverlightApplication1.TestListSelectionControl"
    d:DesignWidth="640" d:DesignHeight="480">
    <UserControl.Resources>
        <DataTemplate x:Key="DataTemplate1">
            <Grid>
                <StackPanel Orientation="Horizontal">
                    <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding FirstName}"/>
                    <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding LastName}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">
        <local:ListSelectionControl x:Name="SelectionControl" d:LayoutOverrides="Height" LeftItemTemplate="{StaticResource DataTemplate1}" RightItemTemplate="{StaticResource DataTemplate1}"/>
    </Grid>
</UserControl>

示例ListSelectionControl XAML如下:

<UserControl x:Class="SilverlightApplication1.ListSelectionControl"
    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"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox HorizontalAlignment="Stretch" Margin="12" Name="leftListBox" VerticalAlignment="Stretch" />
        <ListBox HorizontalAlignment="Stretch" Margin="12" Name="rightListBox" VerticalAlignment="Stretch" Grid.Column="2" />
        <StackPanel Grid.Column="1" Orientation="Vertical">
            <Button Content="Add &gt;" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
            <Button Content="&lt; Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
        </StackPanel>
    </Grid>
</UserControl>

控件的简单代码隐藏:

using System.Windows;
using System.Windows.Controls;

    namespace SilverlightApplication1
    {
        public partial class ListSelectionControl : UserControl
        {
            public DataTemplate LeftItemTemplate
            {
                get
                {
                    return leftListBox.ItemTemplate;
                }

                set
                {
                    leftListBox.ItemTemplate = value;
                }
            }

            public DataTemplate RightItemTemplate
            {
                get
                {
                    return rightListBox.ItemTemplate;
                }

                set
                {
                    rightListBox.ItemTemplate = value;
                }
            }

            public ListSelectionControl()
            {
                InitializeComponent();

            }

        }
    }

只是为了完成这个例子,这是填充示例GUI的代码:

using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace SilverlightApplication1
{
    public partial class TestListSelectionControl : UserControl
    {
        public TestListSelectionControl()
        {
            // Required to initialize variables
            InitializeComponent();
            SelectionControl.leftListBox.ItemsSource = Person.People;
            SelectionControl.rightListBox.ItemsSource = Person.Machines;
        }
    }

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person(string firstname, string lastname)
        {
            this.FirstName = firstname;
            this.LastName = lastname;
        }

        public static ObservableCollection<Person> People = new ObservableCollection<Person>()
                                                                {
                                                                    new Person("Tom", "Jones"),
                                                                    new Person("Elis", "Presley"),
                                                                    new Person("Joe", "Blogs")
                                                                };

        public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
                                                                {
                                                                    new Person("Marvin", "Android"),
                                                                    new Person("Hal", "9000"),
                                                                    new Person("B", "9")
                                                                };
    }
}