简体   繁体   English

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

[英]How to bind a canvas' children to a collection?

I have a canvas in one of my controls that I'd like to add/remove children to as a collection grows/shrinks. 我在其中一个控件中有一个画布,随着集合的增长/缩小,我想向其中添加/删除子级。

I know that I can do this: 我知道我可以这样做:

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

But these elements are statically defined. 但是这些元素是静态定义的。 I envision something like this: 我设想这样的事情:

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

Is there something analogous to my above code, which actually works? 有没有类似于我上面的代码的东西,实际上可以工作吗? I am aware of others posts, such as this one and this one but a problem I see with these answers (for my purposes) is that you lose the capability of naming the canvas and accessing it in code behind: it's an element in a template, and is therefore out of scope. 我知道其他帖子,例如本篇本篇,但是我看到这些答案(出于我的目的)的一个问题是,您失去了命名画布并在后面的代码中访问它的能力:这是模板中的一个元素,因此超出范围。

When it comes to showing a collection of items (and support Binding), you should think about ItemsControl . 在显示项目集合(并支持Binding)时,您应该考虑ItemsControl In this case you can just set its ItemsPanel to some ItemsPanelTemplate holding a Canvas , something like this: 在这种情况下,您可以将其ItemsPanel设置为一些包含Canvas ItemsPanelTemplate ,如下所示:

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

Now the ItemsControl acts like a Canvas holding some dynamic collection of children. 现在, ItemsControl就像Canvas其中包含一些动态子级集合。

Update : 更新

I doubt that you are following a wrong approach. 我怀疑您使用的是错误的方法。 Not sure how necessary those methods are so that you have to create a custom Canvas and require some easy access to it in codebehind. 不确定这些方法的必要性,以便您必须创建自定义Canvas并要求在代码隐藏中轻松访问它。 Using the standard Canvas would not be able to set some Binding for the Children property because it's readonly. 使用标准的Canvas将无法为Children属性设置某些Binding,因为它是只读的。 Here I introduce a simple implementation for an attached property supporting binding instead of the standard non-attached Children property: 在这里,我为支持绑定的附加属性而不是标准的非附加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);
        }
    }
}

Usage in XAML : 在XAML中的用法

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

Again I think you should consider for another approach. 同样,我认为您应该考虑另一种方法。 You should have some solution around the ItemsControl. 您应该在ItemsControl周围有一些解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM