2010-05-05 12 views
5

Ich schreibe ein WPF-Benutzersteuerelement für meine Anwendung, umschließt eine ListBox und einige andere Elemente.Wie implementiert DisplayMemberPath für meine Wpf UserControl?

Die ListBox hat eine neue ItemTemplate, die vier Informationen für jeden Eintrag in meiner Liste enthält. Ich kann jede der vier Bindungen mit bestimmten Eigenschaften auf meinen Listenelementen fest codieren und sie werden gut angezeigt.

Allerdings möchte ich meine UserControl ein bisschen flexibler sein.

Auf ListBox und ComboBox gibt es eine Eigenschaft DisplayMemberPath (von ItemsControl geerbt), die die entsprechende Eigenschaft Bindung in das Standard ItemTemplate zu "injizieren" scheint.

Wie erreiche ich das gleiche Ergebnis mit meiner Benutzerkontrolle?

Ich möchte vier neue Eigenschaften einrichten Konfiguration der angezeigten Informationen zu ermöglichen:

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

Überprüfung ItemsControl.DisplayMemberPath mit Reflektor scheint das Kaninchenloch zu gehen, habe ich nicht in der Lage gewesen, zu ergründen, wie es funktioniert.

Auch, wenn ich völlig vom Kurs ab bin - und es gibt eine andere, mehr "WPF" Technik, die ich stattdessen verwenden sollte, bitte zeigen Sie mich in diese Richtung.

aktualisieren

Hier ist eine Klarstellung, was ich zu erreichen bin versucht.

Die List-Box in meiner Benutzersteuerung zeigt vier Informationen pro Artikel: Label, Titel, Untertitel und Metric

In einem Ort, möchte ich diese Benutzerverwaltung verwenden, um eine Liste von Themen angezeigt werden soll. Jede Ausgabe sieht wie folgt aus:

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

Wenn Probleme anzeigt, möchte ich folgende Zuordnungen verwenden:

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

An anderer Stelle in der gleichen Anwendung, habe ich eine Liste der Beiträge, die ich will mit angezeigt werden das gleiche UserControl. Jeder Beitrag sieht wie folgt aus:

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

Wenn Beiträge anzuzeigen, möchte ich folgende Zuordnungen verwenden:

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

Da Probleme und Beiträge sind ganz andere Abstraktionen, ich will nicht, sie zu zwingen, die gleichen Eigenschaften zu haben, nur um das UserControl unverändert zu lassen. Stattdessen möchte ich ein wenig Konfigurierbarkeit einführen, damit ich mein UserControl in beiden Seiten sauber wiederverwenden kann.

+1

Ich verstehe nicht ganz, was Sie versuchen zu tun. Durch "Hardcoding" Ihrer Bindungen erhalten Sie immer noch Flexibilität durch die Tatsache, dass Sie Databinding verwenden, richtig? Ich meine, Sie könnten an jede DependencyProperty binden und diesen Wert im Code-Behind ändern. Kannst du ein Beispiel für das von dir verwendete XAML posten und hervorheben, worüber du unglücklich bist? – Dave

Antwort

2

ich die Lösung für mein Problem gefunden habe - es ist ein bisschen komplizierter ist als Ich mag würde ...

Auf jedem Item, wenn Sie die DisplayMemberPath Eigenschaft, eine Instanz der inneren Klasse DisplayMemberTemplateSelector ist zugeordnet zu ItemTemplateSelector.Im Endeffekt generiert diese Klasse ein benutzerdefiniertes DataTemplate mit einer entsprechenden Bindung, die der aktuellen Konfiguration von DisplayMemberPath entspricht. Eine neue Instanz dieser Klasse wird jedes Mal erstellt und verwendet, wenn DisplayMemberPath geändert wird.

Um dies auf meiner eigenen Benutzerverwaltung zu replizieren, anstatt meine vorhandene Datenvorlage im laufenden Betrieb wie ursprünglich geplant zu ändern, muss ich meine eigene Template-Selektor-Hilfsklasse erstellen und den gleichen Ansatz wie ItemsControl nachahmen.

+0

Ich kenne das schon lange her, aber hast du den Helfer irgendwo verfügbar? –

+0

Leider habe ich keinen Zugang mehr zu diesem Code - er wurde für einen früheren Arbeitgeber geschrieben. – Bevan

0

Nach vielen Hinweise von Ihrem Update und Kommentar, ich glaube, dass mit DependencyProperty erreichen kann mir

Lassen Sie den Umriss der Idee geben

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> 

Dabei sind DisplayPaths die Abhängigkeitseigenschaften

Jetzt Erstellen Sie diese Abhängigkeitseigenschaften in dem Usercontrol-Code hinter:

<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

Ich denke, die beste Wahl zu verwenden ist: LabelDisplayPathProperty, MetricDisplayPathProperty ...

Jetzt sie wie die eingebaute DisplayMemberPath Eigenschaft wie folgt Sie verwenden können, Datenvorlagen.
Wenn Sie es flexibel machen möchten, dann definieren Sie eine Standarddatenvorlage in Ihrer Ressourcenbibliothek (Sie können dies tun, indem Sie einfach kein x: Schlüssel oder x: Name für das Element definieren). Erstellen Sie eine Datenvorlage für jede andere Klasse, die Sie anzeigen möchten. Dann einfach binden/füllen Sie Ihre Liste mit Klassen und sie werden die Standarddatenvorlage werden angezeigt unter Verwendung :)

+1

Dies scheint das DRY-Prinzip zu verletzen (Do not Repeat Yourself). Ich möchte nicht für jede Situation ein anderes Aussehen und Gefühl haben, sondern nur unterschiedliche Informationen innerhalb der bestehenden Vorlage. Alle Änderungen am Erscheinungsbild des Benutzersteuerelements müssen für jedes (jede) Vorlage identisch wiederholt werden. – Bevan

1

I Viewmodels als Wrapper in diesem Fall verwenden würden die beiden Fälle zu vereinen: Sie könnten eine (abstrakte) erstellen ItemViewModelBase Klasse, die die Eigenschaften Label, Title, Subtitle und Metric definiert. Anschließend erstellen Sie anhand dieser Basis-VM konkrete Klassen für alle Elemente, die Sie mit demselben Steuerelement anzeigen möchten. Jede dieser Klassen gibt in den Eigenschaften etwas anderes zurück.
Auf diese Weise können Sie einDataTemplate für die ItemViewModelBase Klasse definieren, und es wird zu beiden Elementtypen angewendet werden.

können einige Code machen dies deutlicher:

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

In Ihrer List-Box werden Sie dann eine Sammlung von ItemViewModelBase Instanzen angezeigt werden, anstatt Issue und Post Instanzen, die einen gemeinsame DataTemplate mit gerendert werden, wie dies:

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

natürlich Ihre DataTemplate wird anders aussehen, ist die oben j ein Beispiel, um das Prinzip zu demonstrieren. Darüber hinaus könnten Sie andere Rückgabetypen für die Eigenschaften definieren - ich habe sie alle Strings erstellt, obwohl Sie in Ihren Datenklassen einen DateTime und einen int haben. Eine weitere Sache, die ich nicht in Produktionscode tun würde: DateTime.ToString(), wie ich in PostViewModel.Label - besser ersetzen Sie es mit einigen lokalisierten String-Darstellung.

+0

Ich könnte eine Variante davon verwenden - die einzelnen 'Issue'-,' Post'- und andere Klassen behalten, die jeweils in einem benutzerdefinierten Wrapper als Adapter verpackt werden. Es lohnt sich, zu folgen - danke. – Bevan

+0

Ja, genau das habe ich vorgeschlagen. Nur dass Sie es Wrapper/Adapter nennen, und ich nenne es ViewModel. ;) – gehho

Verwandte Themen