WPF - MVVM:如何用鼠标绘制一个可移动的矩形
WPF - MVVM: how to draw a movable Rectangle with mouse
我是 WPF 的新手,我想使用 MVVM 模式创建一个程序(不使用像 MvvmLight 这样的外部库)。该应用程序允许用户使用鼠标在 canvas 内绘制矩形(以及未来的其他形状)(类似于 Windows Paint,只是为了清楚起见)。用户也可以 move/resize 使用鼠标交互创建矩形。
此时我实现了一个基本版本,用户可以通过单击按钮添加一个矩形(随机 size/position)。这是实现此行为的 MainWindow 视图的代码:
...
Title="{Binding Path=Titolo}"
Height="450" Width="800"
d:DataContext="{d:DesignInstance vm:MainWindowViewModel}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Add Rectangle" Command="{Binding AddShapeCommand}" />
</StackPanel>
<Canvas>
<ItemsControl ItemsSource="{Binding AllShapeCollection}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vm:MyRectViewModel}">
<uc:MyRectView/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
</StackPanel>
用户控件MyRectView定义如下:
<UserControl x:Class="MVVM_TestRect.Views.MyRectView"
...
xmlns:vm="clr-namespace:MVVM_TestRect.ViewModels"
xmlns:local="clr-namespace:MVVM_TestRect.Views"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:MyRectViewModel}"
Width="auto" Height="auto">
<Grid>
<Rectangle Width="{Binding Path=Width, Mode=TwoWay}"
Height="{Binding Path=Height, Mode=TwoWay}"
Fill="Green"/>
</Grid>
</UserControl>
程序按方面运行,绑定似乎没问题。
现在,如前所述,我将实现用鼠标绘图和 move/resize 形状的能力。
我找到了很多例子,但没有人能解决我的疑惑:
- MVVM 模式不允许在视图代码隐藏中放置事件处理程序(用于鼠标交互),那么我应该在哪里编写事件处理程序?
- MouseAction (msdn description here) 似乎不合适,因为缺少可能的交互(即按下按钮、移动);我应该考虑哪些替代方案?
- 鼠标交互应该由绘制形状的地方 canvas 处理还是由形状本身处理?
- MVVM 是用于此类应用程序的正确模式吗?
MVVM 模式只是将视觉设计与应用程序数据结构分开的理想模式,所以当您说“MVVM 模式不允许将事件处理程序(用于鼠标交互)放在视图代码隐藏中”时,也许就是这样太激烈了。尝试坚持它以获得更好的设计,但一开始不要太疯狂。看看 this page,它可能会引起您的兴趣。
WPF 在后面的代码中自动为您创建事件处理程序,因此这是它们的位置。您可能想要做的是,不是直接在事件处理程序中修改数据,而是会有一个 ViewModel,其方法将帮助您修改数据。
您可以使用与 Canvas 关联的 PreviewMouse(Up,Down,Move) 事件。这可能比尝试单击您的形状更容易,特别是当多个形状可能重叠时。显然,如果你有很多形状,必须有一种方法可以知道要编辑哪些形状(通过组合框手动选择,找到鼠标点击的闭合形状等)
从您的设计来看,可能会给您带来麻烦的一个方面是您的 viewModel 管理 1 个形状。这没有错,但您需要另一个管理形状集合的 VM 层。它可以作为另一个 class 或 MyRectViewModel
:
中的一堆静态方法来完成
void MyCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.StoreMouseDown(e);
}
void MyCanvas_PreviewMouseMove(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.ModifyShapes(e);
}
void MyCanvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.ModifyShapes(e);
}
其中ModifyShapes()
将计算鼠标拖动,找到要编辑的形状并调用其编辑方法。然后,一旦修改了形状数据,就会触发相应的事件来更新关联的View
我是 WPF 的新手,我想使用 MVVM 模式创建一个程序(不使用像 MvvmLight 这样的外部库)。该应用程序允许用户使用鼠标在 canvas 内绘制矩形(以及未来的其他形状)(类似于 Windows Paint,只是为了清楚起见)。用户也可以 move/resize 使用鼠标交互创建矩形。
此时我实现了一个基本版本,用户可以通过单击按钮添加一个矩形(随机 size/position)。这是实现此行为的 MainWindow 视图的代码:
...
Title="{Binding Path=Titolo}"
Height="450" Width="800"
d:DataContext="{d:DesignInstance vm:MainWindowViewModel}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Add Rectangle" Command="{Binding AddShapeCommand}" />
</StackPanel>
<Canvas>
<ItemsControl ItemsSource="{Binding AllShapeCollection}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type vm:MyRectViewModel}">
<uc:MyRectView/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding xLT}" />
<Setter Property="Canvas.Top" Value="{Binding yLT}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Canvas>
</StackPanel>
用户控件MyRectView定义如下:
<UserControl x:Class="MVVM_TestRect.Views.MyRectView"
...
xmlns:vm="clr-namespace:MVVM_TestRect.ViewModels"
xmlns:local="clr-namespace:MVVM_TestRect.Views"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:MyRectViewModel}"
Width="auto" Height="auto">
<Grid>
<Rectangle Width="{Binding Path=Width, Mode=TwoWay}"
Height="{Binding Path=Height, Mode=TwoWay}"
Fill="Green"/>
</Grid>
</UserControl>
程序按方面运行,绑定似乎没问题。
现在,如前所述,我将实现用鼠标绘图和 move/resize 形状的能力。 我找到了很多例子,但没有人能解决我的疑惑:
- MVVM 模式不允许在视图代码隐藏中放置事件处理程序(用于鼠标交互),那么我应该在哪里编写事件处理程序?
- MouseAction (msdn description here) 似乎不合适,因为缺少可能的交互(即按下按钮、移动);我应该考虑哪些替代方案?
- 鼠标交互应该由绘制形状的地方 canvas 处理还是由形状本身处理?
- MVVM 是用于此类应用程序的正确模式吗?
MVVM 模式只是将视觉设计与应用程序数据结构分开的理想模式,所以当您说“MVVM 模式不允许将事件处理程序(用于鼠标交互)放在视图代码隐藏中”时,也许就是这样太激烈了。尝试坚持它以获得更好的设计,但一开始不要太疯狂。看看 this page,它可能会引起您的兴趣。
WPF 在后面的代码中自动为您创建事件处理程序,因此这是它们的位置。您可能想要做的是,不是直接在事件处理程序中修改数据,而是会有一个 ViewModel,其方法将帮助您修改数据。
您可以使用与 Canvas 关联的 PreviewMouse(Up,Down,Move) 事件。这可能比尝试单击您的形状更容易,特别是当多个形状可能重叠时。显然,如果你有很多形状,必须有一种方法可以知道要编辑哪些形状(通过组合框手动选择,找到鼠标点击的闭合形状等)
从您的设计来看,可能会给您带来麻烦的一个方面是您的 viewModel 管理 1 个形状。这没有错,但您需要另一个管理形状集合的 VM 层。它可以作为另一个 class 或 MyRectViewModel
:
void MyCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.StoreMouseDown(e);
}
void MyCanvas_PreviewMouseMove(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.ModifyShapes(e);
}
void MyCanvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
MyShapeCollectionViewModel.ModifyShapes(e);
}
其中ModifyShapes()
将计算鼠标拖动,找到要编辑的形状并调用其编辑方法。然后,一旦修改了形状数据,就会触发相应的事件来更新关联的View