2010-05-05 72 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