是否可以在XAML中绑定Canvas的Children属性?

Rob*_*Rob 57 c# data-binding wpf canvas .net-3.5

我有点惊讶的是,无法通过XAML为Canvas.Children设置绑定.我不得不采用代码隐藏方法,看起来像这样:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    DesignerViewModel dvm = this.DataContext as DesignerViewModel;
    dvm.Document.Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged);

    foreach (UIElement element in dvm.Document.Items)
        designerCanvas.Children.Add(element);
}

private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    ObservableCollection<UIElement> collection = sender as ObservableCollection<UIElement>;

    foreach (UIElement element in collection)
        if (!designerCanvas.Children.Contains(element))
            designerCanvas.Children.Add(element);

    List<UIElement> removeList = new List<UIElement>();
    foreach (UIElement element in designerCanvas.Children)
        if (!collection.Contains(element))
            removeList.Add(element);

    foreach (UIElement element in removeList)
        designerCanvas.Children.Remove(element);
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢在XAML中设置一个绑定,如下所示:

<Canvas x:Name="designerCanvas"
        Children="{Binding Document.Items}"
        Width="{Binding Document.Width}"
        Height="{Binding Document.Height}">
</Canvas>
Run Code Online (Sandbox Code Playgroud)

有没有办法在不采用代码隐藏方法的情况下实现这一目标?我已经完成了一些关于这个主题的谷歌搜索,但是对于这个具体问题还没有提出太多.

我不喜欢我当前的方法,因为它通过使View知道它的ViewModel来破坏我的漂亮的Model-View-ViewModel.

ken*_*ner 140

<ItemsControl ItemsSource="{Binding Path=Circles}">
    <ItemsControl.ItemsPanel>
         <ItemsPanelTemplate>
              <Canvas Background="White" Width="500" Height="500"  />
         </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Ellipse Fill="{Binding Path=Color, Converter={StaticResource colorBrushConverter}}" Width="25" Height="25" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
            <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>
Run Code Online (Sandbox Code Playgroud)

  • 几乎没有 - 你的解决方案为每个项目创建一个单独的画布 - 这个解决方案更加优雅和高效,因为只有一个画布用于渲染所有项目 (15认同)
  • 以防万一有人试图像我一样在WinRT上这样做:那里的setter不支持绑定 - http://stackoverflow.com/questions/11857505/how-do-i-do-bindings-in-itemcontainerstyle-in- WinRT的 (5认同)

Pav*_*aev 25

其他人已经就如何做你真正想做的事做了可扩展的回复.我只是解释为什么你不能Children直接绑定.

问题非常简单 - 数据绑定目标不能是只读属性,而且Panel.Children是只读的.那里的藏品没有特殊处理.相反,它ItemsControl.ItemsSource是一个读/写属性,即使它是集合类型 - .NET类很少出现,但需要支持绑定方案.


Jos*_*h G 19

ItemsControl 用于从其他集合创建UI控件的动态集合,甚至是非UI数据集合.

你可以模板化ItemsControl绘制一个Canvas.理想的方法是将支持面板设置为a Canvas,然后设置直接子项的Canvas.LeftCanvas.Top属性.我无法使用它,因为ItemsControl用容器包装它的子容器,很难Canvas在这些容器上设置属性.

相反,我使用a Grid作为所有项目的bin,并逐个绘制它们Canvas.这种方法有一些开销.

<ItemsControl x:Name="Collection" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:MyPoint}">
            <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Ellipse Width="10" Height="10" Fill="Black" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"/>
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
Run Code Online (Sandbox Code Playgroud)

这是我用来设置源集合的代码:

List<MyPoint> points = new List<MyPoint>();

points.Add(new MyPoint(2, 100));
points.Add(new MyPoint(50, 20));
points.Add(new MyPoint(200, 200));
points.Add(new MyPoint(300, 370));

Collection.ItemsSource = points;
Run Code Online (Sandbox Code Playgroud)

MyPoint是一个自定义类,其行为与System版本相同.我创建它是为了演示您可以使用自己的自定义类.

最后一个细节:您可以将ItemsSource属性绑定到您想要的任何集合.例如:

<ItemsControls ItemsSource="{Binding Document.Items}"><!--etc, etc...-->
Run Code Online (Sandbox Code Playgroud)

有关ItemsControl及其工作原理的更多详细信息,请查看以下文档:MSDN Library Reference ; 数据模板 ; WPF博士关于ItemsControl的系列文章.

  • 这是否有性能问题,因为您要为要绘制的每个点添加一个全新的画布? (3认同)

小智 12

internal static class CanvasAssistant
{
    #region Dependency Properties

    public static readonly DependencyProperty BoundChildrenProperty =
        DependencyProperty.RegisterAttached("BoundChildren", typeof (object), typeof (CanvasAssistant),
                                            new FrameworkPropertyMetadata(null, onBoundChildrenChanged));

    #endregion

    public static void SetBoundChildren(DependencyObject dependencyObject, string value)
    {
        dependencyObject.SetValue(BoundChildrenProperty, value);
    }

    private static void onBoundChildrenChanged(DependencyObject dependencyObject,
                                               DependencyPropertyChangedEventArgs e)
    {
        if (dependencyObject == null)
        {
            return;
        }
        var canvas = dependencyObject as Canvas;
        if (canvas == null) return;

        var objects = (ObservableCollection<UIElement>) e.NewValue;

        if (objects == null)
        {
            canvas.Children.Clear();
            return;
        }

        //TODO: Create Method for that.
        objects.CollectionChanged += (sender, args) =>
                                            {
                                                if (args.Action == NotifyCollectionChangedAction.Add)
                                                    foreach (object item in args.NewItems)
                                                    {
                                                        canvas.Children.Add((UIElement) item);
                                                    }
                                                if (args.Action == NotifyCollectionChangedAction.Remove)
                                                    foreach (object item in args.OldItems)
                                                    {
                                                        canvas.Children.Remove((UIElement) item);
                                                    }
                                            };

        foreach (UIElement item in objects)
        {
            canvas.Children.Add(item);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用:

<Canvas x:Name="PART_SomeCanvas"
        Controls:CanvasAssistant.BoundChildren="{TemplateBinding SomeItems}"/>
Run Code Online (Sandbox Code Playgroud)


小智 9

我不相信它可以使用与Children属性的绑定.我今天真的试着这样做,它就像我一样对我产生了错误.

Canvas是一个非常简陋的容器.它真的不是为这种工作而设计的.您应该查看许多ItemsControls中的一个.您可以将ViewModel的ObservableCollection数据模型绑定到其ItemsSource属性,并使用DataTemplates来处理每个项在控件中的呈现方式.

如果找不到以令人满意的方式呈现项目的ItemsControl,则可能必须创建一个能够满足您需要的自定义控件.