【问题标题】:Suggestions for synchronization of lists in Silverlight在 Silverlight 中同步列表的建议
【发布时间】:2010-09-06 05:14:08
【问题描述】:

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

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

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

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

还可以添加一条验证规则,说明用户必须向目标列表中添加至少一项。

很简单……

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

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

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

如果能提供任何帮助,我将不胜感激。现在我们在视图中一遍又一遍地实现相同的逻辑,感觉不对。必须有某种方法可以使它成为易于使用的可重用组件。

谢谢!

【问题讨论】:

    标签: silverlight user-controls datagrid synchronization listbox


    【解决方案1】:

    “网格版”的答案:

    (参见下面的列表版本)

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

    要对网格做类似的事情,您不要公开模板因为数据网格列没有模板化(它们是控件列表,可以单独模板化)

    相反,您将左侧网格和右侧网格列集合作为属性公开,并且只需在父页面中设置控件的 LeftColumns 和 RightColumns 属性。唯一的技巧是数据网格列集合没有设置器,因此您需要从新的属性集合更新网格集合项:

    代码隐藏现在是:

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

    【讨论】:

      【解决方案2】:

      由于您要创建一个供其他开发人员使用的控件,因此通常最好使用模板控件而不是UserControl。在这种情况下,开发人员可以为控件指定自定义模板。然而,这并没有你想象的那么有用,尤其是当两个网格的标题集相同时。

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

      对这些演示者的Content 属性的分配进行编码,然后将演示者加载的ItemsControlDataGrid 的ItemsSource 分配给适当的集合。

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

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

      【讨论】:

      • +1:这带来了在开发完整模板控件与更简单的用户控件时需要花费额外费用的问题。我同意您的观点,即模板化方法具有更大的灵活性,但这一特定要求(2 个不同列的网格)似乎并不能证明模板化列表提供程序的额外复杂性是合理的。但是,我将按照您的建议为未来的项目创建一个,以允许在一个控件中同时使用列表或网格( 或任何东西)。干杯。
      【解决方案3】:

      答案的“列表版本”

      (另见网格版本)

      自定义控件听起来很合适,但您希望两个列表都被模板化,以便开发人员可以定义每个项目的视图。我假设您不需要标题,所以一直使用列表框。下面的测试代码结果如下:

      由于列表框已经有项目模板,你真的想在你的自定义用户控件中将它们作为属性公开。然后,您可以单独编辑 2 个模板(下面的示例只是将 LeftRight 模板设置为相同的 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")
                                                                      };
          }
      }
      

      【讨论】:

      • 不错。谢谢!然而,我们的客户想要标题和对每一列进行排序的能力。这是否可以通过列表框实现以一种简单的方式解决,还是应该使用数据网格来代替?如果是这样,(数据网格)实现与上述有何不同?
      • 我为网格添加了第二个答案,因为它们使用不同的技术。干杯
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-06
      • 2019-07-28
      • 2013-10-26
      相关资源
      最近更新 更多