【问题标题】:How to bind to a property of a UserControl in WPF?如何绑定到 WPF 中 UserControl 的属性?
【发布时间】:2015-04-18 00:00:42
【问题描述】:

我正在尝试绑定到用户控件的属性,但它似乎不起作用。

<Controls:BasicFilter Title="Some title" FilterLists="{Binding LocationFilterLists}"/>

FilterLists 是我要绑定的属性。 Title 属性包含一个文本,该文本显示在我的控件内的标签上,效果很好。

我已经设置了一个基本的 ViewModel,它尝试设置 LocationFilterLists。因为它绑定到我的控件的FilterLists 属性,所以我希望这会将该值设置为该属性,但这不会发生。这是我的 ViewModel 的代码:

public class MainViewModel : DependencyObject
{
 public NamedListList LocationFilterLists
 {
  get { return (NamedListList)GetValue(LocationFilterListsProperty); }
  set { SetValue(LocationFilterListsProperty, value); }
 }
 public static readonly DependencyProperty LocationFilterListsProperty =
     DependencyProperty.Register("LocationFilterLists", typeof(NamedListList), typeof(MainViewModel), new PropertyMetadata(null));

 public MainViewModel()
 {
 }

 internal void Refresh()
 {
  NamedListList nll = new NamedListList();
  NamedList nl1 = new NamedList();
  nl1.Name = "filter1";
  NamedList nl2 = new NamedList();
  nl2.Name = "filter2";
  nll.Add(nl1);
  nll.Add(nl2);
  LocationFilterLists = nll;
 }
}

NamedList 继承自 List&lt;object&gt; 并添加了属性 NameNamedListList 继承自 List&lt;NamedList&gt;。我已将视图设置为在 DataContext 设置为此 ViewModel 后立即调用 Refresh 方法。

这是我的控件的 XAML:

<UserControl x:Class="Plin.CommonDisplayObjects.Controls.BasicFilter"
             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" 
             mc:Ignorable="d">
 <Grid Background="Gray">
  <DockPanel x:Name="dockPanel" Margin="3">
   <Label Content="{Binding Title}" DockPanel.Dock="Top" Margin="0" Padding="5,3,5,3"/>
  </DockPanel>
 </Grid>
</UserControl>

这是我的控件的代码隐藏:

public partial class BasicFilter : UserControl
{
 #region Dependency Properties
 public string Title
 {
  get { return (string)GetValue(TitleProperty); }
  set { SetValue(TitleProperty, value); }
 }
 public static readonly DependencyProperty TitleProperty =
     DependencyProperty.Register("Title", typeof(string), typeof(BasicFilter), new PropertyMetadata(""));

 public NamedListList FilterLists
 {
  get { return (NamedListList)GetValue(FilterListsProperty); }
  set
  {
   SetValue(FilterListsProperty, value);
   RegenerateFilter(); // Every time property is set, new buttons should be generated.
  }
 }
 public static readonly DependencyProperty FilterListsProperty =
     DependencyProperty.Register("FilterLists", typeof(NamedListList), typeof(BasicFilter), new PropertyMetadata(null));
 #endregion

 public BasicFilter()
 {
  InitializeComponent();
  this.DataContext = this;
 }

 private void RegenerateFilter()
 {
  dockPanel.Children.Clear();
  if (FilterLists != null)
  {
   foreach (NamedList nl in FilterLists)
   {
    Button b = new Button();
    b.Content = "Some generic content " + nl.Name;
    // set other properties on Button
    dockPanel.Children.Add(b);
   }
  }
 }
}

在启动时,我的控件上应该出现两个按钮,但在这种情况下没有出现。我尝试以编程方式设置FilterLists 属性(通过将视图引用传递给ViewModel,命名我的控件和viewReference.myControl.FilterLists = nll;)并且这可行,但我希望能够如上所示进行绑定。我错过了什么?

【问题讨论】:

  • this.DataContext = this; 不要这样做。请。在您的 UserControls 中,只需使用 Binding.ElementName 来引用 UserControl(为控件的根指定 x:Name,并在将控件绑定到 UserControl 表面上的属性时使用它。您正在为此设置自己心碎练习。
  • @Will,是的,我已经改变了这一点,我们已经在 Clemens 的回答中介绍了它(见那里的 cmets)。
  • 另外,在你受到诱惑之前,不要为你的 UserControls 创建 ViewModel。 UC 应该只是 UI,它们需要的任何逻辑/绑定都应该存在于代码隐藏中。

标签: wpf mvvm data-binding user-controls dependency-properties


【解决方案1】:

除了依赖属性的 CLR 包装器中的 SetValue 之外,您不应该做任何事情,因为 WPF 可能会绕过包装器。有关详细信息,请参阅 MSDN 上的XAML Loading and Dependency Properties 文章。

您必须使用依赖属性元数据注册一个 PropertyChangedCallback,而不是在 setter 中调用 RegenerateFilter()

public NamedListList FilterLists
{
    get { return (NamedListList)GetValue(FilterListsProperty); }
    set { SetValue(FilterListsProperty, value); }
}

public static readonly DependencyProperty FilterListsProperty =
    DependencyProperty.Register(
        "FilterLists", typeof(NamedListList), typeof(BasicFilter),
        new PropertyMetadata(null, FilterListsPropertyChanged));

private static void FilterListsPropertyChanged(
    DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    ((BasicFilter)o).RegenerateFilter();
}

也就是说,您的视图模型不应该从 DependencyObject 派生并声明依赖属性。相反,它应该实现INotifyPropertyChanged 接口,并在必须通知集合更改时使用 ObservableCollections。


编辑:由于 BasicFilter 构造函数中的语句this.DataContext = this;,UserControl 的 DataContext 设置为它自己,而不是任何视图模型实例。因此,绑定基础结构会在 UserControl 中搜索绑定源属性 LocationFilterLists,而这当然不存在。

从 BasicFilter 构造函数中删除该行,并让 UserControl 从其父控件(自动工作)继承其 DataContext。这假设例如的 DataContext MainWindow 被设置为 MainViewModel 的一个实例。

【讨论】:

  • 我使用 PropertyChangedCallback 完成了它,但是当 LocationFilterLists = nll; 执行时没有任何反应。我正在在我不太明白的输出中收到此错误消息:System.Windows.Data Error: 40 : BindingExpression path error: 'LocationFilterLists' property not found on 'object' ''BasicFilter' (Name='')'. BindingExpression:Path=LocationFilterLists; DataItem='BasicFilter' (Name=''); target element is 'BasicFilter' (Name=''); target property is 'FilterLists' (type 'NamedListList') 我不需要通知集合更改 - 一旦设置,它们就不会更改。
  • 太好了,成功了!标题没有显示,但那是因为dockPanel.Children.Clear(); 并且因为我必须将dockPanelDataContext 设置为thisBasicFilter 控制)。我添加了另一个名为dockPanelOutDockPanel,它包装了现有的标签并将标签移出dockPanel 并设置dockPanelOut.DataContext = this,它现在可以工作了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-23
  • 2014-08-31
  • 2011-06-22
  • 2018-04-26
  • 1970-01-01
  • 2011-07-22
  • 1970-01-01
相关资源
最近更新 更多