C# 绑定到UserControl中ComboBox的SelectedItem

C# 绑定到UserControl中ComboBox的SelectedItem,c#,wpf,xaml,data-binding,wpf-controls,C#,Wpf,Xaml,Data Binding,Wpf Controls,我有一个UserControl,它由一个带有标签的组合框组成。我希望用这个组合框的实例更新屏幕,并根据SelectedItem值在StackPanel中动态创建UserControl 我当前有一个屏幕,其中包含此组合框的一个实例,并通过以下方式将其绑定: 伪代码示例(删除不相关的代码): 有几种方法可以做到这一点。这里有一条路。这不一定是最好的方式,但很容易理解 首先,用户控制xaml。注意用户控件上ItemsSource属性的绑定,它将MyComboxItems指定为items源。更多关于

我有一个UserControl,它由一个带有标签的组合框组成。我希望用这个组合框的实例更新屏幕,并根据SelectedItem值在StackPanel中动态创建UserControl

我当前有一个屏幕,其中包含此组合框的一个实例,并通过以下方式将其绑定:

伪代码示例(删除不相关的代码):



有几种方法可以做到这一点。这里有一条路。这不一定是最好的方式,但很容易理解

首先,用户控制xaml。注意用户控件上ItemsSource属性的绑定,它将MyComboxItems指定为items源。更多关于这一点的来源

 <UserControl x:Class="WpfApp1.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
        <Grid>
            <ComboBox Height="Auto" ItemsSource="{Binding MyComboBoxItems}" SelectionChanged="OnSelectionChanged">
               <ComboBox.ItemTemplate>
                   <DataTemplate>
                       <TextBlock Text="{Binding Text}"/>
                   </DataTemplate>
               </ComboBox.ItemTemplate>
           </ComboBox>
        </Grid>
    </UserControl>
现在,我们在MainWindow.xaml.cs中实现INotifyPropertyChanged,以便WPF绑定引擎在更改属性时更新UI。这是事件处理程序和助手方法OnPropertyChanged

我们还修改了组合框初始值设定项,为Color属性添加一个值。为了好玩,我们将留白

然后,我们添加一个新的ObservableCollect,“ActiveUserControls”来存储在组合框selection changed事件中收到的MyComboxItem。我们这样做,而不是在代码中动态创建用户控件

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public List<MyComboBoxItem> MyComboBoxItems { get; set; } = new List<MyComboBoxItem>()
    {
        new MyComboBoxItem() {Text = "Item1", Color = "Red"},
        new MyComboBoxItem() {Text = "Item2", Color = "Green"},
        new MyComboBoxItem() {Text = "Item3"},
    };

    private ObservableCollection<MyComboBoxItem> _activeUserControls;
    public ObservableCollection<MyComboBoxItem> ActiveUserControls
    {
        get => _activeUserControls;
        set { _activeUserControls = value; OnPropertyChanged(); }
    }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void OnSelectionChanged(object sender, MyComboBoxSelectionChangedEventArgs e)
    {
        if (e.MyComboBoxItem is MyComboBoxItem item)
        {
            if (ActiveUserControls == null)
            {
                ActiveUserControls = new ObservableCollection<MyComboBoxItem>();
            }

            ActiveUserControls.Add(item);
        }
    }
}
让我们看看如何绑定到MainWindow.xaml中的内容:

<local:MyUserControl Width="140"
                         Height="32"
                         Grid.Row="0"
                         MyComboBoxSelectionChanged="OnSelectionChanged"
                         ItemsSource="{Binding MyComboBoxItems}"
                         ItemTemplate="{StaticResource ComboBoxItemDataTemplate}" />
<Window.Resources>
    <local:UserControlDataTemplateSelector x:Key="UserControlDataTemplateSelector" />
...
我们在MainWindow.xaml中的xaml中创建数据模板选择器的实例:

<local:MyUserControl Width="140"
                         Height="32"
                         Grid.Row="0"
                         MyComboBoxSelectionChanged="OnSelectionChanged"
                         ItemsSource="{Binding MyComboBoxItems}"
                         ItemTemplate="{StaticResource ComboBoxItemDataTemplate}" />
<Window.Resources>
    <local:UserControlDataTemplateSelector x:Key="UserControlDataTemplateSelector" />
...

...
最后,我们将堆栈面板替换为Items控件:

   <ItemsControl Grid.Row="1"
                  x:Name="MyUserControls"
                  ItemsSource="{Binding ActiveUserControls}"
                  ItemTemplateSelector="{StaticResource UserControlDataTemplateSelector}" />


ItemsSource=“{Binding MyBoxItems}”
应该是
ItemsSource=“{Binding MyBoxItems,RelativeSource={RelativeSource AncestorType=UserControl}}”
。不要显式设置UserControl的DataContext。哇,谢谢。我最后也做了类似的事情,非常感激。很高兴你发现了这一点!
<UserControl x:Class="WpfApp1.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:WpfApp1"
         mc:Ignorable="d"
         d:DesignHeight="450"
         d:DesignWidth="800">
    <Grid>
        <ComboBox Height="Auto"
              ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyUserControl}}}"
              ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyUserControl}}}"
              SelectionChanged="OnSelectionChanged">

        </ComboBox>
    </Grid>
</UserControl>
public partial class MyUserControl : UserControl
{
    public event MyComboBoxSelectionChangedEventHandler MyComboBoxSelectionChanged;
    public MyUserControl()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource",
            typeof(System.Collections.IEnumerable),
            typeof(MyUserControl),
            new PropertyMetadata(null));

    public System.Collections.IEnumerable ItemsSource
    {
        get => GetValue(ItemsSourceProperty) as IEnumerable;
        set => SetValue(ItemsSourceProperty, (IEnumerable)value);
    }

    public static readonly DependencyProperty ItemTemplateProperty =
        DependencyProperty.Register("ItemTemplate",
            typeof(DataTemplate),
            typeof(MyUserControl),
            new PropertyMetadata(null));

    public DataTemplate ItemTemplate
    {
        get => GetValue(ItemTemplateProperty) as DataTemplate;
        set => SetValue(ItemTemplateProperty, (DataTemplate)value);
    }

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {

        if (e.AddedItems.Count > 0)
        {
            MyComboBoxSelectionChanged?.Invoke(this,
                new MyComboBoxSelectionChangedEventArgs() {MyComboBoxItem = e.AddedItems[0]});
        }
    }
}
<local:MyUserControl Width="140"
                         Height="32"
                         Grid.Row="0"
                         MyComboBoxSelectionChanged="OnSelectionChanged"
                         ItemsSource="{Binding MyComboBoxItems}"
                         ItemTemplate="{StaticResource ComboBoxItemDataTemplate}" />
    <DataTemplate x:Key="ComboBoxItemDataTemplate"
                  DataType="local:MyComboBoxItem">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="4"
                       Text="{Binding Text}" />
            <TextBlock Margin="4"
                       Text="{Binding Color}" />
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="GreenUserControlDataTemplate"
                  DataType="local:MyComboBoxItem">
        <local:GreenUserControl DataContext="{Binding}" />
    </DataTemplate>

    <DataTemplate x:Key="RedUserControlDataTemplate"
                  DataType="local:MyComboBoxItem">
        <local:RedUserControl DataContext="{Binding}" />
    </DataTemplate>

    <DataTemplate x:Key="UnspecifiedUserControlDataTemplate"
                  DataType="local:MyComboBoxItem">
        <TextBlock Margin="4"
                   Text="{Binding Text}" />
    </DataTemplate>
<UserControl x:Class="WpfApp1.RedUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:WpfApp1"
             mc:Ignorable="d"
             d:DesignHeight="450"
             d:DesignWidth="800">
    <Grid Background="LightGray"
          Margin="2">
        <TextBlock Margin="4"
                   Foreground="DarkRed"
                   TextWrapping="Wrap"
                   Text="{Binding Text}"
                   FontSize="24"
                   FontWeight="Bold" />
    </Grid>
</UserControl>
public class UserControlDataTemplateSelector : DataTemplateSelector
{

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (container is FrameworkElement fe)
        {
            if (item is MyComboBoxItem cbItem)
            {
                if (cbItem.Color == "Red")
                {
                    return fe.FindResource("RedUserControlDataTemplate") as DataTemplate;
                }
                if (cbItem.Color == "Green")
                {
                    return fe.FindResource("GreenUserControlDataTemplate") as DataTemplate;
                }
                return fe.FindResource("UnspecifiedUserControlDataTemplate") as DataTemplate;
            }
        }
        return null;
    }
}
<Window.Resources>
    <local:UserControlDataTemplateSelector x:Key="UserControlDataTemplateSelector" />
...
   <ItemsControl Grid.Row="1"
                  x:Name="MyUserControls"
                  ItemsSource="{Binding ActiveUserControls}"
                  ItemTemplateSelector="{StaticResource UserControlDataTemplateSelector}" />