我正在尝试在wpf中编写双列表用户控件。
我是wpf的新手,我觉得很难。 这是我在几个小时内放在一起的东西。它不是那么好,而是一个开始。
如果具有wpf经验的人可以改进它,我将非常感激。 目的是尽可能简化使用
我有点卡住了。 我希望DualList Control的用户能够设置标头如何做到这一点。 我是否需要在我的控件中公开一些依赖项属性?
当加载用户必须传递一个ObservableCollection时,还有更好的方法吗?
你能看看并可能对某些代码提出任何建议吗?
非常感谢!!!!!
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="25px"></ColumnDefinition>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="0">
<Label Name="lblLeftTitle" Content="Available"></Label>
<ListView Name="lvwLeft">
</ListView>
</StackPanel>
<WrapPanel Grid.Column="1" Grid.Row="0">
<Button Name="btnMoveRight" Content=">" Width="25" Margin="0,35,0,0" Click="btnMoveRight_Click" />
<Button Name="btnMoveAllRight" Content=">>" Width="25" Margin="0,05,0,0" Click="btnMoveAllRight_Click" />
<Button Name="btnMoveLeft" Content="<" Width="25" Margin="0,25,0,0" Click="btnMoveLeft_Click" />
<Button Name="btnMoveAllLeft" Content="<<" Width="25" Margin="0,05,0,0" Click="btnMoveAllLeft_Click" />
</WrapPanel>
<StackPanel Orientation="Vertical" Grid.Column="2" Grid.Row="0">
<Label Name="lblRightTitle" Content="Selected"></Label>
<ListView Name="lvwRight">
</ListView>
</StackPanel>
</Grid>
public partial class DualListTest
{
public ObservableCollection<ListViewItem> LeftList { get; set; }
public ObservableCollection<ListViewItem> RightList { get; set; }
public DualListTest()
{
InitializeComponent();
LoadCustomers();
LoadDualList();
}
private void LoadDualList()
{
dualList1.Load(LeftList, RightList);
}
private void LoadCustomers()
{
//Pretend we are getting a list of Customers from a repository.
//Some go in the left List(Good Customers) some go in the Right List(Bad Customers).
LeftList = new ObservableCollection<ListViewItem>();
RightList = new ObservableCollection<ListViewItem>();
var customers = GetCustomers();
foreach (var customer in customers)
{
if (customer.Status == CustomerStatus.Good)
{
LeftList.Add(new ListViewItem { Content = customer });
}
else
{
RightList.Add(new ListViewItem{Content=customer });
}
}
}
private static IEnumerable<Customer> GetCustomers()
{
return new List<Customer>
{
new Customer {Name = "Jo Blogg", Status = CustomerStatus.Good},
new Customer {Name = "Rob Smith", Status = CustomerStatus.Good},
new Customer {Name = "Michel Platini", Status = CustomerStatus.Good},
new Customer {Name = "Roberto Baggio", Status = CustomerStatus.Good},
new Customer {Name = "Gio Surname", Status = CustomerStatus.Bad},
new Customer {Name = "Diego Maradona", Status = CustomerStatus.Bad}
};
}
}
public partial class DualList:UserControl
{
public ObservableCollection<ListViewItem> LeftListCollection { get; set; }
public ObservableCollection<ListViewItem> RightListCollection { get; set; }
public DualList()
{
InitializeComponent();
}
public void Load(ObservableCollection<ListViewItem> leftListCollection, ObservableCollection<ListViewItem> rightListCollection)
{
LeftListCollection = leftListCollection;
RightListCollection = rightListCollection;
lvwLeft.ItemsSource = leftListCollection;
lvwRight.ItemsSource = rightListCollection;
EnableButtons();
}
public static DependencyProperty LeftTitleProperty = DependencyProperty.Register("LeftTitle",
typeof(string),
typeof(Label));
public static DependencyProperty RightTitleProperty = DependencyProperty.Register("RightTitle",
typeof(string),
typeof(Label));
public static DependencyProperty LeftListProperty = DependencyProperty.Register("LeftList",
typeof(ListView),
typeof(DualList));
public static DependencyProperty RightListProperty = DependencyProperty.Register("RightList",
typeof(ListView),
typeof(DualList));
public string LeftTitle
{
get { return (string)lblLeftTitle.Content; }
set { lblLeftTitle.Content = value; }
}
public string RightTitle
{
get { return (string)lblRightTitle.Content; }
set { lblRightTitle.Content = value; }
}
public ListView LeftList
{
get { return lvwLeft; }
set { lvwLeft = value; }
}
public ListView RightList
{
get { return lvwRight; }
set { lvwRight = value; }
}
private void EnableButtons()
{
if (lvwLeft.Items.Count > 0)
{
btnMoveRight.IsEnabled = true;
btnMoveAllRight.IsEnabled = true;
}
else
{
btnMoveRight.IsEnabled = false;
btnMoveAllRight.IsEnabled = false;
}
if (lvwRight.Items.Count > 0)
{
btnMoveLeft.IsEnabled = true;
btnMoveAllLeft.IsEnabled = true;
}
else
{
btnMoveLeft.IsEnabled = false;
btnMoveAllLeft.IsEnabled = false;
}
if (lvwLeft.Items.Count != 0 || lvwRight.Items.Count != 0) return;
btnMoveLeft.IsEnabled = false;
btnMoveAllLeft.IsEnabled = false;
btnMoveRight.IsEnabled = false;
btnMoveAllRight.IsEnabled = false;
}
private void MoveRight()
{
while (lvwLeft.SelectedItems.Count > 0)
{
var selectedItem = (ListViewItem)lvwLeft.SelectedItem;
LeftListCollection.Remove(selectedItem);
RightListCollection.Add(selectedItem);
}
lvwRight.ItemsSource = RightListCollection;
lvwLeft.ItemsSource = LeftListCollection;
EnableButtons();
}
private void MoveAllRight()
{
while (lvwLeft.Items.Count > 0)
{
var item = (ListViewItem)lvwLeft.Items[lvwLeft.Items.Count - 1];
LeftListCollection.Remove(item);
RightListCollection.Add(item);
}
lvwRight.ItemsSource = RightListCollection;
lvwLeft.ItemsSource = LeftListCollection;
EnableButtons();
}
private void MoveAllLeft()
{
while (lvwRight.Items.Count > 0)
{
var item = (ListViewItem)lvwRight.Items[lvwRight.Items.Count - 1];
RightListCollection.Remove(item);
LeftListCollection.Add(item);
}
lvwRight.ItemsSource = RightListCollection;
lvwLeft.ItemsSource = LeftListCollection;
EnableButtons();
}
private void MoveLeft()
{
while (lvwRight.SelectedItems.Count > 0)
{
var selectedCustomer = (ListViewItem)lvwRight.SelectedItem;
LeftListCollection.Add(selectedCustomer);
RightListCollection.Remove(selectedCustomer);
}
lvwRight.ItemsSource = RightListCollection;
lvwLeft.ItemsSource = LeftListCollection;
EnableButtons();
}
private void btnMoveLeft_Click(object sender, RoutedEventArgs e)
{
MoveLeft();
}
private void btnMoveAllLeft_Click(object sender, RoutedEventArgs e)
{
MoveAllLeft();
}
private void btnMoveRight_Click(object sender, RoutedEventArgs e)
{
MoveRight();
}
private void btnMoveAllRight_Click(object sender, RoutedEventArgs e)
{
MoveAllRight();
}
}
答案 0 :(得分:1)
在查看了所有代码之后,我很清楚您使用的是旧的 WinForms 方式,而不是 WPF 方式。这不一定是坏事 - 它仍然有效,但维护起来非常困难。而不是利用WPF为我们提供的许多工具,例如数据绑定,命令,模板和依赖属性,您几乎可以使用事件处理程序连接所有内容并编写大量代码来维护UI。使用一些原始的XAML和代码,我构建了一个使用所有上述功能的示例。为了便于演示,我只是将其全部写在一个UserControl
中,而不是将其分成Window
。首先,XAML:
<Window x:Class="TestWpfApplication.DualList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="DualList" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<ObjectDataProvider x:Key="Customers" ObjectType="{x:Type local:Customer}" MethodName="GetCustomers"/>
<CollectionViewSource x:Key="GoodCustomers" Source="{StaticResource Customers}" Filter="GoodFilter"/>
<CollectionViewSource x:Key="BadCustomers" Source="{StaticResource Customers}" Filter="BadFilter"/>
<DataTemplate DataType="{x:Type local:Customer}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</Window.Resources>
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Column="0" Grid.Row="0">
<Label Name="lblLeftTitle" Content="{Binding LeftHeader, FallbackValue=Available}"/>
<ListView Name="lvwLeft" MinHeight="200"
ItemsSource="{Binding Source={StaticResource GoodCustomers}}"/>
</StackPanel>
<WrapPanel Grid.Column="1" Grid.Row="0"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<Button Name="btnMoveRight" Command="{Binding MoveRightCommand}"
CommandParameter="{Binding ElementName=lvwLeft, Path=SelectedItem}"
Content=">" Width="25" Margin="0,35,0,0"/>
<Button Name="btnMoveAllRight" Command="{Binding MoveAllRightCommand}"
CommandParameter="{Binding Source={StaticResource GoodCustomers}}"
Content=">>" Width="25" Margin="0,05,0,0"/>
<Button Name="btnMoveLeft" Command="{Binding MoveLeftCommand}"
CommandParameter="{Binding ElementName=lvwRight, Path=SelectedItem}"
Content="<" Width="25" Margin="0,25,0,0"/>
<Button Name="btnMoveAllLeft" Command="{Binding MoveAllLeftCommand}"
CommandParameter="{Binding Source={StaticResource BadCustomers}}"
Content="<<" Width="25" Margin="0,05,0,0"/>
</WrapPanel>
<StackPanel Orientation="Vertical" Grid.Column="2" Grid.Row="0">
<Label Name="lblRightTitle" Content="{Binding RightHeader, FallbackValue=Selected}"/>
<ListView Name="lvwRight" MinHeight="200"
ItemsSource="{Binding Source={StaticResource BadCustomers}}"/>
</StackPanel>
</Grid>
从顶部开始,您将注意到的最重要的事情是我声明了ObjectDataProvider
和两个CollectionViewSource
对象。数据提供程序绑定到将创建默认客户列表的方法。视图来源(列出所有客户)列表并将其过滤到两个单独的列表中 - 一个用于好客户,另一个用于坏客户。这是通过CollectionViewSource.Filter
属性完成的。
接下来,您将看到最初构建用户界面的用户界面,而不是连接事件处理程序,我已将按钮绑定到窗口上的命令。 ListView.ItemSource
属性分别绑定在XAML中,绑定到GoodCustomers
和BadCustomers
源。所有这些绑定都将删除大量的样板用户界面代码。现在让我们来看看代码隐藏:
public partial class DualList : Window
{
public ICommand MoveRightCommand
{
get;
set;
}
public ICommand MoveLeftCommand
{
get;
set;
}
public ICommand MoveAllRightCommand
{
get;
set;
}
public ICommand MoveAllLeftCommand
{
get;
set;
}
public static DependencyProperty RightHeaderProperty =
DependencyProperty.Register("RightHeader", typeof(string), typeof(DualList));
public string RightHeader
{
get { return (string)GetValue(RightHeaderProperty); }
set { SetValue(RightHeaderProperty, value); }
}
public static DependencyProperty LeftHeaderProperty =
DependencyProperty.Register("LeftHeader", typeof(string), typeof(DualList));
public string LeftHeader
{
get { return (string)GetValue(LeftHeaderProperty); }
set { SetValue(LeftHeaderProperty, value); }
}
/// <summary>
/// Default constructor-- set up RelayCommands.
/// </summary>
public DualList()
{
InitializeComponent();
LeftHeader = "Good Customers";
RightHeader = "Bad Customers";
MoveRightCommand = new RelayCommand((o) => OnMoveRight((Customer)o), (o) => o != null);
MoveLeftCommand = new RelayCommand((o) => OnMoveLeft((Customer)o), (o) => o != null);
MoveAllRightCommand = new RelayCommand((o) => OnMoveAllRight((ListCollectionView)o), (o) => ((ListCollectionView)o).Count > 0);
MoveAllLeftCommand = new RelayCommand((o) => OnMoveAllLeft((ListCollectionView)o), (o) => ((ListCollectionView)o).Count > 0);
}
/// <summary>
/// Make this selected customer bad.
/// </summary>
private void OnMoveRight(Customer customer)
{
customer.Status = CustomerStatus.Bad;
RefreshViews();
}
/// <summary>
/// Make this selected customer good.
/// </summary>
private void OnMoveLeft(Customer customer)
{
customer.Status = CustomerStatus.Good;
RefreshViews();
}
/// <summary>
/// Make all customers bad.
/// </summary>
private void OnMoveAllRight(ListCollectionView customers)
{
foreach (Customer c in customers.SourceCollection)
c.Status = CustomerStatus.Bad;
RefreshViews();
}
/// <summary>
/// Make all customers good.
/// </summary>
private void OnMoveAllLeft(ListCollectionView customers)
{
foreach (Customer c in customers.SourceCollection)
c.Status = CustomerStatus.Good;
RefreshViews();
}
/// <summary>
/// Filters out any bad customers.
/// </summary>
private void GoodFilter(object sender, FilterEventArgs e)
{
Customer customer = e.Item as Customer;
e.Accepted = customer.Status == CustomerStatus.Good;
}
/// <summary>
/// Filters out any good customers.
/// </summary>
private void BadFilter(object sender, FilterEventArgs e)
{
Customer customer = e.Item as Customer;
e.Accepted = customer.Status == CustomerStatus.Bad;
}
/// <summary>
/// Refresh the collection view sources.
/// </summary>
private void RefreshViews()
{
foreach (object resource in Resources.Values)
{
CollectionViewSource cvs = resource as CollectionViewSource;
if (cvs != null)
cvs.View.Refresh();
}
}
}
从顶部开始,您会看到每个按钮的ICommand
声明。我还添加了两个依赖项属性,表示列表的右侧和左侧标题(更改这些属性将自动更新UI中的标题)。然后,在构造函数中,我将每个命令连接到RelayCommand
(由Josh Smith创建),它只是让我指定两个委托 - 一个用于执行命令,另一个用于控制命令何时可以执行。您可以看到,而不是在列表之间移动项目,我只是更改列表被过滤的项目的属性。因此,为了移动项目,我将客户状态更改为良好。这是分离UI和业务逻辑的示例:UI反映了对基础项所做的更改,但不会自行进行更改。
每个命令的逻辑应该相当容易理解 - 将所有客户移到良好的列表中,我们只是遍历每个客户,将他们的Status
设置为好。冲洗并重复其他命令。请注意,我们必须更新我们的CollectionViewSource
个对象以进行过滤器更新。否则不会显示任何更改。
所以,总结一下:
CollectionViewSource
和ObjectDataProvider
可用于按您希望的方式过滤和显示数据。在您的方案中,我有一个列表可以根据客户状态进行过滤,而不是管理两个列表。