使组合框ItemsSource通用在UserControl内部以使其可用于多个类型的集合

问题描述:

我正在应用WPF应用程序,并且最近遇到了制作可重复使用的要求User Controls使组合框ItemsSource通用在UserControl内部以使其可用于多个类型的集合

我有两个User ControlsInputUCComboBoxUC。两者分别具有LabelTextBoxLabelComboBox。我已经通过定义所需的依赖属性成功实现了InputUC

我面临的问题是ComboBoxUC。我在我的应用程序中有一个场景,我必须显示CollectionCities,Customers,Salesmen和其他实体在不同的地方。很明显,每个实体将提供不同的属性名称,以DisplayMemberPath,SelectedValuePath,SelectedValue属性和不同类型的Collection作为ItemsSource属性ComboBox

我在互联网上搜索,但没有找到任何相同的解决方案。

我想要的代码是

ComboBox控制ComboBoxUC.xaml

<ComboBox Name="valuesComboBox" 
      Grid.Column="1" 
      ItemsSource="{Binding ComboBoxItems}" 
      DisplayMemberPath="{Binding ComboBoxDisplayMemberPath}" 
      SelectedValuePath="{Binding ComboBoxSelectedValuePath}" 
      SelectedValue="{Binding ComboBoxValue}" 
      IsEnabled="{Binding ComboBoxIsEnabled}" 
      Style="{StaticResource ComboBox-Base}"> 
</ComboBox> 

代码ComboBoxUC背后ComboBoxUC.xaml.cs

public string ComboBoxLabel 
    { 
     get { return (string)GetValue(LabelProperty); } 
     set { SetValue(LabelProperty, value); } 
    } 

    public bool ComboBoxIsRequired 
    { 
     get { return (bool)GetValue(IsRequiredProperty); } 
     set { SetValue(IsRequiredProperty, value); } 
    } 

    public long ComboBoxValue 
    { 
     get { return (long)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    public bool ComboBoxIsEnabled 
    { 
     get { return (bool)GetValue(ValueEnabledProperty); } 
     set { SetValue(ValueEnabledProperty, value); } 
    } 

    public ObservableCollection<CityViewModel> ComboBoxItems 
    { 
     get { return (ObservableCollection<CityViewModel>)GetValue(ValueItems); } 
     set { SetValue(ValueItems, value); } 
    } 

    public string ComboBoxDisplayMemberPath 
    { 
     get { return GetValue(ValueDisplayMemberPath).ToString(); } 
     set { SetValue(ValueDisplayMemberPath, value); } 
    } 

    public string ComboBoxSelectedValuePath 
    { 
     get { return GetValue(ValueSelectedValuePath).ToString(); } 
     set { SetValue(ValueSelectedValuePath, value); } 
    } 

    public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("ComboBoxLabel", typeof(string), 
     typeof(ComboBoxUC), new PropertyMetadata(string.Empty)); 

    public static readonly DependencyProperty IsRequiredProperty = DependencyProperty.Register("ComboBoxIsRequired", typeof(bool), 
      typeof(ComboBoxUC), new PropertyMetadata(false)); 

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("ComboBoxValue", typeof(long), 
      typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

    public static readonly DependencyProperty ValueEnabledProperty = DependencyProperty.Register("ComboBoxIsEnabled", typeof(bool), 
      typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

    public static readonly DependencyProperty ValueItems = DependencyProperty.Register("ComboBoxItems", typeof(ObservableCollection<CityViewModel>), 
      typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

    public static readonly DependencyProperty ValueDisplayMemberPath = DependencyProperty.Register("ComboBoxDisplayMemberPath", typeof(string), 
      typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

    public static readonly DependencyProperty ValueSelectedValuePath = DependencyProperty.Register("ComboBoxSelectedValuePath", typeof(string), 
      typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = true, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

    public ComboBoxUC() 
    { 
     InitializeComponent(); 
    } 

Page控件的代码,我现在用的这个UserControl

<local:ComboBoxUC ComboBoxLabel="City" 
        ComboBoxIsRequired="True" 
        ComboBoxValue="{Binding CustomerViewModel.customer_city_id}" 
        ComboBoxItems="{Binding Cities}" 
        ComboBoxDisplayMemberPath="city_name" 
        ComboBoxSelectedValuePath="city_id" 
        ComboBoxIsEnabled="{Binding Flags.AddOrUpdate}"> 
</local:ComboBoxUC> 

现在我将在我的应用程序的多个地方使用上面的xaml。 可以在每种情况下而变化的事情是:

  • CustomerViewModel.customer_city_id
  • 城市
  • CITY_NAME
  • city_id

我已正确设置在ComboBoxUC.xamlDataContext的和当前代码因为我的UserControl对于一种类型的Collection (CityViewModel)正常工作。我想为其他实体(如CustomerViewModel,SalesmanViewModel等)使用相同的代码,并使用明显不同的属性名称。

我想下面的代码是通用的。

public ObservableCollection<CityViewModel> ComboBoxItems 
{ 
    get { return (ObservableCollection<CityViewModel>)GetValue(ValueItems); } 
    set { SetValue(ValueItems, value); } 
} 

public static readonly DependencyProperty ValueItems = DependencyProperty.Register("ComboBoxItems", typeof(ObservableCollection<CityViewModel>), 
       typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

我已经试过objectCollection很好,但当然object类型没有,我在我的实体的属性。

帮助将被赞赏,因为我被卡住了,并且无法从这一点开始发展。

而不是尝试使用户控件中的集合通过使它们成为泛型来进行强类型化,而应该使它们不那么强类型化;请记住,Combobox的ItemsSource本身就是类型'object'。

我建议你让你的ComboBoxUC公开一个IEnumerable类型的DependencyProperty并将它绑定到ComboBox ItemsSource。然后还公开一个DataTemplate类型的DependencyProperty并将其绑定到ComboBox的ItemTemplate属性。使用用户控件时,可以提供一个简单的DataTemplate来显示所需的属性,而不是使用DisplayMember路径。例如,当你想在ComboboxUC显示市的,你可以这样做:

<local:ComboBoxUC ItemsSource="{Binding Cities}"> 
    <local.ComboBoxUC.ItemTemplate> 
    <DataTemplate> 
     <TextBlock Text="{Binding city_name}"/> 
    </DataTemplate> 
    </local:ComboBoxUC.ItemTemplate> 
</local:ComboBoxUC/> 

然后,我会揭露组合框的的SelectedItem的用户控件的一个DependencyProperty,如果你绝对必须绑定到SelectedValuePath代替SelectedItem的使用ValueConverter。

说实话,感觉这些用户控件有点OTT。如果您所获得的只是标签和样式,则可以通过在资源字典中重新设计控件并将模板应用到要以此方式使用的每个组合框来实现。

+0

我唯一关心的不仅仅是DisplayMemberPath属性。我还必须使用SelectedValuePath和SelectedValue属性。我不能只为每个属性定义一个DataTemplate我猜。如果我在添加用户控件的同时必须在我的Window/Page Xaml中提供这个长项目模板,那么我不妨回过头来分离这个用户控件。 –

+1

DataTemplates(并且,如果您决定这样做,控制模板)可以在资源字典中集中定义,因此只需定义一次即可使用,如下所示: ibebbs

+0

你能提供一个例子吗? –

需要改变的唯一的事情就是@ibebbs指出的ObservableCollectionIEnumerable的类型。

public IEnumerable ComboBoxItems 
{ 
    get { return (IEnumerable)GetValue(ValueItems); } 
    set { SetValue(ValueItems, value); } 
} 

public static readonly DependencyProperty ValueItems = DependencyProperty.Register("ComboBoxItems", typeof(IEnumerable), 
       typeof(ComboBoxUC), new FrameworkPropertyMetadata { BindsTwoWayByDefault = false, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); 

这让我在应用程序和不同类型的Collection,甚至与不同的属性名称使用相同的UserControl

+0

_“我自己弄明白了**,唯一需要改变的是ObservableCollection的类型为IEnumerable **,就像@ibebbs指出的那样**。”_ - 如果我读过一个并且答案是矛盾的临界抄袭 – MickyD

+0

我的目的不是要拿任何信用,而是要清楚地描述在这种情况下解决我的问题的具体事情。就负面投票而言,这是我的错误,我已将其删除。但仍然@ibebbs答案完全没有解决我的问题。我只是回答了解决我的问题的具体事情,我认为这不是抄袭。 –