WPF ComboBox / ListBox с MultiSelect на основе Enum с флагами - PullRequest
5 голосов
/ 10 октября 2009

Так что, возможно, я немного раздвигаю границы ...

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

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

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

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

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

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

1 Ответ

4 голосов
/ 10 октября 2009

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

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

Конвертер:

/// <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}...

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

Edit:

Здесь я сделал небольшой тестовый проект, чтобы продемонстрировать, как использовать его в комбинированном окне с сеткой данных, он основан на приложении 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;
        }
    }

}

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

...