是否可以使用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); }
}
}
}
这是View,它是一个包含在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>
答案 0 :(得分:1)
您的LineView
必须是LineViewModel
,它会更正确。
我描述了积分的机制,对于我认为你会自己理解的行。
主要控制
ItemsControl
。 ItemsControl.PanelControl
必须为Canvas
。 ItemsSource
- 您的PointWiewModel
。DataTemplate
制作两个PointWiewModel
。PointView控件
Canvas.X
附加属性到PointViewModel.X
属性。Canvas.Y
附加属性到PointViewModel.Y
属性。Canvas.X
和Canvas.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。答案 1 :(得分:1)
绝对令人厌恶的是这需要多少XAML。我将寻找一种使用样式和模板进行清理的方法。此外,我需要画线到点的中心,这应该不难。目前,以下是有效的。我最终创建了一个Collection<Pair<Point, Point>> ViewModel
来绑定“Line”集合。否则我会逐点看线,因为找不到X2 / Y2而无法画线。
感谢Alexander的灵感。
这是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>
这是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 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);
}
}
}
}