UserControl中的Wpf绑定集合属性(xaml)

时间:2017-08-18 09:15:58

标签: c# wpf xaml user-controls

在我的userControl(选项)中添加按钮集合。 在xaml disigner中显示。

Output 当我运行我的应用程序时:

  • 如果未初始化选项,则出现错误XamlObjectWriterException:属性集合“WpfAppUserControl.Buttons”。“选项”(null)。
  • 如果选项=新列表(),则window without buttons

MainWindow.xaml

    <Window x:Class="WpfAppUserControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppUserControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="250" Width="300">
    <Grid>
        <local:Buttons x:Name="Buttons"  
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center" 
                       HorizontalContentAlignment="Center">
            <local:Buttons.Options>
                <Button Content="Agudabi 1" Height="20" Margin="2" />
                <Button Content="Agudabi 2" Height="20" Margin="2" />
                <Button Content="Agudabi 3" Height="20" Margin="2" />
            </local:Buttons.Options>
        </local:Buttons>
    </Grid>
</Window>

Buttons.xaml

<UserControl x:Class="WpfAppUserControl.Buttons"
             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:WpfAppUserControl"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <WrapPanel x:Name="InternalContainer"  Orientation="Horizontal" HorizontalAlignment="Center"/>
    </Grid>
</UserControl>

Buttons.xaml.cs

#region Usings

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

#endregion

namespace WpfAppUserControl
{
    public partial class Buttons : UserControl
    {
        public Buttons()
        {
            //Options = new ObservableCollection<Button>();
            InitializeComponent();
        }

        public ObservableCollection<Button> Options
        {
            get { return (ObservableCollection<Button>) GetValue(OptionsProperty); }
            set { SetValue(OptionsProperty, value); }
        }

        public static readonly DependencyProperty OptionsProperty =
            DependencyProperty.Register(nameof(Options), typeof(ObservableCollection<Button>), typeof(Buttons),
                                        new PropertyMetadata(/*new ObservableCollection<Button>()*/, PropertyChangedCallback));

        private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var obj = d as Buttons;

            foreach (var button in obj.Options)
            {
                obj.InternalContainer.Children.Add(button);
            }
        }
    }
}

2 个答案:

答案 0 :(得分:1)

以下是您应该做的事情的一个例子。

使用像这样的ItemsControl而不是具有集合属性的UserControl:

<ItemsControl ItemsSource="{Binding Options}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="{Binding Name}" Command="{Binding Command}"
                    Height="20" Margin="2"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

然后创建一个视图模型,其中包含一组数据项,其中包含Button Content和Command的属性:

public class ViewModel
{
    public ObservableCollection<Option> Options { get; }
        = new ObservableCollection<Option>();
}

public class Option
{
    public string Name { get; set; }
    public ICommand Command { get; set; }
}

初始化它,如下所示,为简洁起见,省略了ICommand实现。在网上搜索RelayCommand以获取实现细节。

public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel();
    vm.Options.Add(new Option { Name = "Agudabi 1" });
    vm.Options.Add(new Option { Name = "Agudabi 2" });
    vm.Options.Add(new Option { Name = "Agudabi 3" });

    DataContext = vm;
}

答案 1 :(得分:0)

首先用空的ObservableCollection初始化OptionsProperty

public partial class Buttons : UserControl
{
    public Buttons()
    {
        Options = new ObservableCollection<Button>();

        InitializeComponent();
    }

    public ObservableCollection<Button> Options
    {
        get { return (ObservableCollection<Button>) GetValue(OptionsProperty); }
        set { SetValue(OptionsProperty, value); }
    }

    public static readonly DependencyProperty OptionsProperty =
        DependencyProperty.Register("Options", typeof(ObservableCollection<Button>), typeof(Buttons));    
}

Clemens评论说&#34; 从不将集合类型DP的默认值设置为null以外的任何其他值。否则,UserControl类的所有实例都将在相同的默认集合实例上运行。&#34;任何参考类型都是如此。所以属性初始化是在构造函数中完成的。

您可以不使用PropertyChangedCallback,因为可以使用ItemsControl更有效地展示集合:

<UserControl x:Name="myUC" x:Class="WpfAppUserControl.Buttons"
             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:WpfAppUserControl"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <ItemsControl ItemsSource="{Binding Path=Options, ElementName=myUC}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel x:Name="InternalContainer"  Orientation="Horizontal" HorizontalAlignment="Center"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
  </Grid>
</UserControl>

请注意,当您向集合添加项目时,不会触发PropertyChangedCallback,因为集合属性本身没有更改,它是相同的引用