通过基本WPF指导新手?(我没有摸它。)
这个问题听上去就是这样。无论我多么努力,多么频繁地试图理解WPF,我都觉得我的头撞到了墙上。我喜欢Winforms,一切都有意义 例如,我正在尝试编写一个简单的应用程序,它允许我布置一组二维路径(由多段线表示)并拖动它们的顶点,并使顶点信息与演示者(我想是ViewModel)同步 因此,问题是:通过基本WPF指导新手?(我没有摸它。),wpf,Wpf,这个问题听上去就是这样。无论我多么努力,多么频繁地试图理解WPF,我都觉得我的头撞到了墙上。我喜欢Winforms,一切都有意义 例如,我正在尝试编写一个简单的应用程序,它允许我布置一组二维路径(由多段线表示)并拖动它们的顶点,并使顶点信息与演示者(我想是ViewModel)同步 因此,问题是: 使窗口将IExtendedObjectPresenter的实例识别为数据源 从IExtendedObject的集合中,为每个IExtendedObject绘制一条多段线 对于扩展对象中由集合IExte
- 使窗口将
的实例识别为数据源李>IExtendedObjectPresenter
- 从
的集合中,为每个IExtendedObject
绘制一条多段线李>IExtendedObject
- 对于扩展对象中由集合
表示的每个顶点,将多段线顶点放置在指定的坐标处IExtendedObject.Points
在我被提名并告知RTFM之前,我想再次强调,我已经对WPF的基本概念进行了很多次研究。我对它的了解并不比第一次发布时多。它似乎完全无法穿透。针对一种行为给出的示例在任何方面都不适用于哪怕是稍有不同的行为类型,因此您回到了原点。我希望重复和有针对性的示例可能会在某个时候在我的脑海中打开一盏灯。我将展示如何使用MVVM模式为具有可拖动顶点的2D Poliline构建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");
}
}
}
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; }
}
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; }
}
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;
}
}
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);
}
}
类ViewModelBase
仅包含接口INotifyPropertyChanged
的实现。这对于在视觉表示上反映clr属性的更改是必需的
LineViewModel.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");
}
}
}
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; }
}
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; }
}
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;
}
}
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);
}
}
它具有点的引用,因此将自动接收更改
MainViewModel.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");
}
}
}
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; }
}
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; }
}
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;
}
}
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);
}
}
左上边距等于宽度和高度的一半。我们有一个透明的Fill
,因为这个属性没有默认值,并且鼠标事件不起作用
差不多就这些了。只保留拖放功能
PointView.xaml.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");
}
}
}
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; }
}
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; }
}
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;
}
}
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);
}
}
MainVindow.xaml.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");
}
}
}
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; }
}
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; }
}
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;
}
}
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);
}
}
因此,您的示例是使用2个xaml文件和3个viewmodels完成的。我很同情您。真正理解WPF需要很长时间,完成最简单的事情可能会非常令人沮丧。但是,潜入一个对专家来说并不容易的问题只是自找麻烦。您需要处理更简单的任务并阅读大量代码,直到事情开始变得有意义为止。Donald Knuth说,在做练习之前,你并不真正了解材料
我解决了你的问题,我承认有很多先进的概念在干净地做这件事,并附加在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>
并将System.Windows.Interactivity
添加到您的项目中。@Tom如果Xaml让您发疯,我建议您在代码中而不是在Xaml中尝试一些东西。@Tom我建议从Canvas
控件开始,通过编程方式向其中添加元素并更改它们的位置。您可以体验使用路径,但不必同时与添加的Xaml头锅炉竞争。无论谁投票关闭,请解释原因。这是一个合理的问题。谢谢。如果您对WinForms感到更满意,您可以在没有MVVM的情况下进行编码,MVVM与WinForms的概念相同,只是UI将由Xaml解析器创建。此外,我认为安装Expression Blend也是一个好主意,因为它更容易完成许多任务,例如编辑样式。Rick Sladkey和vorrtex对此问题的两个答案表示感谢。正是像他们这样的人使stackoverflow成为了一个非常有价值的资源。谢谢,这是一个非常好而且非常详细的例子。非常感谢您抽出时间发布。我也对沃尔特斯说过同样的话,因为他们都很有帮助。@Rick我希望我能投两次赞成票!!感谢您花时间解释这个概念并提供详细的答案。不,我明白了,哇。虽然我同意这不是一个适合StackOverflow的问题,因为它离题了>太宽泛了,但我真的很钦佩你的回答能够解决这个问题。好东西,伙计。非常感谢。开箱即用,非常有用。谢谢你,这是一个非常好的非常详细的例子。非常感谢您抽出时间发布。我也对瑞克·斯莱德基说过同样的话,因为他们都很有帮助。