2009-08-02 11 views
10

Me he estado volviendo loco con vincular un combobox a una propiedad enum tipada de una clase, donde la enumeración en sí se declara en esa misma clase.Enlazando un ComboBox a una enumeración anidada en una clase

Estoy tratando de seguir la respuesta proporcionada aquí (wpf combobox binding to enum what i did wrong?) Específicamente, estoy usando el código MarkupExtension sugerido y el código xaml coincidente.

Mi código de trabajo es:

Definición de la enumeración en un archivo separado.

namespace EnumTest 
{ 
    public enum TestEnum {one, two, three, four }; 
} 

clase que utiliza la enumeración (Tenga en cuenta que el código PropertyChanged se ha eliminado para simplificar las cosas):

namespace EnumTest 
{ 
    public class Test : INotifyPropertyChanged 
    { 
     private TestEnum _MyVar; 
     public TestEnum MyVar { 
      get { return _MyVar; } 
      set 
      { 
       _MyVar = value; 
       OnPropertyChanged("MyVar"); 
      } 
     } 

     public Test() 
     { 
      _MyVar = TestEnum.three; 
     } 
    } 
} 

archivo de programa que utiliza la clase: Método de extensión

namespace EnumTest 
{ 
    public partial class Window1 : Window 
    { 
     Test _oTest = new Test(); 

     public Window1() 
     { 
      InitializeComponent(); 
      cmbBox.DataContext = _oTest; 
     } 
    } 
} 

de mostrando el Enum

namespace EnumTest 
{ 
    [MarkupExtensionReturnType(typeof(object[]))] 
    public class EnumValuesExtension : MarkupExtension 
    { 
     public EnumValuesExtension() 
     { 
     } 

     public EnumValuesExtension(Type enumType) 
     { 
      this.EnumType = enumType; 
     } 

     [ConstructorArgument("enumType")] 
     public Type EnumType { get; set; } 

     public override object ProvideValue(IServiceProvider serviceProvider) 
     { 
      if (this.EnumType == null) 
       throw new ArgumentException("The enum type is not set"); 
      return Enum.GetValues(this.EnumType); 
     } 
    } 
} 

Y el código XAML que se utiliza para mostrar los datos:

<Window x:Class="EnumTest.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:w="clr-namespace:EnumTest" 
    Title="Window1" Height="300" Width="300"> 
    <Grid> 
     <ComboBox Name="cmbBox" 
        Height="20" 
        Width="80" 
        ItemsSource="{Binding Source={w:EnumValues EnumType=w:TestEnum}}" 
        SelectedItem="{Binding Path=MyVar}" 
        /> 
    </Grid> 
</Window> 

Lo anterior es todo bien y dandy, pero quiero definir la enumeración dentro la clase de prueba y deshacerse de la enumeración de ser definido en el alcance global. De este modo:

namespace EnumTest 
{ 
    public class Test : INotifyPropertyChanged 
    { 
     // Declare Enum **INSIDE** the class 
     public enum TestEnum {one, two, three, four }; 
     private TestEnum _MyVar; 
     public TestEnum MyVar { 
      get { return _MyVar; } 
      set 
      { 
       _MyVar = value; 
       OnPropertyChanged("MyVar"); 
      } 
     } 

     public Test() 
     { 
      _MyVar = TestEnum.three; 
     } 
    } 
} 

la pregunta para que me he referido alude a la sintaxis XAML la igualación es:

 <ComboBox Name="cmbBox" 
        ... 
        ItemsSource="{Binding Source={w:EnumValues EnumType=w:Test+TestEnum}}" 
        ... 
        /> 

Pero esta (casi) no funciona para mí. Cuando hago una generación limpia consigo un "Construir tenido éxito" mensaje en la barra de estado VS 2008, pero también me da un error que se informa en el xaml

Type 'Test+TestEnum' was not found. 

Sin embargo, el código se ejecuta como se esperaba!

Sin embargo, esto significa que el diseñador xaml no se cargará. Así que estoy algo atormentado en hacer cualquier trabajo más wpf hasta que pueda borrar el error xaml.

Me pregunto si esto es un problema VS 2008 SP1, y no es un problema de mi parte.

Editar

  1. hicieron que mi planteamiento del problema más explícito.
  2. Probamos primera solución de Joel, pero que terminó con el código que se ejecuta y 2 errores xaml
  3. segunda solución probada de Joel y que trabajaban directamente de la caja - por lo que voy con eso!

Notas la pregunta para que me dieron el código MarkupExtension de los usos de este estilo de sintaxis para el XAML:

<ComboBox ItemsSource="{w:EnumValues w:TestEnum}"/> 

Cuando uso que consigo un error de compilación diciendo que ningún constructor EnumValues ​​toma 1 parámetro. Hice algunos google y esto parece ser un error en VS. Estoy usando VS 2008 SP1. Vi algunos comentarios que aludían a que estaba en la versión beta de VS 2010. De todos modos, es por eso que uso la sintaxis XAML de

<ComboBox ItemsSource="{w:EnumValues EnumType=w:TestEnum}"/> 

este tipo de obras de sintaxis!

+0

En cuanto al error sobre el constructor, ocurre solo en el diseñador.Debería funcionar bien de lo contrario. Para evitar este error, defina la extensión de marcado en un ensamblaje aparte –

+0

@Thomas: había olvidado cómo el diseñador puede mostrarse irritable sobre qué tipos se crean cuándo/dónde y cómo los crea. –

+0

@Joel - Y los programadores novatos de wpf saben de qué se trata. Saber poner una clase en un ensamble por separado para satisfacer una parte de la herramienta de construcción es bastante oscuro en mi humilde opinión. Pero seguiré trabajando duro hasta que tenga un entendimiento razonable. Gracias de nuevo. –

Respuesta

3

Otra forma de obtener los valores de enumeración para su uso como fuente de datos:

<Window.Resources> 
    <ObjectDataProvider 
     MethodName="GetValues" 
     ObjectType="{x:Type sys:Enum}" 
     x:Key="TestValues"> 
     <ObjectDataProvider.MethodParameters> 
      <w:Type2 
       TypeName="w:Test+TestEnum" /> 
     </ObjectDataProvider.MethodParameters> 
    </ObjectDataProvider> 
</Window.Resources> 

... 

ItemsSource="{Binding Source={StaticResource TestValues}}" 

Tenga en cuenta que todavía necesita la Type2Extension debido a la rareza con TypeExtension y tipos anidados. Pero no necesitaría la extensión adicional de marcado personalizado. De esta forma es mejor si va a utilizar la lista en varios lugares, ya que puede declararla en sus recursos App.xaml.

+0

Muchas gracias por eso, ¡me ha estado volviendo loco por demasiado tiempo! –

+1

Muy bien - Me faltaba el "+" al declarar la enumeración en los MethodParameters, y me estaba volviendo loco. –

1

¿Qué pasa con el uso de la extensión de marcado x:Type?

{w:EnumValues EnumType={x:Type w:Test+TestEnum}} 

A excepción de la aplicación de INotifyPropertyChanged, he copiado el código exactamente. Obtengo los errores que obtienes, pero parece funcionar bien. Sin embargo, es muy molesto no poder cargar el diseñador. Nada de lo que he probado ha resuelto el problema.

Encontré this page en MSDN sobre tipos anidados, y una sugerencia en ese hilo era una MarkupExtension personalizada para resolver el nombre de tipo anidado. Estoy intentando que funcione, pero hasta ahora no tuve suerte. Obtengo errores similares en Type2Extension a veces, y obtengo "El tipo de entrada no está configurado" con otros ajustes.

Aha! Hubo un error en cómo el autor original llamaba al GetType(). Aquí está el corregida Type2Extension y cómo lo estaba utilizando:

public class Type2Extension : System.Windows.Markup.TypeExtension { 
    public Type2Extension() { 
    } 

    public Type2Extension(string typeName) { 
     base.TypeName = typeName; 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) { 
     IXamlTypeResolver typeResolver = (IXamlTypeResolver) serviceProvider.GetService(typeof(IXamlTypeResolver)); 
     int sepindex = TypeName.IndexOf('+'); 
     if (sepindex < 0) 
      return typeResolver.Resolve(TypeName); 
     else { 
      Type outerType = typeResolver.Resolve(TypeName.Substring(0, sepindex)); 
      return outerType.Assembly.GetType(outerType.FullName + "+" + TypeName.Substring(sepindex + 1)); 
     } 
    } 
} 

Y XAML:

ItemsSource="{Binding Source={w:EnumValues {w:Type2 w:Test+TestEnum}}}" 

Esto parece funcionar cargas finas y el diseño. Agregaré Type2Extension a mis propias pequeñas bibliotecas.

Editar: Curiosamente, si cambio esto en EnumValues:

if (this.EnumType == null) 
    throw new ArgumentException("The enum type is not set"); 

A esto:

if (this.EnumType == null) 
    return null; 

Entonces esos errores constructor desaparece. Esa fue la otra cosa que cambié. Sin embargo, en breve voy a publicar una forma alternativa de obtener valores enum.

+0

Si quiere decir: ItemsSource = "{Binding Source = {w: EnumValues ​​EnumType = {x: Escriba w: Test + TestEnum}}}" Entonces eso acaba de dar un gran error de compilación –

+0

Interesante. Después de responder, comencé a juntar tu código de ejemplo para recrearlo (solo para ver si funcionaba). Editaré si descubro algo. –

+0

Gracias por eso. Estoy demasiado cansado para molestarme con eso ahora, pero lo veré a primera hora de la mañana. Soy nuevo en wpf y tener que saltar a través de este tipo de aros para hacer lo que debería ser algo básico me molesta por completo. –

Cuestiones relacionadas