2010-05-05 73 views
5

我正在爲我的應用程序編寫一個WPF用戶控件,包裝一個ListBox和一些其他項目。如何爲我的Wpf UserControl實現DisplayMemberPath?

ListBox有一個新的ItemTemplate,它爲我的列表中的每個項目提供四條信息。我可以將四個綁定中的每一個硬編碼到我的列表項上的特定屬性,並且它們顯示正常。

但是,我希望我的UserControl更靈活一點。

ListBoxComboBox有一個屬性DisplayMemberPath(繼承自ItemsControl),似乎將相應的屬性綁定「注入」到標準ItemTemplate中。

如何通過我的用戶控制實現相同的結果?

我想成立四個新的屬性,讓顯示的信息配置:回顧與ItemsControl.DisplayMemberPath反射

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

似乎在下降兔子洞,我一直沒能瞭解它是如何工作的。另外,如果我完全偏離了課程 - 並且還有另外一種更應該使用的「WPF」技術,請指出我的方向。

更新

這裏有一個澄清什麼,我想要的目的。

列表框我的用戶控件顯示四個信息每個項目中:標籤,標題,副標題和公制

在一個地方,我想用這個用戶控件來顯示的問題的列表。每一個問題是這樣的:

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

當顯示的問題,我想用下面的映射:

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

別處在同一個應用程序,我的職位名單,我想用顯示相同的UserControl。每個帖子是這樣的:

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

當顯示的帖子,我想用下面的映射:

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

考慮到的問題和文章是完全不同的抽象,我不想強​​迫他們具有相同的屬性,只是爲了使用戶控件能夠保持不變。相反,我想介紹一些可配置性,以便我可以在兩個站點中乾淨地重用我的UserControl。

+1

我不太明白你想要做什麼。通過對你的綁定進行「硬編碼」,你僅僅依靠數據綁定這一事實來獲得靈活性,對嗎?我的意思是,您可以綁定到任何DependencyProperty並在代碼隱藏中更改該值。你可以發佈你正在使用的XAML樣本,並突出顯示你不滿意的是什麼? – Dave 2010-05-05 03:48:55

回答

2

我已經找到了解決我的問題 - 這是一個比較複雜一點比我想...

在任何的ItemsControl,當你設置DisplayMemberPath屬性,內部類的一個實例DisplayMemberTemplateSelector是分配給ItemTemplateSelector。實際上,此類使用適當的綁定生成自定義DataTemplate以適應DisplayMemberPath的當前配置。每當DisplayMemberPath發生更改時,都會創建並使用此類的新實例。

要在我自己的UserControl上覆制此代碼,而不是像我原本想要的那樣即時修改現有的數據模板,我需要創建自己的模板選擇器幫助器類並模仿與ItemsControl相同的方法。

+0

我很久以前就知道這個,但是你有幫助器在某處嗎? – 2016-10-10 04:08:31

+0

不幸的是,我不再有權訪問該代碼 - 它是爲以前的僱主編寫的。 – Bevan 2016-10-11 03:51:03

0

很多從您的更新和評論的提示後,我想你可以做到這一點使用的DependencyProperty

讓我給你的想法

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> 

其中DisplayPath是依賴項屬性

立即創建在用戶控件的代碼,這些依賴屬性背後:LabelDisplayPathProperty,MetricDisplayPathProperty ...

現在你可以使用他們喜歡這樣的內置的DisplayMemberPath屬性:

<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

我認爲最好的方法是使用數據模板。
如果您想使其更靈活,那麼在您的資源庫中定義一個默認數據模板(您可以通過簡單地不定義該項目的x:Key或x:Name來完成此操作)。爲您希望顯示的每個不同類創建一個數據模板。然後,只需綁定/帶班填滿你的名單,他們將使用默認的數據模板:)

+1

這似乎違反了DRY原則(不要重複自己)。我不希望每種情況都有不同的外觀和感覺,只是現有模板中顯示的信息不同。對用戶控件的外觀和感覺的任何更改都必須在每個(每個)模板上重複相同。 – Bevan 2010-05-05 06:02:35

1

顯示我會用的ViewModels如包裝在這種情況下,統一兩種情況:您可以創建一個(抽象)ItemViewModelBase它定義了屬性Label,Title,SubtitleMetric。然後,使用相同的控件爲所有要顯示的項目創建從此基本VM派生的具體類。這些類中的每一個都會返回屬性中不同的內容。
這樣,您就可以定義一個DataTemplateItemViewModelBase類,它將被應用到兩種項目類型。

某些代碼會說明清楚:

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

在你的列表框然後你會顯示ItemViewModelBase實例,而不是IssuePost實例的集合,這將使用一個共同DataTemplate呈現,像這樣的:

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

當然,你的DataTemplate看起來不同,上面爲j用一個例子來說明原則。此外,您可以爲屬性定義其他返回類型 - 我將它們全部設置爲字符串,儘管您的數據類中有DateTimeint。還有一件事我不會在生產代碼中做:DateTime.ToString(),正如我在PostViewModel.Label中所做的那樣 - 更好地用一些本地化的字符串表示替換它。

+0

我可以使用這個變體 - 保留單個'Issue','Post'和其他類,將每個包裝在一個自定義包裝中作爲一個適配器。值得跟進 - 謝謝。 – Bevan 2010-05-05 09:12:32

+0

是的,這正是我所提出的。只是你稱它爲包裝器/適配器,我把它稱爲ViewModel。 ;) – gehho 2010-05-05 11:14:38