2010-05-05 17 views
5

Sto scrivendo un controllo utente WPF per la mia applicazione, inserendo un ListBox e alcuni altri elementi.Come implementare DisplayMemberPath per il mio UserControl Wpf?

Il ListBox ha un nuovo ItemTemplate che presenta quattro informazioni per ciascun elemento nel mio elenco. Riesco a scrivere a chiave ciascuno dei quattro collegamenti su proprietà specifiche delle mie voci di elenco e vengono visualizzati correttamente.

Tuttavia, voglio che il mio UserControl sia un po 'più flessibile.

Su ListBox e ComboBox c'è una struttura DisplayMemberPath (ereditato da ItemsControl) che sembra "iniettare" la proprietà appropriata vincolante in ItemTemplate standard.

Come ottengo lo stesso risultato con il controllo utente?

mi piacerebbe istituire quattro nuove proprietà per consentire la configurazione delle informazioni visualizzate:

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

Revisione ItemsControl.DisplayMemberPath con riflettore sembra andare nella tana del coniglio, non sono stato in grado capire come funziona

Inoltre, se sono completamente fuori rotta - e c'è un'altra, più tecnica "WPF" che dovrei usare, per favore, indicarmi in quella direzione.

Aggiornamento

Ecco un chiarimento di quello che sto cercando di realizzare.

Il ListBox all'interno del mio controllo display utente quattro informazioni per articolo: Etichetta, titolo, sottotitolo e metriche

In un unico luogo, voglio usare questo controllo utente per visualizzare un elenco dei problemi. Ogni numero è simile al seguente:

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

Quando si visualizzano le questioni, voglio usare le seguenti mappature:

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

Altrove nella stessa applicazione, ho una lista di messaggi che voglio visualizzare utilizzando lo stesso UserControl. Ogni post è simile al seguente:

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

Quando si visualizzano i messaggi, voglio usare le seguenti mappature:

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

Considerato che gli aspetti ei messaggi sono piuttosto differenti astrazioni, non voglio che per costringerli avere le stesse proprietà, solo per consentire l'utilizzo di UserControl invariato. Invece, voglio introdurre una piccola configurabilità in modo da poter riutilizzare il mio UserControl in entrambi i siti in modo pulito.

+1

Non capisco cosa stai cercando di fare. Con "hardcoding" i tuoi binding, stai ancora ottenendo flessibilità dal semplice fatto che stai usando il databinding, giusto? Voglio dire, potresti legare a qualsiasi DependencyProperty e cambiare quel valore nel code-behind. Puoi pubblicare un campione dello XAML che stai utilizzando e mettere in risalto ciò di cui non sei soddisfatto? – Dave

risposta

2

Ho trovato la soluzione al mio problema - è un po 'più complicato di quanto mi piacerebbe ...

Su qualsiasi ItemsControl, quando si imposta la proprietà DisplayMemberPath, un'istanza della classe interna è DisplayMemberTemplateSelector assegnato allo ItemTemplateSelector.In effetti, questa classe genera un DataTemplate personalizzato con un'associazione appropriata per soddisfare la configurazione corrente di DisplayMemberPath. Una nuova istanza di questa classe viene creata e utilizzata ogni volta che viene modificato DisplayMemberPath.

Per replicare questo sul mio Usercontrol, invece di modificare il mio modello di dati esistente al volo come avevo originariamente inteso, ho bisogno di creare la mia classe helper selettore modello e imitare lo stesso approccio di ItemsControl.

+0

Lo so molto tempo fa, ma hai l'aiuto disponibile da qualche parte? –

+0

Purtroppo non ho più accesso a quel codice: è stato scritto per un precedente datore di lavoro. – Bevan

0

Dopo un sacco di suggerimenti da l'aggiornamento e commentare, penso che si può ottenere che l'utilizzo di DependencyProperty

Lasciate che vi dia il contorno dell'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> 

dove DisplayPath sono le proprietà di dipendenza

ora creare queste proprietà di dipendenza nel codice UserControl dietro: LabelDisplayPathProperty, MetricDisplayPathProperty ...

Ora li potete usare come la proprietà DisplayMemberPath insito in questo modo:

<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

penso che la soluzione migliore è quella di utilizzare modelli di dati.
Se si desidera renderlo flessibile, quindi definire un modello di dati predefinito nella libreria delle risorse (è possibile farlo semplicemente non definendo una x: Key o x: Name per l'elemento). Crea un modello di dati per ogni classe diversa che desideri visualizzare. Poi basta legano/riempire la vostra lista con le classi e verranno visualizzati utilizzando il modello di dati di default :)

+1

Questo sembra violare il principio ASCIUTTO (non ripetersi). Non voglio avere un aspetto diverso per ogni situazione, solo diverse informazioni mostrate all'interno del modello esistente. Eventuali modifiche al look and feel del controllo utente dovrebbero essere ripetute identicamente su ciascun modello (ogni). – Bevan

1

userei ViewModels come involucri in questo caso di unificare i due casi: è possibile creare un (abstract) ItemViewModelBase classe che definisce le proprietà Label, Title, Subtitle e Metric. Quindi si creano classi concrete derivanti da questa VM di base per tutti gli elementi che si desidera visualizzare utilizzando lo stesso controllo. Ognuna di queste classi restituisce qualcosa di diverso nelle proprietà.
In questo modo, è possibile definire una DataTemplate per la classe ItemViewModelBase, e sarà applicato a entrambi i tipi di elemento .

Alcuni codice può rendere questo più chiaro:

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

Nel vostro ListBox sarà quindi visualizzare una collezione di ItemViewModelBase casi, piuttosto che Issue e Post casi, che sarà reso utilizzando uno comune DataTemplate, come questo:

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

Naturalmente, il vostro DataTemplate guarderà in modo diverso, quanto sopra è j è un esempio per dimostrare il principio. Inoltre, potresti definire altri tipi di restituzione per le proprietà: li ho creati tutti, sebbene tu abbia un DateTime e uno int nelle tue classi di dati. Un'altra cosa che non farei nel codice di produzione: DateTime.ToString(), come ho fatto in PostViewModel.Label - meglio sostituirlo con una rappresentazione di stringa localizzata.

+0

Potrei usare una variazione di questo - mantenendo il singolo 'Problema',' Post' e altre classi, avvolgendoli ciascuno in un wrapper personalizzato come un adattatore. Vale la pena seguire - grazie. – Bevan

+0

Sì, è esattamente quello che ho proposto. Solo che lo chiami wrapper/adapter e lo chiamo ViewModel. ;) – gehho

Problemi correlati