2009-10-10 21 views
5

Así que puede estar empujando los límites sólo un poco ...WPF ComboBox/ListBox con selección múltiple basado en Enum con Banderas

Básicamente tengo la siguiente enumeración, declarada en el código C#:

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

Esta enumeración es un miembro de un objeto que he vinculado exitosamente a un objeto DataGrid. Lo que significa con éxito que he vinculado todos los otros campos con éxito. :)

Lo que quiero lograr aquí es un control donde se verifican todas las opciones apropiadas anteriores, que se comporta y actúa como un ComboBox/ListBox. Entonces, hace clic en el campo y aparece un menú desplegable con la capacidad de "marcar" las opciones que sean necesarias.

El control también deberá poder leer desde la enumeración y escribir una enumeración.

Soy un novato de WPF, así que no tengo idea de dónde separarme de crear un ComboBox y encuadernarlo a la columna ... ¡Cualquier ayuda sería apreciada!

Respuesta

4

Tengo una manera que podría funcionar. No me atribuyo ningún mérito: encontré este método en la web y olvidé guardar la dirección.

En mi proyecto tenía que enlazar unas pocas casillas de verificación con una marca de enum. Para ayudar, encontré una implementación de un convertidor de valores simple para facilitar el enlace bidireccional. No es genérico, y una sola instancia de un convertidor solo puede funcionar con un objetivo (es decir, una instancia de un valor y su grupo de casillas de verificación) a la vez. El convertidor utiliza una referencia almacenada al valor como una forma de convertir de nuevo, por lo que si intenta reutilizarlo entre instancias de objetos separados, no funcionará. Dicho esto, este es el único uso que tuve para algo como esto y funcionó a las mil maravillas.

El convertidor:

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

en XAML que se utiliza de esta manera:

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

En su caso puede colocar esto en su plantilla DataCELL (aunque, obviamente, es probable que prefiere utilizar un ragther cuadro combinado que un simple panel de distribución. Asegúrese de crear una instancia del convertidor cerca del contenedor del grupo de casillas de verificación para asegurarse de que tengan su propia instancia del convertidor.

Edit:

Aquí, realicé un pequeño proyecto de prueba para demostrar el uso de este en un cuadro combinado con una cuadrícula de datos, está basado en la aplicación WPF predeterminada; solo asegúrese de hacer referencia al kit de herramientas de WPF.

Aquí está el archivo 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> 

Y aquí es el archivo de código subyacente 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; 
     } 
    } 

} 

Por defecto, la cuadrícula de datos va a crear su propia representación de la columna, así como mi una plantilla mandato, por lo que se puede ver la representación de texto, así como la casilla uno. La enumeración de la bandera confunde la representación de texto predeterminada, pero aún puede ver que la vinculación funciona correctamente (marque ambas, luego desmarque la última que verificó, el valor de la cadena cambia a la otra, no a la 0).

+0

¡Salud, parece que funcionaría perfectamente! Continuaré y trataré de implementarlo en mi aplicación. – sohum

+0

Egor, pude hacerlo funcionar perfectamente. Me preguntaba si podría indicar al DataGrid que una fila se modificó cuando se modificó el estado de una casilla de verificación. En este momento estoy confiando en una implementación de IEditableInterface en los datos enlazados para escribir actualizaciones en un DB back-end. Sin embargo, editar las casillas de verificación no activa este comportamiento. – sohum

+1

Además, ¿es posible cambiar el valor que se muestra en el cuadro combinado? Noté la propiedad SelectionBoxItem pero parece que es de solo lectura. – sohum

Cuestiones relacionadas