Decir que tengo una interfaz como esta:

public interface ISomeInterface
{
...
}

También tengo un par de clases que implementan esta interfaz;

public class SomeClass : ISomeInterface
{
...
}

Ahora tengo una WPF ListBox listado de elementos de ISomeInterface, utilizando una costumbre DataTemplate.

El enlace de datos del motor al parecer no (que yo he sido capaz de averiguar) me permite enlazar a propiedades de la interfaz – se ve que el objeto es un SomeClass objeto, y los datos sólo se muestra si SomeClass debe pasar a tener la propiedad enlazada disponible como un no-interfaz de propiedad.

¿Cómo puedo saber el DataTemplate a actuar como si cada objeto es una ISomeInterface, y no un SomeClass etc.?

Gracias!

  • trate de Proxies Dinámicos
  • Gracias por la notificación @slugster, he actualizado el aceptado respuesta. 🙂

6 Comentarios

  1. 60

    Con el fin de unirse a explícito implementado los miembros de la interfaz, todo lo que necesita hacer es utilizar los paréntesis. Por ejemplo:

    implícito:

    {Binding Path=MyValue}

    explícito:

    {Binding Path=(mynamespacealias:IMyInterface.MyValue)}
    • Me alegro de que me topé con esto, también me ayudó a resolver un problema con la unión a PostSharp objetos creados; stackoverflow.com/questions/13636962/…
    • Lamentablemente, esta realidad no parece funcionar para el OP pregunta (yo sé que esta pregunta es muy viejo). Él no preguntar acerca de una unión, pero DataType="{x:Type local:SomeClassBase}".
    • esta respuesta es correcta – el OP está hablando acerca de rutas de enlace dentro de la plantilla de datos, explícitos propiedades de la interfaz en el tipo concreto no son visibles para el motor de enlace por defecto (implícito es). Creo que el aceptado la respuesta no era realmente lo que el OP fue después, sobre todo cuando esta respuesta fue publicado un año más tarde.
    • Esto funciona, pero por desgracia, el motor no es lo suficientemente sabio como para agregar un controlador para un IMyInterface.MyValueChanged evento, si es que lo hay. Esto significa que si desea que los cambios de notificaciones, la clase de implementación también debe ser la aplicación de INotifyPropertyChanged y usar el mismo nombre de la interfaz de la propiedad. Esto podría ser un problema si múltiples interfaces se implementan de manera explícita y el mismo nombre de la propiedad que existe en más de uno. Usar sabiamente.
  2. 18

    Esta respuesta de foros de Microsoft por Beatriz Costa – MSFT es que vale la pena leer (bastante antiguo):

    El enlace de datos equipo discutieron la posibilidad de añadir soporte para interfaces de hace un tiempo, pero no terminaron de implementar porque no podemos llegar con un buen diseño de la misma. El problema era que las interfaces no tienen una jerarquía como los tipos de objeto de hacer. Considere el escenario donde el origen de datos implementa tanto IMyInterface1 y IMyInterface2 y tiene DataTemplates para ambas interfaces en los recursos: que DataTemplate ¿crees que deberíamos recoger?

    Cuando se hace implícito de las plantillas de datos para tipos de objeto, primero tratamos de encontrar un DataTemplate por el tipo exacto, pues por su padre, abuelo y así sucesivamente. No está muy bien definido el orden de los tipos para aplicar. Cuando hablamos acerca de la adición de soporte para las interfaces, hemos considerado el uso de la reflexión para encontrar todas las interfaces y añadiendo al final de la lista de tipos. El problema que encontramos fue el de definir el orden de las interfaces cuando el tipo implementa múltiples interfaces.

    La otra cosa que había que tener en cuenta es que la reflexión es que no es barato, y esto disminuiría nuestra perf un poco para este escenario.

    Así que ¿cuál es la solución? Usted no puede hacer todo esto en XAML, pero usted puede hacerlo fácilmente con un poco de código. El ItemTemplateSelector propiedad de ItemsControl se puede utilizar para seleccionar el que DataTemplate desea utilizar para cada elemento. En el SelectTemplate método para su selector de plantilla, que recibe como parámetro el elemento que va de la plantilla. Aquí, usted puede comprobar para qué interfaz se implementa y devolver el DataTemplate que coincide con ella.

  3. 13

    La respuesta corta es DataTemplate la no compatibilidad con interfaces (pensar acerca de la herencia múltiple, explícita v. implícita, etc). La forma en que tienden a conseguir alrededor de esto, es tener una base de clase de cosas se extienden para que el DataTemplate especialización/generalización. Esto significa un decente, pero no necesariamente óptima, la solución sería:

    public abstract class SomeClassBase
    {
    
    }
    
    public class SomeClass : SomeClassBase
    {
    
    }
    
    <DataTemplate DataType="{x:Type local:SomeClassBase}">
        <!-- ... -->
    </DataTemplate>
    • Muchas gracias, voy a aceptar tu respuesta aunque esto significa que bastante tienen que rehacer un poco de cosas para hacer lo que quiero. Algunas veces la vida apesta cuando estás con la tarea de poner un WPF interfaz de usuario en la parte superior de una biblioteca de objetos de negocio.. 🙂
    • Jacobsen: estamos tratando con los mismos dolores de crecimiento en nuestra tienda también.
  4. 9

    Tienes otra opción. Establecer una Clave en su plantilla de datos de referencia y que la clave en el ItemTemplate. Como este:

    <DataTemplate DataType="{x:Type documents:ISpecificOutcome}"
                  x:Key="SpecificOutcomesTemplate">
        <Label Content="{Binding Name}"
               ToolTip="{Binding Description}" />
    </DataTemplate>

    a continuación, hacer referencia a la plantilla de la clave donde usted quiere usar, como este:

    <ListBox ItemsSource="{Binding Path=SpecificOutcomes}"
             ItemTemplate="{StaticResource SpecificOutcomesTemplate}"
             >
    </ListBox>

    WPF enlace de datos para la interfaz y no objeto real - casting posible?

    • Desafortunadamente, esto significa que usted tiene que decirle explícitamente cada enlace de la plantilla a utilizar. Esto es molesto porque en mi caso quiero enlazar a la misma interfaz de la propiedad, pero tiene subyacente cambio de objeto en tiempo de ejecución. No quiero decirle explícitamente la unión para aplicar sólo una plantilla de datos, porque lo que quiero es cambiar los datos de la plantilla en tiempo de ejecución dependiendo del subyacente objeto de la copia de esa propiedad.
  5. 8

    La respuesta sugerida por dummyboy es la mejor respuesta (debe ser votó a favor de la parte superior de la omi). Tiene un problema por el que el diseñador no le gusta (le da un error «Objeto nulo no puede ser utilizado como un descriptor de acceso de parámetros para un PropertyPath) pero no es una buena solución. La solución es definir el elemento en una plantilla de datos y, a continuación, establecer la plantilla a una etiqueta u otro control de contenido. Como un ejemplo, yo estaba tratando de agregar una Imagen como esta

    <Image Width="120" Height="120" HorizontalAlignment="Center" Source="{Binding Path=(starbug:IPhotoItem.PhotoSmall)}" Name="mainImage"></Image>

    Pero mantuvo a darme el mismo error. La solución fue crear una etiqueta y usar una plantilla de datos para mostrar el contenido de mi

    <Label Content="{Binding}" HorizontalAlignment="Center" MouseDoubleClick="Label_MouseDoubleClick">
        <Label.ContentTemplate>
            <DataTemplate>
                <StackPanel>
                    <Image Source="{Binding Path=(starbug:IPhotoItem.PhotoSmall)}" Width="120" Height="120" Stretch="Uniform" ></Image>
                </StackPanel>
            </DataTemplate>
        </Label.ContentTemplate>
    </Label>

    Esto tiene sus desventajas, pero parece que funciona bastante bien para mí.

    • Alguna idea de cómo/por qué esta solución funciona?
  6. 4

    Nota: puede utilizar más complejo multi-parte rutas como esta demasiado si la interfaz de la propiedad está dentro de una ruta de acceso:

     <TextBlock>
        <TextBlock.Text>
            <Binding Path="Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode"/>
        </TextBlock.Text>
     </TextBlock>

    O directamente con el Binding directiva.

     <TextBlock Text="{Binding Path=Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode}"/>

    O cuando se utilizan múltiples propiedades de una interfaz puede redefinir el DataContext localmente para hacer el código más legible.

     <StackPanel DataContext={Binding Path=Packages[0].(myNamespace:IShippingPackage.ShippingMethod)}">
        <TextBlock Text="{Binding CarrierName}"/>
        <TextBlock Text="{Binding CarrierServiceCode}"/>
      </StackPanel>

    Consejo: cuidado por querer acabar con )}al final de un Camino de expresión. Estúpido copiar/pegar error de seguir haciendo.

    Path="(myNameSpace:IShippingPackage.ShippingMethod)}"


    Asegúrese de utilizar Path=

    Descubierto que si no me explícitamente el uso de Path=, entonces puede que no sean capaces de analizar la unión.
    Normalmente voy a escribir algo como esto:

    Text="{Binding FirstName}"

    lugar de

    Text="{Binding Path=FirstName}"

    Pero con el más complejo de interfaz de enlace que he encontrado que Path= era necesaria para evitar esta excepción:

    System.ArgumentNullException: Key cannot be null.
    Parameter name: key
       at System.Collections.Specialized.ListDictionary.get_Item(Object key)
       at System.Collections.Specialized.HybridDictionary.get_Item(Object key)
       at System.ComponentModel.PropertyChangedEventManager.RemoveListener(INotifyPropertyChanged source, String propertyName, IWeakEventListener listener, EventHandler`1 handler)
       at System.ComponentModel.PropertyChangedEventManager.RemoveHandler(INotifyPropertyChanged source, EventHandler`1 handler, String propertyName)
       at MS.Internal.Data.PropertyPathWorker.ReplaceItem(Int32 k, Object newO, Object parent)
       at MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(Int32 k, ICollectionView collectionView, Object newValue, Boolean isASubPropertyChange)

    es decir, no hacer esto:

    <TextBlock Text="{Binding Packages[0].(myNamespace:IShippingPackage.ShippingMethod).CarrierServiceCode}"/>

Dejar respuesta

Please enter your comment!
Please enter your name here