Как я могу добавить заголовки в двойной список управления wpf - PullRequest
0 голосов
/ 19 апреля 2010

Я пытаюсь написать usercontrol Dual List в wpf.

Я новичок в wpf, и мне это довольно сложно. Это то, что я собрал за пару часов. Это не так хорошо, но для начала.

Я был бы очень признателен, если бы кто-то с опытом работы с wpf смог улучшить его. Цель состоит в том, чтобы максимально упростить использование

Я застрял. Я хотел бы, чтобы пользователь DualList Control мог настроить заголовки, как вы это делаете. Нужно ли выставлять некоторые свойства зависимостей в моем элементе управления?

В момент загрузки пользователю необходимо передать 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}
                   };
    }
}

UserControl

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

1 Ответ

1 голос
/ 20 апреля 2010

После просмотра всего вашего кода мне становится ясно, что вы делаете это старым WinForms способом, а не WPF способом. Это не обязательно плохо - все еще работает, но поддерживать его будет довольно сложно. Вместо того чтобы использовать преимущества множества инструментов, предоставляемых WPF, таких как привязка данных, команды, шаблоны и свойства зависимостей, вы в значительной степени связываете все с обработчиками событий и пишете много кода для поддержки пользовательского интерфейса. Используя некоторые из ваших исходных 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 с источниками 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 для каждой кнопки. Я также добавил два свойства зависимостей, которые представляют правый и левый заголовки для ваших списков (изменение этих свойств автоматически обновит заголовки в пользовательском интерфейсе). Затем в конструкторе я подключаю каждую команду к RelayCommand (созданному Джошем Смитом), который просто позволяет мне указать двух делегатов - один для выполнения команды, другой для управления, когда Команда может быть выполнена. Вы можете видеть, что вместо перемещения элементов между списками я просто изменяю свойство элемента, по которому фильтруется список. Поэтому, чтобы переместить товар влево, я изменяю статус клиента на хороший. Это пример разделения пользовательского интерфейса и бизнес-логики : пользовательский интерфейс отражает изменения, внесенные в базовые элементы, но не вносит сами изменения.

Логика каждой команды должна быть достаточно простой для понимания - чтобы переместить всех клиентов в список хороших, мы просто выполняем итерацию по каждому клиенту, устанавливая их Status на хорошее. Промойте и повторите для других команд. Обратите внимание, что мы должны обновить наши CollectionViewSource объекты, чтобы обновить фильтр. В противном случае изменения не будут отображаться.

Итак, в итоге:

  • Не пишите много кода для поддержки вашего интерфейса, вместо этого используйте привязку данных. CollectionViewSource и ObjectDataProvider могут быть использованы для фильтрации и отображения ваших данных так, как вы хотите. В вашем сценарии вместо управления двумя списками у меня есть один список, который фильтруется по статусу клиента.
  • Не используйте обработчики событий для настройки логики пользовательского интерфейса, используйте команды. Они увеличивают инкапсуляцию и позволяют автоматически обновлять ваш пользовательский интерфейс.
  • Используйте свойства зависимостей и привязку, чтобы пользователь мог настраивать заголовки списка.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...