问题是听起来的.无论我多么努力,多久我都会尝试理解WPF,我觉得我正撞在墙上.我喜欢Winforms,一切都很有意义.
作为一个例子,我正在尝试编写一个简单的应用程序,它允许我布置一堆二维路径(由折线表示)并拖动它们的顶点,并使顶点信息与演示者同步(即,我想,一个ViewModel)
因此问题是:
IExtendedObjectPresenter作为数据源的实例;IExtendedObject,为每个绘制一条折线IExtendedObject;IExtendedObject.Points,将折线顶点放置在指定的坐标处.IDE在这里完全没有给我任何帮助.XAML中可用的许多属性对我来说都没有意义.因为似乎隐含地做了很多,所以没有明显的地方可以告诉窗户该做什么.
在我定期并告知RTFM之前,我想再次强调我已经多次研究过WPF的基本概念.我对它的了解不比我第一次发布时所做的更多.它似乎完全无法穿透.给出一种行为类型的示例并不以任何方式适用于稍微不同的行为,因此您将回到原点.我希望重复和有针对性的例子可能会在某些时候打开我的脑海.
Ric*_*key 26
我很同情你.真正理解WPF需要很长时间,完成最简单的事情会非常令人沮丧.但是,潜入一个对专家来说不容易的问题只是要求麻烦.您需要处理更简单的任务并阅读大量代码,直到事情开始变得有意义.唐纳德克努特说,在你做练习之前,你真的不知道这些材料.
我解决了你的问题,我承认在干净利落的时候有很多先进的概念,并且在MVVM上加强使得它变得更加困难.对于它的价值,这里是一个零问题的代码隐藏解决方案,符合MVVM的精神.
这是XAML:
<Grid>
<Grid.Resources>
<local:PolylineCollection x:Key="sampleData">
<local:Polyline>
<local:Coordinate X="50" Y="50"/>
<local:Coordinate X="100" Y="100"/>
<local:Coordinate X="50" Y="150"/>
</local:Polyline>
</local:PolylineCollection>
</Grid.Resources>
<Grid DataContext="{StaticResource sampleData}">
<ItemsControl ItemsSource="{Binding Segments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X1="{Binding Start.X}" Y1="{Binding Start.Y}" X2="{Binding End.X}" Y2="{Binding End.Y}" Stroke="Black" StrokeThickness="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding ControlPoints}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Margin="-10,-10,0,0" Width="20" Height="20" Stroke="DarkBlue" Fill="Transparent">
<i:Interaction.Behaviors>
<local:ControlPointBehavior/>
</i:Interaction.Behaviors>
</Ellipse>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
Run Code Online (Sandbox Code Playgroud)
以下是支持类:
public class Coordinate : INotifyPropertyChanged
{
private double x;
private double y;
public double X
{
get { return x; }
set { x = value; OnPropertyChanged("X", "Point"); }
}
public double Y
{
get { return y; }
set { y = value; OnPropertyChanged("Y", "Point"); }
}
public Point Point
{
get { return new Point(x, y); }
set { x = value.X; y = value.Y; OnPropertyChanged("X", "Y", "Point"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propertyNames)
{
foreach (var propertyName in propertyNames)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Polyline : List<Coordinate>
{
}
public class Segment
{
public Coordinate Start { get; set; }
public Coordinate End { get; set; }
}
public class PolylineCollection : List<Polyline>
{
public IEnumerable<Segment> Segments
{
get
{
foreach (var polyline in this)
{
var last = polyline.FirstOrDefault();
foreach (var coordinate in polyline.Skip(1))
{
yield return new Segment { Start = last, End = coordinate };
last = coordinate;
}
}
}
}
public IEnumerable<Coordinate> ControlPoints
{
get
{
foreach (var polyline in this)
{
foreach (var coordinate in polyline)
yield return coordinate;
}
}
}
}
public class ControlPointBehavior : Behavior<FrameworkElement>
{
private bool mouseDown;
private Vector delta;
protected override void OnAttached()
{
var canvas = AssociatedObject.Parent as Canvas;
AssociatedObject.MouseLeftButtonDown += (s, e) =>
{
mouseDown = true;
var mousePosition = e.GetPosition(canvas);
var elementPosition = (AssociatedObject.DataContext as Coordinate).Point;
delta = elementPosition - mousePosition;
AssociatedObject.CaptureMouse();
};
AssociatedObject.MouseMove += (s, e) =>
{
if (!mouseDown) return;
var mousePosition = e.GetPosition(canvas);
var elementPosition = mousePosition + delta;
(AssociatedObject.DataContext as Coordinate).Point = elementPosition;
};
AssociatedObject.MouseLeftButtonUp += (s, e) =>
{
mouseDown = false;
AssociatedObject.ReleaseMouseCapture();
};
}
}
Run Code Online (Sandbox Code Playgroud)
此解决方案使用行为,这是实现与MVVM交互的理想选择.
如果您不熟悉行为,请安装Expression Blend 4 SDK并添加以下命名空间:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Run Code Online (Sandbox Code Playgroud)
并添加System.Windows.Interactivity到您的项目.
vor*_*olf 13
我将展示如何使用具有可拖动顶点的2D-Poliline的MVVM模式构建WPF应用程序.
PointViewModel.cs
public class PointViewModel: ViewModelBase
{
public PointViewModel(double x, double y)
{
this.Point = new Point(x, y);
}
private Point point;
public Point Point
{
get { return point; }
set
{
point = value;
OnPropertyChanged("Point");
}
}
}
Run Code Online (Sandbox Code Playgroud)
该类ViewModelBase仅包含接口的实现INotifyPropertyChanged.这对于反映视觉表示上的clr-属性的变化是必要的.
LineViewModel.cs
public class LineViewModel
{
public LineViewModel(PointViewModel start, PointViewModel end)
{
this.StartPoint = start;
this.EndPoint = end;
}
public PointViewModel StartPoint { get; set; }
public PointViewModel EndPoint { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
它引用了Points,因此将自动接收更改.
MainViewModel.cs
public class MainViewModel
{
public MainViewModel()
{
this.Points = new List<PointViewModel>
{
new PointViewModel(30, 30),
new PointViewModel(60, 100),
new PointViewModel(50, 120)
};
this.Lines = this.Points.Zip(this.Points.Skip(1).Concat(this.Points.Take(1)),
(p1, p2) => new LineViewModel(p1, p2)).ToList();
}
public List<PointViewModel> Points { get; set; }
public List<LineViewModel> Lines { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
它包含点和线的样本数据
MainVindow.xaml
<Window.Resources>
<ItemsPanelTemplate x:Key="CanvasPanelTemplate">
<Canvas/>
</ItemsPanelTemplate>
<Style x:Key="PointListBoxItem">
<Setter Property="Canvas.Left" Value="{Binding Point.X}"/>
<Setter Property="Canvas.Top" Value="{Binding Point.Y}"/>
</Style>
<DataTemplate x:Key="LineTemplate">
<Line X1="{Binding StartPoint.Point.X}" X2="{Binding EndPoint.Point.X}" Y1="{Binding StartPoint.Point.Y}" Y2="{Binding EndPoint.Point.Y}" Stroke="Blue"/>
</DataTemplate>
<DataTemplate x:Key="PointTemplate">
<view:PointView />
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Lines}" ItemsPanel="{StaticResource CanvasPanelTemplate}" ItemTemplate="{StaticResource LineTemplate}"/>
<ItemsControl ItemsSource="{Binding Points}" ItemContainerStyle="{StaticResource PointListBoxItem}" ItemsPanel="{StaticResource CanvasPanelTemplate}"
ItemTemplate="{StaticResource PointTemplate}"/>
</Grid>
Run Code Online (Sandbox Code Playgroud)
这里有很多技巧.首先,这些ItemsControls不是基于垂直StackPanel,而是基于Canvas.在ItemsControl同一个目标上放置必要物品的坐标点施加一个特殊的容器模板.但是ItemsControl线条不需要这样的模板,在某些方面它很奇怪.最后两个DataTemplates是显而易见的.
PointView.xaml
<Ellipse Width="12" Height="12" Stroke="Red" Margin="-6,-6,0,0" Fill="Transparent"/>
Run Code Online (Sandbox Code Playgroud)
左边和顶部边距等于的一半Width和Height.我们有一个透明的,Fill因为这个属性没有默认值,鼠标的事件不起作用.
这几乎都是.只有拖放功能仍然存在.
PointView.xaml.cs
public partial class PointView : UserControl
{
public PointView()
{
InitializeComponent();
this.MouseLeftButtonDown += DragSource_MouseLeftButtonDown;
this.MouseMove += DragSource_MouseMove;
}
private bool isDraggingStarted;
private void DragSource_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.isDraggingStarted = true;
}
private void DragSource_MouseMove(object sender, MouseEventArgs e)
{
if (isDraggingStarted == true)
{
var vm = this.DataContext as PointViewModel;
var oldPoint = vm.Point;
DataObject data = new DataObject("Point", this.DataContext);
DragDropEffects effects = DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
if (effects == DragDropEffects.None) //Drag cancelled
vm.Point = oldPoint;
this.isDraggingStarted = false;
}
}
Run Code Online (Sandbox Code Playgroud)
MainVindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
this.AllowDrop = true;
this.DragOver += DropTarget_DragOver;
}
private void DropTarget_DragOver(object sender, DragEventArgs e)
{
var vm = e.Data.GetData("Point") as PointViewModel;
if (vm != null)
vm.Point = e.GetPosition(this);
}
}
Run Code Online (Sandbox Code Playgroud)
因此,您的示例使用2个xaml文件和3个视图模型完成.