2010-08-13 15 views
5

Quiero vincular una enumeración que tiene atributos de banderas en un cuadro de lista con una plantilla de elemento de cuadro de lista de verificación en el patrón mvvm? ¿Cómo puedo hacer esto?Cómo enlazar indicadores Enums a ListBox En MVVM

[Flags] 
public enum SportTypes 
{ 
    None = 0, 
    Baseball = 1, 
    Basketball = 2, 
    Football = 4, 
    Handball = 8, 
    Soccer = 16, 
    Volleyball = 32 
} 


<ListBox Name="checkboxList2" 
       ItemsSource="{Binding Sports}" 

       Margin="0,5" 
       SelectionMode="Multiple"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter}}" 
        Content="{Binding Item}"/> 
       </DataTemplate> 

      </ListBox.ItemTemplate> 
+0

@HCL - La solución con un convertidor de valor no funciona porque ConverterParameter no es enlazable. – mkus

Respuesta

3

Veo dos soluciones: una que es completamente dinámica y otra estática. La solución dinámica es mucho trabajo y la OMI no es trivial. El estático debe ser fácil:

Cree un Panel dentro de su DataTemplate. Allí, coloque para cada Flag-Value un CheckBox. Luego use el parámetro Convertidor para especificar el indicador, el convertidor debería usar. Esto sería algo como esto:

<StackPanel>  
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Baseball}}" Content="Baseball"/> 
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Basketball}}" Content="Basketball"/> 
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource enumBooleanConverter},ConverterParameter={x:Static local:SportTypes.Football}}" Content="Football"/> 
    <CheckBox IsChecked="{Binding ..../> 
</StackPanel/> 

En su convertidor sólo tiene que hacer algunas comparaciones lógicas AND-y usted tendrá lo que estás buscando. Si está interesado en la solución dinámica, haga un comentario, puedo darle algunas ideas de dónde empezar. Pero IMO esto realmente no será trivial.

información Additonal

Si usted quiere tener una lista en lugar de StackPanel, use un ScrollViewer en una frontera o incluso un cuadro de lista.

+0

¿Qué escribe exactamente en el método ConvertBack para enlace bidireccional? – TDaver

4

No puede enlazar fácilmente el valor directamente, porque el convertidor no puede construir la combinación de indicadores desde un solo indicador. Por lo tanto, debe administrar una colección de indicadores y crear la combinación basada en esta colección. La dificultad es que la propiedad ListBox.SelectedItems es de solo lectura. Sin embargo, this blog post ofrece una buena solución, utilizando una propiedad adjunta.

Aquí está un ejemplo completo utilizando esta solución:

de código subyacente

[Flags] 
public enum MyEnum 
{ 
    Foo = 1, 
    Bar = 2, 
    Baz = 4 
} 

public partial class TestEnum : Window, INotifyPropertyChanged 
{ 
    public TestEnum() 
    { 
     InitializeComponent(); 
     _flags = (MyEnum[])Enum.GetValues(typeof(MyEnum)); 
     _selectedFlags = new ObservableCollection<MyEnum>(); 
     _selectedFlags.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_selectedFlags_CollectionChanged); 
     this.DataContext = this; 
    } 

    void _selectedFlags_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     if (_selectedFlags.Count == 0) 
      Value = default(MyEnum); 
     else 
      Value = _selectedFlags.Aggregate((v, acc) => acc | v); 
    } 

    private MyEnum[] _flags; 
    public MyEnum[] Flags 
    { 
     get { return _flags; } 
     set 
     { 
      _flags = value; 
      OnPropertyChanged("Flags"); 
     } 
    } 

    private ObservableCollection<MyEnum> _selectedFlags; 
    public ObservableCollection<MyEnum> SelectedFlags 
    { 
     get { return _selectedFlags; } 
     set 
     { 
      _selectedFlags = value; 
      OnPropertyChanged("SelectedFlags"); 
     } 
    } 

    private MyEnum _value; 
    public MyEnum Value 
    { 
     get { return _value; } 
     set 
     { 
      _value = value; 
      OnPropertyChanged("Value"); 
      var currentFlags = _flags.Where(f => _value.HasFlag(f)); 
      var addedFlags = currentFlags.Except(_selectedFlags).ToArray(); 
      var removedFlags = _selectedFlags.Except(currentFlags).ToArray(); 
      foreach (var f in addedFlags) 
      { 
       _selectedFlags.Add(f); 
      } 
      foreach (var f in removedFlags) 
      { 
       _selectedFlags.Remove(f); 
      } 
     } 
    } 

    private DelegateCommand<MyEnum> _setValueCommand; 
    public ICommand SetValueCommand 
    { 
     get 
     { 
      if (_setValueCommand == null) 
      { 
       _setValueCommand = new DelegateCommand<MyEnum>(v => Value = v); 
      } 
      return _setValueCommand; 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

XAML

<Window x:Class="TestPad.TestEnum" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:my="clr-namespace:TestPad" 
     Title="TestEnum" Height="300" Width="300"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="*" /> 
      <RowDefinition Height="*" /> 
     </Grid.RowDefinitions> 
     <TextBlock Text="{Binding Value}" /> 
     <ListBox Grid.Row="1" 
       ItemsSource="{Binding Flags}" 
       SelectionMode="Extended" 
       my:MultiSelectorBehavior.SynchronizedSelectedItems="{Binding SelectedFlags}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <CheckBox Content="{Binding}" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}" /> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 
     <ItemsControl Grid.Row="2" 
         ItemsSource="{Binding Flags}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <Button Command="{Binding DataContext.SetValueCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" 
          CommandParameter="{Binding}" 
          Content="{Binding}" /> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </Grid> 
</Window> 

NOTA: en aras de la simplicidad, el modelo de vista es la Clase de ventana En una aplicación real de MVVM, por supuesto, utilizaría una clase de modelo de vista independiente ...

0

Para ampliar la publicación de Chris, aquí hay una explicación más completa de cómo puede hacerlo.

Este no es el escenario más ideal, ya que la propiedad que tiene la enumeración tiene que ser un poco más compleja de lo habitual, pero funciona.código

Convertidor:

public class EnumFlagConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var theEnum = value as Enum; 
     return theEnum.HasFlag(parameter as Enum); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var theEnum = parameter as Enum; 
     return theEnum; 
    } 
} 

XAML para el convertidor:

<StackPanel> 
    <StackPanel.Resources> 
     <local:EnumFlagConverter x:Key="MyConverter" /> 
    </StackPanel.Resources> 
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Baseball}}" Content="Baseball"/> 
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Basketball}}" Content="Basketball"/> 
    <CheckBox IsChecked="{Binding Path=SportTypeEnum, Converter={StaticResource MyConverter},ConverterParameter={x:Static local:SportTypes.Football}}" Content="Football"/> 
    <CheckBox IsChecked="{Binding ..../> 
</StackPanel> 

Entonces, para unirse a esto, hice un poco de engaño para obtener el convertidor funcione correctamente:

private SportTypeEnum _TheSportType; 
public SportTypeEnum _TheSportType 
{ 
    get { return _TheSportType; } 
    set 
    { 
     if (_TheSportType.HasFlag(value)) 
      _TheSportType &= ~value; 
     else 
      _TheSportType |= value; 
     NotifyPropertyChanged(); 
    } 
} 

Debido a esta lógica especial del setter, es probable que desee incluir un método como este para permitirle establece completamente el valor del código:

private void ResetTheSportType() 
{ 
    _TheSportType = _TheSportType.None; 
    NotifyPropertyChanged(() => TheSportType); 
} 
Cuestiones relacionadas