使用UserControl列表在ComboBox中绑定WPF

时间:2014-06-03 06:17:11

标签: c# wpf xaml combobox user-controls

在两个组合框A和B中。 A的ItemsSource是自定义列表。和B的ItemsSource是UserControl列表。 手动设置SelectedItem时,组合框效果很好,但B组合框UI不显示所选项。 (在调试中,SelectedItem的值映射是正确的,但组合框B的UI不会被更改。) 所有其他结构在A和B之间是相同的。这是什么原因?

MainWindow.xaml

 ...
<ComboBox ItemsSource="{Binding FruitList}" SelectedItem="{Binding SelectedFruit}"
  DisplayMemberPath="FruitName"  />
<Button Content="Button" HorizontalAlignment="Left"  
VerticalAlignment="Top" Width="75" Click="Button_Click"/>

<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" DisplayMemberPath="ItemName"  />
    <Button Content="Button" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="75" Click="Button_Click2"/>
</Grid>

MainWindow.xaml.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;

        FruitList.Add(f1);
        FruitList.Add(f2);
        FruitList.Add(f3);

        UserControlList.Add(u1);
        UserControlList.Add(u2);
        UserControlList.Add(u3);

    }

    Fruit f1 = new Fruit { FruitName = "Apple" };
    Fruit f2 = new Fruit { FruitName = "Banana" };
    Fruit f3 = new Fruit { FruitName = "Lemon" };

    MyUserControl u1 = new MyUserControl { ItemName = "Apple" };
    MyUserControl u2 = new MyUserControl { ItemName = "Banana" };
    MyUserControl u3 = new MyUserControl { ItemName = "Lemon" };

    ObservableCollection<Fruit> _FruitList = new ObservableCollection<Fruit>();
    public ObservableCollection<Fruit> FruitList
    {
        get { return _FruitList; }
        set
        {
            _FruitList = value;
            OnPropertyChanged();
        }
    }

    Fruit _SelectedFruit;
    public Fruit SelectedFruit
    {
        get { return _SelectedFruit; }
        set
        {
            _SelectedFruit = value;
            OnPropertyChanged();
        }
    }



    ObservableCollection<MyUserControl> _UserControlList = new ObservableCollection<MyUserControl>();
    public ObservableCollection<MyUserControl> UserControlList
    {
        get
        {
            return _UserControlList;
        }
        set
        {
            _UserControlList = value;
            OnPropertyChanged();
        }
    }
    MyUserControl _SelectedUserControl;
    public MyUserControl SelectedUserControl
    {
        get { return _SelectedUserControl; }
        set
        {
            _SelectedUserControl = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged([CallerMemberName] string caller = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    }


    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.SelectedFruit = f3;
    }

    private void Button_Click2(object sender, RoutedEventArgs e)
    {
        this.SelectedUserControl = u3;
    }
}

public class Fruit
{
    public string FruitName { get; set; }
}

}

用户控件

public partial class MyUserControl : UserControl
{
    public MyUserControl()
    {
        InitializeComponent();

    }

    public string ItemName { get; set; }
}

2 个答案:

答案 0 :(得分:1)

这不是实现这一目标的好方法。最好为组合框定义ItemTemplate,使其具有UserControl,如:

  <ComboBox ItemsSource="{Binding ItemList}" SelectedItem="{Binding SelectedItem}" >
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <myControls:MyUserControl/>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

并定义类Item

public class Item
{
    public string ItemName { get; set; }
}

ObservableCollection<Item> _ItemsList = new ObservableCollection<Item>();
public ObservableCollection<Item> ItemsList 
{
    get
    {
        return _ItemsList ;
    }
    set
    {
        _ItemsList = value;
        OnPropertyChanged();
    }
}

这里UserControl的DataContext将是Item对象。您可以在用户控件中绑定ItemName,以便以任何方式显示它。

在您的用户控件中,您可以:

 <TextBlock Text="{Binding ItemName}"></TextBlock>

答案 1 :(得分:1)

既然你问过“是什么原因?”:

第二个组合框没有显示任何选择的原因是ComboBox特别处理了ContentControl类型的项目。在只读选择框中,不是用于显示值的ContentControl,而是ContentControl内容。由于UserControlContentControlUserControl的内容会显示在选择框内,因此您丢失了UserControl的数据上下文;最后,即使SelectedItem包含对仍具有有效数据上下文的UserControl的引用,也会显示空字符串。 (据我所知,这种行为没有记录;但你可以通过检查http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/ComboBox.cs上的ComboBox代码,特别是UpdateSelectionBoxItem()方法,看到它的工作原理如下。

通过在第二个ComboBox上设置IsEditable="True",您可以看到如果组合框没有只读选择框,一切正常。

因此,您通常应该避免将UI元素添加到组合框中,尤其是在使用DisplayMemberPath属性时,即如果您不想实际显示UI元素。

在@nit的答案中描述了显示具有非标准外观(例如,使用UserControls)的ComboBox项目的推荐方法。

但是,如果您坚持将UserControl项目列表传递给ComboBox,则可以删除DisplayMemberPath并使用以下内容:

<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" >
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ItemName}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

此外,在UserControl的构造函数中,您必须放置以下行:

((FrameworkElement) Content).DataContext = this;

这是确保只读选择框中只有包含用户控件内容的正确数据上下文所必需的,而不是用户控件本身。

请注意,使用上面的示例,下拉列表仅包含文本(即项目名称),但选择框将包含完全呈现的用户控件。