【问题标题】:How can I add headers to DualList control wpf如何将标题添加到 DualList 控件 wpf
【发布时间】:2010-04-20 05:41:53
【问题描述】:

我正在尝试在 wpf 中编写 Dual List 用户控件。

我是 wpf 的新手,我发现它非常困难。 这是我在几个小时内完成的。它不是很好,而是一个开始。

如果有 wpf 经验的人可以改进它,我将非常感激。 目的是尽可能简化使用

我有点卡住了。 我希望 DualList 控件的用户能够设置标题,你是如何做到的。 我需要在我的控件中公开一些依赖属性吗?

在加载用户必须通过 ObservableCollection 的那一刻,有没有更好的方法?

您能看一下并可能对一些代码提出任何建议吗?

非常感谢!!!!!!

xaml

   <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="&lt;" Width="25" Margin="0,25,0,0" Click="btnMoveLeft_Click" />
        <Button Name="btnMoveAllLeft" Content="&lt;&lt;" 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();
            }
        }

【问题讨论】:

    标签: wpf


    【解决方案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="&gt;" Width="25" Margin="0,35,0,0"/>
            <Button Name="btnMoveAllRight" Command="{Binding MoveAllRightCommand}" 
                    CommandParameter="{Binding Source={StaticResource GoodCustomers}}"
                    Content="&gt;&gt;" Width="25" Margin="0,05,0,0"/>
            <Button Name="btnMoveLeft" Command="{Binding MoveLeftCommand}" 
                    CommandParameter="{Binding ElementName=lvwRight, Path=SelectedItem}"
                    Content="&lt;" Width="25" Margin="0,25,0,0"/>
            <Button Name="btnMoveAllLeft" Command="{Binding MoveAllLeftCommand}" 
                    CommandParameter="{Binding Source={StaticResource BadCustomers}}"
                    Content="&lt;&lt;" 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 中分别绑定到 GoodCustomersBadCustomers 源。所有这些绑定都将删除大量样板用户界面代码。现在让我们看一下代码隐藏:

    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 对象以更新过滤器。否则不会显示任何更改。

    所以,总结一下:

    • 不要编写一堆代码隐藏来维护您的 UI,而是使用数据绑定。 CollectionViewSourceObjectDataProvider 可用于按照您希望的方式过滤和显示数据。在您的场景中,我没有管理两个列表,而是有一个根据客户状态进行过滤的列表。
    • 不要使用事件处理程序来设置 UI 逻辑,使用命令。它们增加了封装性并允许自动更新您的 UI。
    • 使用依赖属性和绑定允许用户自定义列表标题。

    【讨论】:

    • 感谢您的帮助。我正计划使用命令(DelegateCommand 或 RelayCommand)我什至在考虑 MVVM 是否适用于 userControls。但我想得到基本的工作。让我消化你有什么完成并回到你身边。我可以看到你所做的一些我不明白的事情。再次,我会尽快回复你。同时谢谢!!!
    • 嗨,非常感谢您的代码。我已将其全部放在一个新项目中并试图让它运行以获得感觉,但在构造函数中 ListCollectionView 需要初始化,因为它抛出“对象变量不是set",从未使用过 ListCollectionView 我猜是 wpf 的新手。我同意 100% 我应该避免在 UI 中进行任何布线,只是缺乏知识。我发现 xaml 的绑定非常棘手,因为有很多做事的方法。最终它必须是一个用户控件,否则它将没有用。我需要它来做一个真正的项目。当我提到标题时,我的意思是每个列表视图都可以用 columnHeaders 设置
    • 您好,抱歉,由于某些原因,我无法在之前的帖子中收到更多信件。当移动到 userControl 并移动项目时,我需要找到一种通用的方式来做事。控件的用户将决定要移动哪些项目。我尝试使用泛型,但无法使其与 userControl 一起使用通用会很棒。非常感谢您的帮助
    • 关于您的错误:确保在 XAML 中正确设置了 CommandParameter 绑定。 CommandParameter 是 ListCollectionView 的初始化方式。关于泛型:现在您绑定到具有字符串类型数据的 Customer 类。但是您可以使用任何类型的数据绑定到任何类。您可以创建一个具有泛型数据类型的泛型类,并绑定到它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2013-09-15
    • 2012-12-19
    • 2012-03-08
    • 2011-03-09
    • 2010-11-12
    • 1970-01-01
    相关资源
    最近更新 更多