2010-05-05 13 views
5

Estoy escribiendo un Control de usuario de WPF para mi aplicación, envolviendo un ListBox y algunos otros elementos.¿Cómo implementar DisplayMemberPath para mi Wpf UserControl?

ListBox tiene una nueva ItemTemplate que presenta cuatro piezas de información para cada elemento en mi lista. Puedo codificar cada una de las cuatro vinculaciones a propiedades específicas en los elementos de mi lista y se muestran bien.

Sin embargo, quiero que mi UserControl sea un poco más flexible.

En ListBox y ComboBox hay una propiedad DisplayMemberPath (heredado de ItemsControl) que parece "inyectar" la propiedad adecuada de unión en el ItemTemplate estándar.

¿Cómo obtengo el mismo resultado con mi control de usuario?

me gustaría establecer cuatro nuevas propiedades para permitir la configuración de la información que se muestra:

public string LabelDisplayPath { get; set; } 
public string MetricDisplayPath { get; set; } 
public string TitleDisplayPath { get; set; } 
public string SubtitleDisplayPath { get; set; } 

Revisión ItemsControl.DisplayMemberPath con reflector parece ir por el agujero del conejo, no he podido para entender cómo funciona.

Además, si estoy completamente fuera de curso, y hay otra técnica más "WPF" que debería usar en su lugar, por favor diríjanme en esa dirección.

actualización

He aquí una aclaración de lo que estoy tratando de lograr.

El cuadro de lista dentro de mi control del usuario aparecen cuatro piezas de información por artículo: etiqueta, título, subtítulo y métricas

En un solo lugar, quiero utilizar este control de usuario para visualizar una lista de cuestiones. Cada número es el siguiente:

public class Issue { 
    public string Code { get; set; } 
    public string Description { get; set; } 
    public string Priority { get; set; } 
    public string Reporter { get; set; } 
} 

Cuando se visualizan problemas, quiero usar las siguientes asignaciones:

Code --> Label 
Description --> Title 
Reporter --> Subtitle 
Priority --> Metric 

En otras partes de la misma aplicación, tengo una lista de mensajes que quiero visualizar utilizando el mismo UserControl. Cada mensaje tiene el siguiente aspecto:

public class Post { 
    public DateTime PostedOn { get; set; } 
    public string Title { get; set; } 
    public string Teaser { get; set; } 
    public int CommentCount { get; set; } 
} 

Cuando se muestran mensajes, quiero usar las siguientes asignaciones:

PostedOn --> Label 
Title --> Title 
Teaser --> Subtitle 
CommentCount --> Metric 

Teniendo en cuenta que las cuestiones y los mensajes son bastante diferentes abstracciones, que no quieren obligarlos tener las mismas propiedades, solo para permitir que UserControl se use sin cambios. En cambio, quiero introducir un poco de configurabilidad para que pueda reutilizar mi UserControl en ambos sitios limpiamente.

+1

No entiendo muy bien lo que estás tratando de hacer. Al "codificar" sus enlaces, aún obtiene flexibilidad por el mero hecho de que está utilizando enlace de datos, ¿verdad? Quiero decir, podrías vincular a DependencyProperty y cambiar ese valor en el código subyacente. ¿Puedes publicar una muestra del XAML que estás utilizando y destacar qué es lo que no te gusta? – Dave

Respuesta

2

he encontrado la solución a mi problema - que es un poco más complicado de lo que me gustaría ...

En cualquier ItemsControl, cuando se establece la propiedad DisplayMemberPath, una instancia de la clase interna es DisplayMemberTemplateSelector asignado al ItemTemplateSelector.En efecto, esta clase genera un DataTemplate personalizado con un enlace apropiado para adaptarse a la configuración actual de DisplayMemberPath. Se crea una nueva instancia de esta clase y se usa cada vez que se cambia DisplayMemberPath.

Para replicar esto en mi propio Usercontrol, en lugar de modificar mi plantilla de datos existente sobre la marcha como había pensado originalmente, necesito crear mi propia clase de asistente de selector de plantillas e imitar el mismo enfoque que ItemsControl.

+0

Lo sé hace mucho tiempo, pero ¿tiene el ayudante disponible en alguna parte? –

+0

Lamentablemente, ya no tengo acceso a ese código, fue escrito para un empleador anterior. – Bevan

0

Después de una gran cantidad de consejos de su actualización y comentario, creo que se puede lograr que el uso de DependencyProperty

Déjeme darle el contorno de la idea

XAML:

<UserControl x:Name="myControl"> 
    <StackPanel> 
     <TextBlock x:Name="textBlock" 
        Text="{Binding ElementName=myControl, Path=LabelDisplayPath}" /> 
     <TextBlock x:Name="textBlock" 
        Text="{Binding ElementName=myControl, Path=MetricDisplayPath}" /> 
     <TextBlock x:Name="textBlock" 
        Text="{Binding ElementName=myControl, Path=TitleDisplayPath}" /> 
     <TextBlock x:Name="textBlock" 
        Text="{Binding ElementName=myControl, Path=SubtitleDisplayPath}" /> 
    </StackPanel> 
</UserControl> 

donde DisplayPaths son las propiedades de dependencia

Ahora

crear estas propiedades de dependencia en el código de control de usuario detrás: LabelDisplayPathProperty, MetricDisplayPathProperty ...

ya se puede utilizar como si fueran la propiedad DisplayMemberPath incorporado como esto:

<ListView ItemsSource="{Binding IssueList}"> 
    <ListView.ItemTemplate> 
     <DataTemplate> 
      <myUC:Control1 LabelDisplayPath = "{Binding Path=Issue.Code}" 
          MetricDisplayPath = "{Binding Path=Issue.Priority}" 
          TitleDisplayPath = "{Binding Path=Issue.Description}" 
          SubtitleDisplayPath = "{Binding Path=Issue.Reporter}" /> 
     </DataTemplate> 
    </ListView.ItemTemplate> 
</ListView> 
0

creo que la mejor opción es utilizar plantillas de datos.
Si desea hacerlo flexible, defina una plantilla de datos predeterminada en su biblioteca de recursos (puede hacerlo simplemente sin definir una x: clave o x: nombre para el elemento). Cree una plantilla de datos para cada clase diferente que desea mostrar. Después, simplemente se unen/llenar su lista con las clases y que se mostrarán usando la plantilla de datos por defecto :)

+1

Esto parece violar el principio DRY (No repetir). No quiero tener una apariencia diferente para cada situación, solo información diferente que se muestra dentro de la plantilla existente. Cualquier cambio en la apariencia del control del usuario debería repetirse de manera idéntica en cada (cada) plantilla. – Bevan

1

me gustaría utilizar ViewModels como envolturas en este caso, para unificar los dos casos: Se puede crear un (resumen) ItemViewModelBase clase que define las propiedades Label, Title, Subtitle y Metric. A continuación, crea clases concretas derivadas de esta máquina virtual básica para todos los elementos que desea mostrar con el mismo control. Cada una de estas clases devuelve algo diferente en las propiedades.
De esta manera, se puede definir una DataTemplate para la clase ItemViewModelBase, y se aplicará a ambos tipos de elementos.

Algunos código puede hacer esto más claro:

public abstract class ItemViewModelBase 
{ 
    public abstract string Label { get; } 
    public abstract string Title { get; } 
    public abstract string Subtitle { get; } 
    public abstract string Metric { get; } 
} 

public class IssueViewModel : ItemViewModelBase 
{ 
    private Issue _issue; 

    public override string Label { get { return _issue.Code; } } 
    public override string Title { get { return _issue.Description; } } 
    public override string Subtitle { get { return _issue.Reporter; } } 
    public override string Metric { get { return _issue.Priority; } } 

    public IssueViewModel(Issue issue) 
    { 
     _issue = issue; 
    } 
} 

public class PostViewModel : ItemViewModelBase 
{ 
    private Post _post; 

    public override string Label { get { return _post.PostedOn.ToString(); } } 
    public override string Title { get { return _post.Title; } } 
    public override string Subtitle { get { return _post.Teaser; } } 
    public override string Metric { get { return _post.CommentCount.ToString(); } } 

    public PostViewModel(Issue post) 
    { 
     _post= post; 
    } 
} 

En el cuadro de lista, a continuación, mostrar una colección de ItemViewModelBase casos, en lugar de Issue y Post casos, que se representa mediante uno común DataTemplate, como esto:

<DataTemplate DataType="{x:Type local:ItemViewModelBase}"> 
    <StackPanel> 
     <TextBlock x:Name="txtLabel" Text="{Binding Label}"/> 
     <TextBlock x:Name="txtTitle" Text="{Binding Title}"/> 
     <TextBlock x:Name="txtSubtitle" Text="{Binding Subtitle}"/> 
     <TextBlock x:Name="txtMetric" Text="{Binding Metric}"/> 
    </StackPanel> 
</DataTemplate> 

Por supuesto, su DataTemplate se verá de manera diferente, lo anterior es j use un ejemplo para demostrar el principio. Además, puede definir otros tipos de devolución para las propiedades: las hice todas, aunque tiene DateTime y int en sus clases de datos. Una cosa más que no haría en el código de producción: DateTime.ToString(), como lo hice en PostViewModel.Label - es mejor reemplazarlo con alguna representación de cadena localizada.

+0

Podría usar una variación de esto, reteniendo el 'Issue',' Post' individual y otras clases, envolviendo cada uno en un contenedor personalizado como un adaptador. Vale la pena seguir, gracias. – Bevan

+0

Sí, eso es exactamente lo que propuse. Solo que lo llames envoltorio/adaptador y lo llamo ViewModel. ;) – gehho

Cuestiones relacionadas