2009-10-10 3 views
5

Так что может быть толкая границы только немного ...WPF ComboBox/ListBox с MULTISELECT на основе Enum с флагами

В принципе у меня есть следующее перечисление, объявленный в C# код:

[Flags] 
public enum FlaggedEnum : int 
{ 
    Option1 = 1, 
    Option2 = 2, 
    Option3 = 4, 
    Option4 = 8, 
    ... 
    Option16 = 32768, 
    None = 0 
} 

Это перечисление является членом объекта, который я успешно привязал к объекту DataGrid. Успешно это означает, что я успешно связал все остальные поля. :)

Что я хочу достичь здесь, это элемент управления, где все соответствующие параметры выше проверены, что ведет себя и действует как ComboBox/ListBox. Таким образом, вы нажимаете на поле и выпадающее меню появляется с возможностью «проверять», какие параметры необходимы.

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

Я новичок в WPF, поэтому я понятия не имею, куда отделяться от создания ComboBox и привязки к столбцу ... Любая помощь будет оценена!

ответ

4

У меня есть способ, который может работать. Я не беру на себя ответственность за это - я нашел этот метод в Интернете и забыл сохранить адрес.

В моем проекте мне нужно было привязать несколько флажков к перечислению флага. Чтобы помочь, я нашел реализацию простого преобразователя значений для упрощения двусторонней привязки. Он не является общим, и один экземпляр конвертера может работать только с одной целью (что означает один экземпляр значения и его группы флажков) за раз. Конвертер использует хранимую ссылку на значение как способ преобразования обратно, поэтому, если вы попытаетесь повторно использовать его между отдельными экземплярами объекта, он не будет работать. Тем не менее, это единственное, что я использовал для чего-то подобного, и это работало как шарм.

Преобразователь:

/// <summary> 
/// Provides for two way binding between a TestErrors Flag Enum property and a boolean value. 
/// TODO: make this more generic and add it to the converter dictionary if possible 
/// </summary> 
public class TestActionFlagValueConverter : IValueConverter { 
    private TestErrors target; 

    public TestActionFlagValueConverter() { 

    } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { 
     TestErrors mask = (TestErrors)parameter; 
     this.target = (TestErrors)value; 
     return ((mask & this.target) != 0); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { 
     this.target ^= (TestErrors)parameter; 
     return this.target; 
    } 
} 

В XAML используется таким образом:

<StackPanel.Resources> 
    <local:TestActionFlagValueConverter x:Key="TestActionFlagValueConverter"/> 
</StackPanel.Resources> 

<CheckBox IsChecked="{Binding Errors, Converter={StaticResource TestActionFlagValueConverter}, ConverterParameter={x:Static local:TestErrors.PowerFailure}... 
<CheckBox IsChecked="{Binding Errors, Converter={StaticResource TestActionFlagValueConverter}, ConverterParameter={x:Static local:TestErrors.OpenCondition}... 

В вашем случае вы можете поместить это в шаблон Datacell (хотя, очевидно, вы, вероятно, предпочитают использовать выпадающий ragther чем простая стековая панель. Обязательно создайте конвертер рядом с контейнером группы флажков, чтобы убедиться, что у них есть собственный экземпляр преобразователя.

Редактировать:

Здесь я сделал небольшой тестовый проект, чтобы продемонстрировать использование этого в combobox с datagrid, он основан на стандартном WPF-приложении - просто не забудьте ссылаться на набор инструментов WPF.

Вот файл Window1.xaml:

<Window 
    x:Class="FlagEnumTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:Controls="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    xmlns:FlagEnumTest="clr-namespace:FlagEnumTest" 
    Title="Window1" Height="300" Width="300"> 

    <Window.Resources> 
     <x:Array Type="{x:Type FlagEnumTest:TestObject}" x:Key="TestArray"> 
      <FlagEnumTest:TestObject Errors="OpenCondition" /> 
      <FlagEnumTest:TestObject /> 
     </x:Array> 
    </Window.Resources> 

    <StackPanel> 

     <Controls:DataGrid ItemsSource="{Binding Source={StaticResource TestArray}}"> 
      <Controls:DataGrid.Columns> 
       <Controls:DataGridTemplateColumn Header="Errors"> 
        <Controls:DataGridTemplateColumn.CellTemplate> 
         <DataTemplate> 
          <ComboBox> 
           <ComboBox.Resources> 
            <FlagEnumTest:TestErrorConverter x:Key="ErrorConverter" /> 
           </ComboBox.Resources> 
           <CheckBox Content="PowerFailure" IsChecked="{Binding Path=Errors, Converter={StaticResource ErrorConverter}, ConverterParameter={x:Static FlagEnumTest:TestErrors.PowerFailure}}" /> 
           <CheckBox Content="OpenCondition" IsChecked="{Binding Path=Errors, Converter={StaticResource ErrorConverter}, ConverterParameter={x:Static FlagEnumTest:TestErrors.OpenCondition}}" /> 
          </ComboBox> 
         </DataTemplate> 
        </Controls:DataGridTemplateColumn.CellTemplate> 
       </Controls:DataGridTemplateColumn> 
      </Controls:DataGrid.Columns> 
     </Controls:DataGrid> 

    </StackPanel> 
</Window> 

А вот файл отделенного кода Window1.xaml.cs.

using System; 
using System.Globalization; 
using System.Windows; 
using System.Windows.Data; 

namespace FlagEnumTest { 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window { 
     public Window1() { 
      InitializeComponent(); 
     } 
    } 

    [Flags] 
    public enum TestErrors { 
     NoError = 0x0, 
     PowerFailure = 0x1, 
     OpenCondition = 0x2, 
    } 

    public class TestObject { 
     public TestErrors Errors { get; set; } 
    } 

    /// <summary> 
    /// Provides for two way binding between a TestErrors Flag Enum property and a boolean value. 
    /// TODO: make this more generic and add it to the converter dictionary if possible 
    /// </summary> 
    public class TestErrorConverter : IValueConverter { 
     private TestErrors target; 

     public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { 
      TestErrors mask = (TestErrors)parameter; 
      this.target = (TestErrors)value; 
      return ((mask & this.target) != 0); 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { 
      this.target ^= (TestErrors)parameter; 
      return this.target; 
     } 
    } 

} 

По умолчанию DataGrid будет создавать свое собственное представление колонки, а также мой мандатом шаблонные один, так что вы можете увидеть текстовое представление, а также флажок один. Перечень флагов смешивает текстовое представление по умолчанию, но вы все равно можете видеть, что привязка работает правильно (проверьте оба, затем снимите флажок, который вы отметили последним, - значение строки изменится на другое, а не 0).

+0

Cheers, похоже, что это сработает отлично! Я пойду дальше и попытаюсь применить его в своем приложении. – sohum

+0

Егор, я смог заставить его работать отлично. Мне было интересно, могу ли я сообщить DataGrid, что строка была изменена, когда состояние флажка было изменено? Прямо сейчас я полагаюсь на реализацию IEditableInterface на связанных данных для записи обновлений БД. Однако, редактирование флажков не срабатывает. – sohum

+1

Кроме того, возможно ли изменить значение, указанное в поле со списком? Я заметил свойство SelectionBoxItem, но похоже, что это только для чтения. – sohum