2009-10-10 6 views
5

So kann ich die Grenzen drängen werden, nur ein bisschen ...WPF ComboBox/ListBox mit Multiselect basierend auf Enum mit Flaggen

Im Prinzip habe ich die folgende Enumeration, in C# -Code erklärt:

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

Diese Aufzählung ist ein Mitglied eines Objekts, das erfolgreich an ein DataGrid-Objekt gebunden wurde. Erfolgreich bedeutet, dass ich alle anderen Felder erfolgreich gebunden habe. :)

Was ich hier erreichen möchte, ist ein Steuerelement, wo alle entsprechenden Optionen oben überprüft werden, die verhält sich und verhält sich wie eine ComboBox/ListBox. Wenn Sie also auf das Feld klicken, wird ein Dropdown-Menü angezeigt, in dem Sie nachsehen können, welche Optionen erforderlich sind.

Die Steuerung muss auch in der Lage sein, aus der Enumeration zu lesen und eine Enumeration zu schreiben.

Ich bin ein WPF-Neuling, also habe ich keine Ahnung, wo man sich von der Erstellung einer ComboBox und der Bindung an die Spalte entfernt ... Jede Hilfe wäre willkommen!

Antwort

4

Ich habe eine Möglichkeit, die funktionieren könnte. Ich nehme das nicht an - ich habe diese Methode im Internet gefunden und vergessen, die Adresse zu speichern.

In meinem Projekt musste ich ein paar Kontrollkästchen an eine Flag Enum binden. Um zu helfen, habe ich eine Implementierung eines einfachen Wertkonverters gefunden, um das Zwei-Wege-Binden zu erleichtern. Es ist nicht generisch, und eine einzelne Instanz eines Konverters kann nur mit einem Ziel (dh einer Instanz eines Werts und seiner Gruppe von Kontrollkästchen) gleichzeitig arbeiten. Der Konverter verwendet einen gespeicherten Verweis auf den Wert als eine Möglichkeit, zurück zu konvertieren. Wenn Sie also versuchen, ihn zwischen separaten Objektinstanzen wiederzuverwenden, funktioniert er nicht. Das heißt, das ist die einzige Verwendung, die ich für so etwas hatte und es funktionierte wie ein Zauber.

Der Konverter:

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

in XAML wird es verwendet, so:

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

In Ihrem Fall Sie könnte diesen Ort in Ihrem Datacell-Vorlage (obwohl offensichtlich wahrscheinlich Sie es vorziehen, eine Combobox ragther zu verwenden als ein einfaches Stackpanel Stellen Sie sicher, dass der Konverter in der Nähe des Checkbox-Gruppencontainers instanziiert wird, um sicherzustellen, dass sie über eine eigene Instanz des Konverters verfügen

Bearbeiten:

Hier habe ich ein kleines Testprojekt zu demonstrieren, die Verwendung in einer Combobox mit einem Datagrid, es basiert auf der Standard-WPF-Anwendung - stellen Sie einfach sicher, das WPF-Toolkit zu verweisen. Hier

ist die Window1.xaml Datei:

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

Und hier ist die Window1.xaml.cs Datei Code-Behind.

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

} 

standardmäßig die Datenraster wird seine eigene Darstellung der Säule sowie mein Mandat auf Vorlagen erstellen, so dass Sie die Textdarstellung sowie die Checkbox man sehen kann. Die Flag-Enumeration verwirft die Standard-Textdarstellung, aber Sie können immer noch sehen, dass die Bindung korrekt funktioniert (markieren Sie beide, dann deaktivieren Sie das Kontrollkästchen, das Sie zuletzt überprüft haben - der String ändert sich in den anderen, nicht 0).

+0

Prost, das scheint, als würde es perfekt funktionieren! Ich werde fortfahren und versuchen, es in meine Anwendung zu implementieren. – sohum

+0

Egor, ich konnte es perfekt funktionieren. Ich habe mich gefragt, ob ich dem DataGrid signalisieren könnte, dass eine Zeile geändert wurde, wenn ein Checkbox-Status geändert wurde? Im Moment verlasse ich mich auf eine IEditableInterface-Implementierung der gebundenen Daten, um Updates in eine Backend-DB zu schreiben. Durch das Bearbeiten der Kontrollkästchen wird dieses Verhalten jedoch nicht ausgelöst. – sohum

+1

Ist es auch möglich, den in der Combobox angezeigten Wert zu ändern? Ich bemerkte die Eigenschaft SelectionBoxItem, aber es scheint, dass dies schreibgeschützt ist. – sohum

Verwandte Themen