如何将画布的子项绑定到集合

本文关键字:绑定 集合 | 更新日期: 2023-09-27 18:30:17

我的一个控件中有一个画布,我想在集合增长/收缩时添加/删除子项。

我知道我可以做到这一点:

<Canvas>
    <Canvas.Children>
        <Rectangle/>
        <TextBox/>
    </Canvas.Children>
</Canvas>

但是这些元素是静态定义的。我设想这样的事情:

<Canvas>
    <Canvas.Children ItemsSource={Binding Path="ItemCollection", Converter="{StaticResource VisualConverter}}/>
</Canvas>

是否有类似于我上面的代码的东西,它实际上有效?我知道其他帖子,例如这个和这个,但我在这些答案中看到的一个问题(出于我的目的)是你失去了命名画布并在代码隐藏中访问它的能力:它是模板中的一个元素,因此超出了范围。

如何将画布的子项绑定到集合

在显示项目集合(并支持绑定)时,您应该考虑ItemsControl。在这种情况下,您可以将其ItemsPanel设置为持有Canvas的某个ItemsPanelTemplate,如下所示:

<ItemsControl ItemsSource="{Binding ItemCollection, 
                           Converter={StaticResource VisualConverter}}">
   <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
          <Canvas/>
      </ItemsPanelTemplate>
   </ItemsControl.ItemsPanel>
</ItemsControl>

现在,ItemsControl就像一个Canvas,里面装着一些充满活力的孩子。

更新

我怀疑你遵循错误的方法。不确定这些方法的必要性如何,因此您必须创建自定义 Canvas 并需要在代码隐藏中轻松访问它。使用标准画布将无法为 Children 属性设置某些绑定,因为它是只读的。在这里,我为支持绑定的附加属性(而不是标准的非附加 Children 属性)介绍一个简单的实现:

public static class CanvasService
{
    public static readonly DependencyProperty ChildrenProperty = DependencyProperty.RegisterAttached("Children", typeof(IEnumerable<UIElement>), typeof(CanvasService), new UIPropertyMetadata(childrenChanged));
    private static Dictionary<INotifyCollectionChanged, Canvas> references = new Dictionary<INotifyCollectionChanged, Canvas>();
    public static IEnumerable<UIElement> GetChildren(Canvas cv)
    {
        return cv.GetValue(ChildrenProperty) as IEnumerable<UIElement>;
    }
    public static void SetChildren(Canvas cv, IEnumerable<UIElement> children)
    {
        cv.SetValue(ChildrenProperty, children);
    }
    private static void childrenChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var canvas = target as Canvas;
        repopulateChildren(canvas);
        var be = canvas.GetBindingExpression(ChildrenProperty);
        if (be != null)
        {
            var elements = (be.ResolvedSourcePropertyName == null ? be.ResolvedSource : be.ResolvedSource.GetType().GetProperty(be.ResolvedSourcePropertyName).GetValue(be.ResolvedSource)) as INotifyCollectionChanged;
            if (elements != null)
            {
                var cv = references.FirstOrDefault(i => i.Value == canvas);
                if (!cv.Equals(default(KeyValuePair<INotifyCollectionChanged,Canvas>)))
                     references.Remove(cv.Key);
                references[elements] = canvas;
                elements.CollectionChanged -= collectionChangedHandler;
                elements.CollectionChanged += collectionChangedHandler;
            }
        } else references.Clear();            
    }
    private static void collectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
    {
        Canvas cv;
        if (references.TryGetValue(sender as INotifyCollectionChanged, out cv))
        {                
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var item in e.NewItems) cv.Children.Add(item as UIElement);
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var item in e.OldItems) cv.Children.Remove(item as UIElement);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    repopulateChildren(cv);
                    break;
            }
        }
    }
    private static void repopulateChildren(Canvas cv)
    {
        cv.Children.Clear();
        var elements = GetChildren(cv);
        foreach (UIElement elem in elements){
            cv.Children.Add(elem);
        }
    }
}

在 XAML 中的用法

<Canvas local:CanvasService.Children="{Binding ItemCollection, 
                                  Converter={StaticResource VisualConverter}}"/>

我再次认为你应该考虑另一种方法。您应该围绕 ItemsControl 有一些解决方案。