【问题标题】:WPF/Silverlight Bind Canvas to Collection of View Model ElementsWPF/Silverlight 将画布绑定到视图模型元素的集合
【发布时间】:2013-07-09 20:25:24
【问题描述】:

是否可以使用 DataTemplate 将一组点呈现为一堆线(通过数据绑定和拖放)?

以下是详细信息:

我的视图模型中有多个对象。这些对象最终在画布上具有以绝对像素坐标指定的位置。我需要能够在画布上拖放这些项目并更新它们的坐标。一些对象由一个点表示,其他对象是线段的集合。我正在使用 MVVM (Jounce)。我的视图模型是否应该公开一个以某种方式绑定坐标的ObservableCollection<Shape>?那感觉不对。或者有没有一种方法可以在这里使用 DataTemplates 在给定线段集合的情况下在每个线段的末端绘制带有点的线?这是一个示例 ViewModel:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using Jounce.Core.ViewModel;

namespace CanvasBindTest.ViewModels
{
    /// <summary>
    /// Sample view model showing design-time resolution of data
    /// </summary>
    [ExportAsViewModel(typeof(MainViewModel))]
    public class MainViewModel : BaseViewModel
    {
        public MainViewModel()
        {
            var start = new PointView { X = 0, Y = 0 };
            var middle = new PointView { X = 1132 / 2, Y = 747 / 2 };
            var end = new PointView() { X = 1132, Y = 747 };
            var lineView = new LineView(new[] { start, middle, end });
            Lines = new LinesView(new[] { lineView });
        }

        public LinesView Lines { get; private set; }
    }

    public class LinesView : BaseViewModel
    {
        public ObservableCollection<LineView> Lines { get; private set; }

        public LinesView(IEnumerable<LineView> lines)
        {
            Lines = new ObservableCollection<LineView>(lines);
        }
    }

    public class LineView : BaseViewModel
    {
        public ObservableCollection<PointView> Points { get; private set; }

        public LineView(IEnumerable<PointView> points)
        {
            Points = new ObservableCollection<PointView>(points);
        }
    }

    public class PointView : BaseViewModel
    {
        private int x, y;

        public int X
        {
            get { return x; }
            set { x = value; RaisePropertyChanged(() => X); }
        }

        public int Y { 
            get { return y; }
            set { y = value; RaisePropertyChanged(() => Y); }
        }
    }
}

这里是视图,它是一个包含在带有背景图像的 ItemsControl 中的画布。视图模型坐标相对于背景图像的未缩放大小:

<UserControl x:Class="CanvasBindTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="clr-namespace:CanvasBindTest.ViewModels" 
    mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">

    <UserControl.Resources>
        <DataTemplate x:Key="SkylineTemplate" DataType="viewModels:LineView">
            <ItemsControl ItemsSource="{Binding Points}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <!--I have a collection of points here, how can I draw all the lines I need and keep the end-points of each line editable?-->
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </DataTemplate>
    </UserControl.Resources>

    <Grid d:DataContext="{d:DesignInstance viewModels:MainViewModel, IsDesignTimeCreatable=True}">
        <ScrollViewer x:Name="Scroll">
            <ItemsControl ItemsSource="{Binding Lines}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas>
                            <Canvas.Background>
                                <ImageBrush Stretch="Uniform" ImageSource="Properties/dv629047.jpg"/>
                            </Canvas.Background>
                        </Canvas>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</UserControl>

【问题讨论】:

    标签: wpf silverlight canvas collections shape


    【解决方案1】:

    你的LineView必须是LineViewModel,这样会更正确。

    我描述了点的机制,我认为你会自己理解的线。

    主控制

    • 使用ItemsControl
    • ItemsControl.PanelControl 必须是 Canvas
    • ItemsSource - 你收藏的PointWiewModel
    • PointWiewModel 类型创建两个 DataTemplates。
    • 制作 PointView 控件并将其放入适当的 DataTemplate。

    PointView 控件

    • 两种方式将Canvas.X附加属性绑定到PointViewModel.X属性。
    • 两种方式将Canvas.Y附加属性绑定到PointViewModel.Y属性。
    • 添加在拖动 PointView 控件时更改 Canvas.XCanvas.Y 的逻辑。

    结果

    之后,您可以拖动您的(例如)PointVew 控件,并且您的视图模型中的属性将因为两种方式绑定而更新。

    假设我正确理解你想要什么。

    添加了问题的答案

    Silverlight 5 支持它。这意味着所有项目都将放置在 Canvas 控件上。 Some article about ItemsControl.

    <ItemsControl>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas></Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
    

    PointView 是第二个用户控件。

    注意:我已经描述了一种使用 MVVM 绘制点数组的方法。您可以拖动画布上的每个点并在视图模型中接收新坐标。 (也许我在这个阶段的描述有点混乱,所以我已经从中删除了 LineViews)

    为了制作一条线,您必须连接您的点。这会更难,所以我建议你只用点做一个变体。

    当您熟悉它时,您可以将您的ItemsControl 移动到模板化控件中。制作你自己的ItemSource 集合,并在它们改变位置时通过这些点绘制路径。

    您还可以搜索一些开源图形控件,看看它们是如何通过点绘制曲线的。实际上,他们通常使用Path 来做这件事,就像我描述的那样。

    对不起,我不会写更多,因为它会变成一篇文章而不是答案)

    P.S:这是一个有趣的问题,所以如果我有空闲时间,我可能会写一篇文章。关于模板化控件你可以阅读here

    【讨论】:

    • 感谢您的回复!在我发布的代码中,我有一个ItemsControl.ItemPanel.ItemTemplate = Canvas,并且我将ItemsSource 绑定到我的LinesViewModel。我用谷歌搜索但在ItemsControl.PanelControl 上没有看到太多信息,那是什么?它在 Silverlight 5 中可用吗?至于ItemsSource,我不需要将它绑定到包含所有行的LinesViewModel,而这些行又包含所有点? PointView 控件是否是第二个 UserControl?第二个 XAML 文件?或者那是一个数据模板?对不起,我还是有点糊涂。来自佛罗里达的欢呼声!
    • 我尝试过相互嵌套的 ItemsControls 并使用嵌套的 DataTemplate 对象。我确实看到我所有的点都是这样绘制的,但它们都在 0,0 处相互重叠。我将每个元素的大小设置为什么或每个椭圆的 Canvas.Top 和 Canvas.Left 属性都没有关系。目前看来,我必须破解它并在我的视图模型中公开数据绑定的形状。
    【解决方案2】:

    这需要多少 XAML 真是令人作呕。我将寻找一种使用样式和模板来清理它的方法。另外,我需要将线画到点的中心,这应该不难。现在,以下是有效的。我最终创建了一个Collection&lt;Pair&lt;Point, Point&gt;&gt; ViewModel 来绑定“Line”集合。否则我会逐点查看线,因为找不到 X2/Y2,所以无法画线。

    感谢亚历山大的灵感。

    这是 XAML:

      <ItemsControl ItemsSource="{Binding Lines}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="viewModels:LineViewModel">
                    <ItemsControl ItemsSource="{Binding LineSegments}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Canvas />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <ItemsControl>
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <Canvas />
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl ItemsSource="{Binding Lines}">
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <Canvas />
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <Line X1="{Binding Item1.X}" X2="{Binding Item2.X}" Y1="{Binding Item1.Y}" Y2="{Binding Item2.Y}" Stroke="Black" StrokeThickness="2"/>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                    <ItemsControl ItemsSource="{Binding LineSegment}">
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <Canvas />
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <Ellipse Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" Width="10" Height="10" Fill="Black">
                                                    <Ellipse.RenderTransform>
                                                        <TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
                                                    </Ellipse.RenderTransform>
                                                </Ellipse>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                </ItemsControl>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    

    这是视图模型:

    namespace CanvasBindTest.ViewModels
    {
        /// <summary>
        ///     Sample view model showing design-time resolution of data
        /// </summary>
        [ExportAsViewModel(typeof (MainViewModel))]
        public class MainViewModel : BaseViewModel
        {
            public MainViewModel()
            {
                var start = new PointViewModel {X = 0, Y = 0};
                var middle = new PointViewModel {X = 30, Y = 10};
                var end = new PointViewModel {X = 20, Y = 0};
                var simpleLine = new LineSegmentsViewModel(new[] {start, middle, end});
                Lines = new ObservableCollection<LineViewModel> {new LineViewModel(new[] {simpleLine})};
            }
    
            public ObservableCollection<LineViewModel> Lines { get; private set; }
        }
    
        public class LineViewModel : BaseViewModel
        {
            public LineViewModel(IEnumerable<LineSegmentsViewModel> lineSegments)
            {
                LineSegments = new ObservableCollection<LineSegmentsViewModel>(lineSegments);
            }
    
            public ObservableCollection<LineSegmentsViewModel> LineSegments { get; private set; }
        }
    
        public class LineSegmentsViewModel : BaseViewModel
        {
            public LineSegmentsViewModel(IEnumerable<PointViewModel> lineSegment)
            {
                LineSegment = new ObservableCollection<PointViewModel>(lineSegment);
                Lines = new Collection<Tuple<PointViewModel, PointViewModel>>();
                var tmp = lineSegment.ToArray();
                for (var i = 0; i < tmp.Length - 1; i++)
                {
                    Lines.Add(new Tuple<PointViewModel, PointViewModel>(tmp[i], tmp[i+1]));
                }
            }
    
            public Collection<Tuple<PointViewModel, PointViewModel>> Lines { get; private set; }  
    
            public ObservableCollection<PointViewModel> LineSegment { get; private set; }
        }
    
    
        public class PointViewModel : BaseViewModel
        {
            private int x, y;
    
            public int X
            {
                get { return x; }
                set
                {
                    x = value;
                    RaisePropertyChanged(() => X);
                }
            }
    
            public int Y
            {
                get { return y; }
                set
                {
                    y = value;
                    RaisePropertyChanged(() => Y);
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-18
      • 2018-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多