none
битовые флаги в ListBox RRS feed

  • Вопрос

  • Добрый день.

    Такой вопрос по ListBox WPF.

    Есть у меня такой список List<User> users;

    где User это

    public class User
    {
      public string Name { get; set; }
      public UserAction action { get; set; }
    }

    где UserAction это битовые флаги типа

    [Flags]
    public enum UserAction
    {
      Action1 = 1,
      Action2 = 2,
      Action3 = 4,
      Action4 = 8
    }

    Этот список юзеров отображается в ListBox. При выделении элемента этого списка, отображается рядом детальная информация. С отображением имени проблем нету. 

    Вопрос: Как реализовать в другом ListBox список UserAction у текущего объекта в виде списка CheckBox-ов?

    Сформировать список у меня получилось таким образом 

    <ObjectDataProvider MethodName="GetValues" ObjectType="{x:Type sys:Enum}" x:Key="UserActionEnum">
       <ObjectDataProvider.MethodParameters>
          <x:Type TypeName="ns:UserAction"/>
       </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    
    <ListBox Name="lstActions" ItemsSource="{Binding Source={StaticResource UserActionEnum}}">
       <ListBox.ItemTemplate>
          <DataTemplate>
              <CheckBox Content="{Binding}" />
          </DataTemplate>
       </ListBox.ItemTemplate>
    </ListBox>

    Но вот как сделать привязку так чтобы эти чек боксы отмечали реальное положение дел не получается.

    Прошу помощи

    25 июля 2012 г. 13:13

Ответы

  • получил вполне работающий способ через другой вариант конвертера

     [Flags]
         public enum UserAction
         {
             Action1 = 1,
             Action2 = 2,
             Action3 = 4,
             Action4 = 8,
         }
    
         public class User
         {
             public string FirstName { get; set; }
             public string LastName { get; set; }
             public UserAction Action { get; set; }
    
             public User() { }
             public User(string fName, string lName, UserAction action)
             {
                 FirstName = fName;
                 LastName = lName;
                 Action = action;
             }
         }
    
         public class ContentConverter : IValueConverter
         {
             static User currentUser;
    
             public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 ListBox lb = new ListBox();
                 List<CheckBox> checks = new List<CheckBox>();
                 foreach(UserAction action in Enum.GetValues(typeof(UserAction)))
                 {
                     CheckBox cb = new CheckBox();
                     cb.Content = action.ToString();
                     User user = value as User;
                     if (user != null)
                     {
                         currentUser = user;
                         cb.IsChecked = (action & user.Action) != 0;
                         cb.Checked += (sender, e) =>
                             {
                                 currentUser.Action |= (UserAction)Enum.Parse(typeof(UserAction), (string)cb.Content);
                             };
                         cb.Unchecked += (sender, e) =>
                             {
                                 currentUser.Action ^= (UserAction)Enum.Parse(typeof(UserAction), (string)cb.Content);
                             };
                     }
                     checks.Add(cb);
                 }
                 lb.ItemsSource = checks;
                 return lb;
             }
    
             public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 throw new NotImplementedException();
             }
         }
    Разметка окна
    <Window x:Class="WpfApplication3.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:local="clr-namespace:WpfApplication3"
             Title="MainWindow" Height="350" Width="525">
         <Window.Resources>
             <local:ContentConverter x:Key="ContentConverter"/>
         </Window.Resources>
         <Grid>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition/>
                 <ColumnDefinition Width="2*"/>
             </Grid.ColumnDefinitions>
             <ListBox Name="lstUsers" BorderBrush="Black" DisplayMemberPath="FirstName"/>
             <StackPanel Orientation="Vertical" Grid.Column="1"
                         DataContext="{Binding ElementName=lstUsers, Path=SelectedItem}">
                 <TextBox Text="{Binding FirstName}" Margin="15"/>
                 <TextBox Text="{Binding LastName}" Margin="15"/>
                 <ContentControl Margin="15">
                     <ContentControl.Content>
                         <Binding ElementName="lstUsers" Path="SelectedItem" Converter="{StaticResource ContentConverter}"/>
                     </ContentControl.Content>
                 </ContentControl>
             </StackPanel>
         </Grid>
     </Window>
    Может немного коряво, но работает

    • Помечено в качестве ответа Abolmasov Dmitry 27 июля 2012 г. 6:25
    26 июля 2012 г. 9:07

Все ответы

  • Добрый день.

    Я бы делал через паттерн "декоратор" (он же обертка, мне нравиться больше второе название).

    Идея проста, мы наш класс User спрячем внутри класса "обертки":

    public class UserWrapper
    {
        public User User { get; set; }
    
        public bool Action1
        {
            get
            {
                return (User.action & UserAction.Action1) == UserAction.Action1;
            }
            set
            {
                User.action |= UserAction.Action1;
            }
        }
    
        public bool Action2
        {
            get
            {
                return (User.action & UserAction.Action2) == UserAction.Action2;
            }
            set
            {
                User.action |= UserAction.Action2;
            }
        }
    
        public bool Action3
        {
            get
            {
                return (User.action & UserAction.Action3) == UserAction.Action3;
            }
            set
            {
                User.action |= UserAction.Action3;
            }
        }
    
        public bool Action4
        {
            get
            {
                return (User.action & UserAction.Action4) == UserAction.Action4;
            }
            set
            {
                User.action |= UserAction.Action4;
            }
        }
    }

    Затем, сделаем разметку для нашего ListBox:

    <ListBox x:Name="lbActions">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding User.Name}" Width="100" />
                    <CheckBox IsChecked="{Binding Action1}" />
                    <CheckBox IsChecked="{Binding Action2}" />
                    <CheckBox IsChecked="{Binding Action3}" />
                    <CheckBox IsChecked="{Binding Action4}" />                    
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    Ну и собственно пример, как я оборачиваю классы User обертками:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // Создаю список
            List<User> users = new List<User>();
            for (int i = 0; i < 16; i++)
            {
                users.Add(
                    new User() 
                    { 
                        Name = string.Format("Иванов {0}", i), 
                        action = (UserAction)i 
                    }
                    );
            }
            // Оборачиваю "обертками"
            List<UserWrapper> wrappers = new List<UserWrapper>();
            foreach (var user in users)
            {
                wrappers.Add(
                    new UserWrapper()
                    {
                        User = user
                    }
                    );
            }
            lbActions.ItemsSource = wrappers;
        }
    }

    Выглядит это вот так:

    Это то, что вам нужно?

    25 июля 2012 г. 19:19
    Отвечающий
  • Спасибо, но это немного не то. Хотелось бы немного универсальней сделать. 

    Если в UserAction будет 30 полей или я изменю UserAction? тогда придется переделывать класс обертки.

    Я начал копать в сторону конвертера. Выкладываю код 

    [Flags]
         public enum UserAction
         {
             Action1 = 1,
             Action2 = 2,
             Action3 = 4,
             Action4 = 8,
         }
    
         public class User
         {
             public string FirstName { get; set; }
             public string LastName { get; set; }
             public UserAction Action { get; set; }
    
             public User() { }
             public User(string fName, string lName, UserAction action)
             {
                 FirstName = fName;
                 LastName = lName;
                 Action = action;
             }
         }
    
         public class UserActionConverter : IValueConverter
         {
             public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 User user = parameter as User;
                 if (user != null)
                 {
                     if (((UserAction)value & user.Action) != 0)
                         return true;
                 }
                 return false;
             }
    
             public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 throw new NotImplementedException();
             }
         }

    Разметка окна:

    <Window x:Class="WpfApplication3.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:local="clr-namespace:WpfApplication3"
             Title="MainWindow" Height="350" Width="525">
         <Window.Resources>
             <local:UserActionConverter x:Key="UserActionConverter"/>
             <ObjectDataProvider x:Key="UserActionEnum" ObjectType="{x:Type sys:Enum}"
                                 MethodName="GetValues">
                 <ObjectDataProvider.MethodParameters>
                     <x:Type TypeName="local:UserAction"/>
                 </ObjectDataProvider.MethodParameters>
             </ObjectDataProvider>
         </Window.Resources>
         <Grid>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition/>
                 <ColumnDefinition Width="2*"/>
             </Grid.ColumnDefinitions>
             <ListBox Name="lstUsers" BorderBrush="Black" DisplayMemberPath="FirstName"/>
             <StackPanel Orientation="Vertical" Grid.Column="1"
                         DataContext="{Binding ElementName=lstUsers, Path=SelectedItem}">
                 <TextBox Text="{Binding FirstName}" Margin="15"/>
                 <TextBox Text="{Binding LastName}" Margin="15"/>
                 <ListBox Name="lstActions" ItemsSource="{Binding Source={StaticResource UserActionEnum}}">
                     <ListBox.ItemTemplate>
                         <DataTemplate>
                             <StackPanel Orientation="Horizontal" >
                                 <CheckBox DataContext="{Binding ElementName=lstUsers, Path=SelectedItem}"
                                                           IsChecked="{Binding Path=Action,
                                                     Converter={StaticResource UserActionConverter}}"/>
                                 <TextBlock Text="{Binding}"/>
                             </StackPanel>
                         </DataTemplate>
                     </ListBox.ItemTemplate>
                 </ListBox>
             </StackPanel>
         </Grid>
     </Window>

    Вот теперь дело за малым - как в параметр конвертера передать SelelectedItem родительского ListBox?

    26 июля 2012 г. 5:52
  • Формулируйте сразу точнее то, что вам нужно, тогда и ответ будуту получать более точный ))

    Я вчера, когда начинал писать ответ, тоже начал с конверторов, если вам надо только показывать состояние, то никаких проблем, а вот если вы хотите, чтобы при клике пользователь изменял состояние у UserAction, то через конвертор не получится, т.к. на основе значения из одного CheckBox вы не сможите востановить все состояние Action.

    Поэтому, если не торопитесь, вечером могу показать пример с переписыванием обертки со списка полей на индексатор... Ну или можете это сделать сами.

    26 июля 2012 г. 6:09
    Отвечающий
  • Не тороплюсь, подожду до вечера. и сам  пока попробую
    26 июля 2012 г. 6:20
  • получил вполне работающий способ через другой вариант конвертера

     [Flags]
         public enum UserAction
         {
             Action1 = 1,
             Action2 = 2,
             Action3 = 4,
             Action4 = 8,
         }
    
         public class User
         {
             public string FirstName { get; set; }
             public string LastName { get; set; }
             public UserAction Action { get; set; }
    
             public User() { }
             public User(string fName, string lName, UserAction action)
             {
                 FirstName = fName;
                 LastName = lName;
                 Action = action;
             }
         }
    
         public class ContentConverter : IValueConverter
         {
             static User currentUser;
    
             public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 ListBox lb = new ListBox();
                 List<CheckBox> checks = new List<CheckBox>();
                 foreach(UserAction action in Enum.GetValues(typeof(UserAction)))
                 {
                     CheckBox cb = new CheckBox();
                     cb.Content = action.ToString();
                     User user = value as User;
                     if (user != null)
                     {
                         currentUser = user;
                         cb.IsChecked = (action & user.Action) != 0;
                         cb.Checked += (sender, e) =>
                             {
                                 currentUser.Action |= (UserAction)Enum.Parse(typeof(UserAction), (string)cb.Content);
                             };
                         cb.Unchecked += (sender, e) =>
                             {
                                 currentUser.Action ^= (UserAction)Enum.Parse(typeof(UserAction), (string)cb.Content);
                             };
                     }
                     checks.Add(cb);
                 }
                 lb.ItemsSource = checks;
                 return lb;
             }
    
             public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
             {
                 throw new NotImplementedException();
             }
         }
    Разметка окна
    <Window x:Class="WpfApplication3.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:local="clr-namespace:WpfApplication3"
             Title="MainWindow" Height="350" Width="525">
         <Window.Resources>
             <local:ContentConverter x:Key="ContentConverter"/>
         </Window.Resources>
         <Grid>
             <Grid.ColumnDefinitions>
                 <ColumnDefinition/>
                 <ColumnDefinition Width="2*"/>
             </Grid.ColumnDefinitions>
             <ListBox Name="lstUsers" BorderBrush="Black" DisplayMemberPath="FirstName"/>
             <StackPanel Orientation="Vertical" Grid.Column="1"
                         DataContext="{Binding ElementName=lstUsers, Path=SelectedItem}">
                 <TextBox Text="{Binding FirstName}" Margin="15"/>
                 <TextBox Text="{Binding LastName}" Margin="15"/>
                 <ContentControl Margin="15">
                     <ContentControl.Content>
                         <Binding ElementName="lstUsers" Path="SelectedItem" Converter="{StaticResource ContentConverter}"/>
                     </ContentControl.Content>
                 </ContentControl>
             </StackPanel>
         </Grid>
     </Window>
    Может немного коряво, но работает

    • Помечено в качестве ответа Abolmasov Dmitry 27 июля 2012 г. 6:25
    26 июля 2012 г. 9:07
  • Спасибо, что выложили решение проблемы

    Для связи [mail]

    27 июля 2012 г. 6:26