我们正在我们公司创建一个业务应用程序(Silverlight 4),在我们的几个视图中,我们使用这些功能来同步两种类型的列表。
目前,我们更精确地使用两个列表框(一个源和一个目标)和两个按钮(一个添加和一个删除)。源列表和目标列表都绑定到具有相同数据类型的集合。
用户可以选择源列表中的一个或多个项目,然后按添加按钮将项目移动到目标列表(即项目从源中删除并添加到目标)。
同样,用户可以选择目标列表中的一个或多个项目,然后按删除将项目从目标移回源列表。
还可以添加验证规则,表明用户必须至少将一个项目添加到目标列表。
很简单......
到目前为止,我们正在使用我们自己创建的用户控件,该控件包含这4个控件(2个列表框和2个按钮)以及保持列表同步的逻辑。依赖属性用于绑定源集合和目标集合。
现在问题。我们的客户现在希望我们的用户控制更加灵活。 他们希望能够在源列表和目标列表中具有任意数量的列(即,源列表可以具有与目标列表不同的列和不同数量的列)。客户还希望能够对任何列进行排序。
我的第一个想法是将列表框替换为数据网格。但后来我意识到我不知道如何以简单的方式让消费者(开发者)定义他或她的列和绑定。这可能是因为我对SL的了解有限。也许自定义用户控件不是要走的路?
我会感激任何帮助。现在我们在我们的观点中一遍又一遍地实施相同的逻辑,但感觉不对。必须有一些方法可以使它成为一个易于使用的可重用组件。
谢谢!
答案 0 :(得分:1)
问题已经改变(澄清)我正在添加一个新答案。第一个对于那些只想要列表的人来说仍然有用,所以我会留在那里。
要使用网格执行类似操作,您不要公开模板,因为datagrid列不是模板化的(它们是控件列表,可以单独模板化)。
而是将左网格和右网格列集合显示为属性,并在父页面中设置控件的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 >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< 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
属性的分配,然后为演示者加载的ItemsControl
或DataGrid
的ItemsSource分配相应的集合。
如果您包含一个简单的ListBox
基础数据模板作为“ListTemplate”属性的默认值,那么如果开发人员想要使用各种DataGrid
,那么您应该以最简单的形式使用它。他们可以在列中定义一个列
ListTemplate
财产。
当然,您需要在控件中编写代码以处理可能是DataGrid
元素的列表。
答案 2 :(得分:0)
自定义控件听起来是正确的,但您希望模板化两个列表,以便开发人员可以定义每个项目视图。我假设你不需要标题,所以坚持使用列表框。下面的测试代码导致:
由于列表框已经有项目模板,您真的希望在自定义用户控件中公开这些属性。然后,您可以单独编辑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 >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< 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")
};
}
}