1

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

以下是详细信息:

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

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>
4

2 回答 2

1

这需要多少 XAML 绝对令人作呕。我将寻找一种使用样式和模板来清理它的方法。另外,我需要将线画到点的中心,这应该不难。现在,下面是有效的。我最终创建了一个Collection<Pair<Point, Point>> ViewModel绑定“线”集合。否则我正在逐点查看线并且无法画线,因为我找不到 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);
            }
        }
    }
}
于 2013-07-25T15:58:49.500 回答
1

LineView必须是LineViewModel,它会更正确。

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

主控

  • 使用ItemsControl.
  • ItemsControl.PanelControl必须是Canvas
  • ItemsSource- 你的收藏PointWiewModel
  • DataTemplate为 types制作两个s PointWiewModel
  • 制作 PointView 控件并将其放入相应的 DataTemplate 中。

点视图控件

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

结果

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

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

添加了问题的答案

Silverlight 5 支持它。这意味着所有项目都将放置在 Canvas 控件上。一些关于 ItemsControl 的文章

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

PointView是第二个用户控件。

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

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

当您熟悉它时,您可以将您的移动ItemsControl到模板化控件中。ItemSource当他们改变位置时,制作你自己的收藏和绘制路径。

您还可以搜索一些开源图形控件并查看它们如何通过点绘制曲线。实际上,他们通常会按照Path我所描述的那样做。

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

PS:这是一个有趣的问题,所以如果我有空闲时间我可能会写一篇文章。关于模板化控件,您可以在此处阅读。

于 2013-07-14T12:15:37.557 回答