【问题标题】:Wpf binding collection property in UserControl (xaml)UserControl (xaml) 中的 Wpf 绑定集合属性
【发布时间】:2017-08-18 09:15:57
【问题描述】:

在我的用户控件(选项)中添加按钮集合。 在 xaml dissigner 中显示。

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

  • 如果 Options 未初始化,则报错 XamlObjectWriterException: Property collection "WpfAppUserControl.Buttons"."Options" (null)。
  • 如果 Options = new List(),则 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);
            }
        }
    }
}

【问题讨论】:

    标签: c# wpf xaml user-controls


    【解决方案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>
    

    然后创建一个视图模型,其中包含一组具有按钮内容和命令属性的数据项:

    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;
    }
    

    【讨论】:

      【解决方案2】:

      首先用空的 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 评论说“从不将集合类型 DP 的默认值设置为 null 以外的任何值。否则,UserControl 类的所有实例都将在同一个默认集合实例上运行。 "任何引用类型都是如此。所以属性初始化是在构造函数中完成的。

      你可以不用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,因为集合属性本身没有更改,它是相同的引用

      【讨论】:

      • 从不将集合类型依赖属性的默认值设置为 null 以外的任何值。否则,UserControl 类的所有实例都将在同一个默认集合实例上运行。
      • @Clemens,感谢您及时的建议。我没有完全意识到使用引用类型实例的后果。固定的。我会把它留在这里,因为有人可能会遇到同样的障碍。我很高兴我在沙盒环境中学会了它:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 2016-11-05
      • 2014-08-31
      相关资源
      最近更新 更多