2013-07-09 84 views
1

是否可以使用DataTemplate将点集合呈现为一束线(使用数据绑定和拖放)?WPF/Silverlight绑定到视图模型元素集合的画布

下面是详细信息:

我有我的视图模型的多个对象。这些对象最终在绝对像素坐标中指定的画布上具有位置。我需要能够将这些项目拖放到画布上并更新坐标。一些对象由一个点表示,另一些则是线段的集合。我正在使用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> 

回答

1

这绝对令人厌恶这需要多少XAML。我会寻找一种方法来使用样式和模板进行清理。另外,我需要画出线条的中心点,这不应该很难。现在,以下是有效的。我最终创建了一个Collection<Pair<Point, Point>> 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); 
      } 
     } 
    } 
} 
1

LineView必须LineViewModel,这将是更正确。

我描述点的机制,对于我认为自己会理解的行。

主控

  • 使用ItemsControl
  • ItemsControl.PanelControl必须是Canvas。您的收藏PointWiewModel
  • PointWiewModel类型制作两个DataTemplate s。
  • 制作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

+0

谢谢您的回复!在我发布的代码中,我有一个'ItemsControl.ItemPanel.ItemTemplate = Canvas',并且我有'ItemsSource'绑定到我的'LinesViewModel'。我google了一下,但没有看到有关'ItemsControl.PanelControl'的更多信息,那是什么?它在Silverlight 5中可用吗?至于'ItemsSource',我不需要将它绑定到'LinesViewModel',它包含所有的行,而这些行又包含所有的点? PointView控件是否是第二个UserControl?第二个XAML文件?或者是数据模板?对不起,我还有点困惑。来自佛罗里达的欢呼! –

+0

我试过嵌套ItemsControls在彼此的内部和使用嵌套的DataTemplate对象。我确实看到我所有的点都是以这种方式绘制的,但是他们都在0,0之上。无论我将每个元素的大小设置为或每个椭圆的Canvas.Top和Canvas.Left属性都没有关系。现在,它看起来像我必须破解它,并暴露我的视图模型中的数据绑定形状。 –

+0

杰森,我编辑了我的答案。 –

相关问题