【问题标题】:WPF: Binding to ObservableCollection in ControlTemplate is not updatedWPF:绑定到 ControlTemplate 中的 ObservableCollection 未更新
【发布时间】:2010-06-15 14:45:23
【问题描述】:

我为我的自定义控件MyControl 创建了一个ControlTemplate

MyControl 派生自 System.Windows.Controls.Control 并定义以下属性 public ObservableCollection<MyControl> Children{ get; protected set; }

为了显示嵌套的子控件,我使用了 ItemsControl (StackPanel),它被 GroupBox 包围。如果没有子控件,我想隐藏GroupBox

在应用程序启动时一切正常:如果 Children 属性最初包含至少一个元素,则显示组框和子控件。在另一种情况下,它是隐藏的。

当用户将子控件添加到空集合时,问题就开始了。 GroupBox 的可见性仍处于折叠状态。当从集合中删除最后一个子控件时,也会出现同样的问题。 GroupBox 仍然可见。 另一个症状是HideEmptyEnumerationConverter 转换器没有被调用。 向非空集合添加/删除子控件按预期工作。

以下绑定有什么问题?显然它只工作一次但不会更新,尽管我绑定的集合是ObservableCollection 类型。

<!-- Converter for hiding empty enumerations -->
<Common:HideEmptyEnumerationConverter x:Key="hideEmptyEnumerationConverter"/>
<!--- ... --->

<ControlTemplate TargetType="{x:Type MyControl}">
  <!-- ... other stuff that works ... -->
  <!-- Child components -->
  <GroupBox Header="Children"
            Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
              Path=Children, Converter={StaticResource hideEmptyEnumerationConverter}}">
    <ItemsControl ItemsSource="{TemplateBinding Children}"/>
  </GroupBox>
</ControlTemplate>

.

[ValueConversion(typeof (IEnumerable), typeof (Visibility))]
public class HideEmptyEnumerationConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int itemCount = ((IEnumerable) value).Cast<object>().Count();
        return itemCount == 0 ? Visibility.Collapsed : Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

另一个更普遍的问题:你们如何调试绑定?找到了这个 (http://bea.stollnitz.com/blog/?p=52),但我仍然觉得很难做到。

很高兴有任何帮助或建议。

【问题讨论】:

    标签: wpf binding observablecollection controltemplate relativesource


    【解决方案1】:

    问题是你的Children 属性本身永远不会改变,只是它的内容。由于属性值不会更改,因此不会重新评估绑定。您需要做的是绑定到集合的Count 属性。实现此目的的最简单方法是在模板中使用 DataTrigger

    <ControlTemplate TargetType="{x:Type MyControl}">
      <!-- ... other stuff that works ... -->
      <!-- Child components -->
      <GroupBox x:Name="gb" Header="Children">
        <ItemsControl ItemsSource="{TemplateBinding Children}"/>
      </GroupBox>
      <ControlTemplate.Triggers>
          <DataTrigger Binding="{Binding Path=Children.Count, RelativeSource={RelativeSource TemplatedParent}}"
                       Value="0">
            <Setter TargetName="gb" Property="Visibility" Value="Collapsed" />
          </DataTrigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>
    

    【讨论】:

      【解决方案2】:

      您需要在您的 Children 属性中的项目数量发生变化时通知您。您可以通过实现 INotifyPropertyChanged 接口来做到这一点,注册到 Children 集合的 CollectionChanged 事件并从那里引发 PropertyChanged。

      例子:

      public class MyControl : Control, INotifyPropertyChanged
      {
          static MyControl()
          {
              DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
          }
      
          public ObservableCollection<UIElement> Children
          {
              get { return (ObservableCollection<UIElement>)GetValue(ChildrenProperty); }
              set { SetValue(ChildrenProperty, value); }
          }
      
          // Using a DependencyProperty as the backing store for Children.  This enables animation, styling, binding, etc...
          public static readonly DependencyProperty ChildrenProperty =
              DependencyProperty.Register("Children", typeof(ObservableCollection<UIElement>), typeof(MyControl), new UIPropertyMetadata(0));
      
          public MyControl()
          {
              Children = new ObservableCollection<UIElement>();
              Children.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Children_CollectionChanged);
          }
      
          void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
          {
              RaisePropertyChanged("Children");
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
          public void RaisePropertyChanged(String propertyName)
          {
              PropertyChangedEventHandler temp = PropertyChanged;
              if (temp != null)
              {
                  temp(this, new PropertyChangedEventArgs(propertyName));
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-01-02
        • 2010-11-29
        • 2011-12-30
        • 2021-08-31
        • 2015-01-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多