如何将裁剪代码从代码隐藏迁移到视图模型 wpf
How to migrate crop coding from code behind to view model wpf
作为 WPF 和 MVVM 的新手,我到处搜索以找到解决我的问题的好方法。我正在创建一个裁剪应用程序,但我正在尝试将代码隐藏代码迁移到视图模型。我能够通过使用混合交互触发器代码绑定我的鼠标按钮事件如下:
<Grid x:Name="GridLoadedImage" HorizontalAlignment="Left" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding MouseLeftButtonDownCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding MouseLeftButtonUpCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding MouseMoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=slider1, Path=Value}" ScaleY="{Binding ElementName=slider1, Path=Value}"/>
</Grid.LayoutTransform>
<Image x:Name="LoadedImage" Margin="10" Source="{Binding ImagePath}"/>
<Canvas x:Name="BackPanel" Margin="10">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed"/>
</Canvas>
</Grid>
现在我的难题是如何从下面显示的代码中迁移我使用的实际代码:
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
Canvas.SetZIndex(selectionRectangle, BackPanel.Children.Count);
isDragging = true;
BackPanel.Cursor = Cursors.Cross;
}
}
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
Crop.IsEnabled = true;
Cut.IsEnabled = true;
BackPanel.Cursor = Cursors.Arrow;
}
}
}
如您所见,我需要能够访问 x 和 y 坐标以及矩形(称为选择矩形)的宽度和高度。我正在考虑在我的视图模型中创建 canvas 和矩形,但这会违反 mvvm 结构。我读到我可以使用附加属性但不熟悉它。关于 MVVM 模式,处理此问题的最佳方法是什么。目前我正在阅读 Adan Nathan WPF unleashed 4 的书,这本书对像我这样的初学者来说是一本好书,但我似乎找不到与我的问题相关的任何内容。感谢您的帮助
我有鼠标事件的视图模型代码:
#region MouseLeftButtonDown
private bool isDragging = false;
private Point anchorPoint = new Point();
private ICommand _mouseLeftButtonDownCommand;
public ICommand MouseLeftButtonDownCommand
{
get
{
if (_mouseLeftButtonDownCommand == null)
{
_mouseLeftButtonDownCommand = new RelayCommand(param => MouseLeftButtonDown());
}
return _mouseLeftButtonDownCommand;
}
}
public void MouseLeftButtonDown()
{
if (isDragging == false)
{
MessageBox.Show("THis is Mouse Down");
//anchorPoint.X = e.GetPosition(BackPanel).X;
//anchorPoint.Y = e.GetPosition(BackPanel).Y;
isDragging = true;
}
}
#endregion
#region MouseLeftButtonUp
private ICommand _mouseLeftButtonUpCommand;
public ICommand MouseLeftButtonUpCommand
{
get
{
if (_mouseLeftButtonUpCommand == null)
{
_mouseLeftButtonUpCommand = new RelayCommand(param => MouseLeftButtonUp((MouseButtonEventArgs)param));
}
return _mouseLeftButtonUpCommand;
}
}
public void MouseLeftButtonUp(MouseButtonEventArgs e)
{
if (isDragging)
{
MessageBox.Show(e.Source.ToString());
isDragging = false;
//if (selectionRectangle.Width > 0)
//{
// Crop.IsEnabled = true;
// Cut.IsEnabled = true;
// BackPanel.Cursor = Cursors.Arrow;
//}
}
}
#endregion
#region MouseMove
private ICommand _mouseMoveCommand;
public ICommand MouseMoveCommand
{
get
{
if (_mouseMoveCommand == null)
{
_mouseMoveCommand = new RelayCommand(param => MouseMove());
}
return _mouseMoveCommand;
}
}
public void MouseMove()
{
if (isDragging)
{
//MessageBox.Show("THis is Mouse Move");
//double x = e.GetPosition(BackPanel).X;
//double y = e.GetPosition(BackPanel).Y;
//selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
//selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
//selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
//selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
//if (selectionRectangle.Visibility != Visibility.Visible)
// selectionRectangle.Visibility = Visibility.Visible;
}
}
#endregion
我只是评论了实际代码并将其替换为消息框,只是为了测试我的触发器是否正常工作。一旦我弄清楚如何使它起作用,这 3 个函数就会在被裁剪的图像顶部绘制裁剪矩形。我确实有一个裁剪按钮,矩形完成后将启用该按钮,并且该按钮将绑定到另一个功能,该功能将是实际的裁剪功能。
MVVM的意思是View和ViewModel分离。您给出的示例通常是仅查看代码。
您的示例似乎是一种选择工具,我推断您想要取回所选内容,或者至少是裁剪坐标。所以最好的办法是在自定义控件中转换您的代码,公开一个 Rect
DependencyProperty for the crop coordinates, and in your view model, you should expose a Rect
属性 保存裁剪矩形坐标,然后将其绑定到您的裁剪控件 DepencyProperty。
视图是关于与视觉方面的交互。 ViewModel 是关于保存和处理视图使用的数据。
这比您想象的要简单。
您正在做的是用户定义行为的用户控件。因此,与其将 XAML 放入您的 Page/View,不如实现您自己的从 UserControl
派生的控件,并像您在代码隐藏中那样实现您的代码。
由于您是在制作自定义控件,因此您不必遵循 MVVM。事实上,不鼓励用户控件使用 MVVM 模式。在您定义的自定义控件中,您可以定义一个 Dependency 属性,它包含一个 "SelectionRect" 类型的对象(您不应该使用 Rect,因为它是一个结构,并且它不能很好地与数据绑定一起工作,因为它每次更改时都会创建一个新副本)。
public class CropControl : UserControl
{
public Rect Selection
{
get { return (Rect)GetValue(SelectionProperty); }
set { SetValue(SelectionProperty, value); }
}
public static readonly DependencyProperty SelectionProperty =
DependencyProperty.Register("Selection", typeof(Rect), typeof(CropControl), new PropertyMetadata(default(Rect)));
// this is used, to react on changes from ViewModel. If you assign a
// new Rect in your ViewModel you will have to redraw your Rect here
private static void OnSelectionChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
{
Rect newRect = (Rect)e.NewValue;
Rectangle selectionRectangle = d as Rectangle;
if(selectionRectangle!=null)
return;
selectionRectangle.SetValue(Canvas.LeftProperty, newRect.X);
selectionRectangle.SetValue(Canvas.TopProperty, newRect.Y);
selectionRectangle.Width = newRect.Width;
selectionRectangle.Height = newRect.Height;
}
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
Canvas.SetZIndex(selectionRectangle, BackPanel.Children.Count);
isDragging = true;
BackPanel.Cursor = Cursors.Cross;
}
}
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
Crop.IsEnabled = true;
Cut.IsEnabled = true;
BackPanel.Cursor = Cursors.Arrow;
}
// Set the Selection to the new rect, when the mouse button has been released
Selection = new Rect(
selectionRectangle.GetValue(Canvas.LeftProperty),
selectionRectangle.GetValue(Canvas.TopProperty),
selectionRectangle.Width,
selectionRectangle.Height);
}
}
}
请注意,唯一的更改是添加 Selection = new Rect(...)
和依赖项 属性。
然后就可以在XAML里绑定了。
<my:CropControl Selection="{Binding Selection,Mode=TwoWay}"/>
更新:
您的 ViewModel 看起来像
public class MyViewModel : ViewModel
{
private Rect selection;
public Rect Selection
{
get
{
return selection;
}
set
{
selection = value;
// Or whatever the name of your framework/implementation the method is called
OnPropertyChanged("Selection");
// Cause ICommands to reevaluate their CanExecute methods
CommandManager.InvalidateRequerySuggested();
}
}
private ICommand cropCommand;
public ICommand CropCommand {
get
{
if(cropCommand==null)
cropCommand = new RelayCommand(Crop, () => Selection.Width > 0); // only allow execution when Selection width > 0
return cropCommand;
}
}
public void Crop()
{
// Get a copy of the selection in case it changes during execution
Rect cropSelection = Selection;
// use it to crop your image
...
}
}
绘图选择=查看逻辑(So View)
使用 CropControl
=> Presentation/Business 逻辑给出的 Rect 进行裁剪(因此 ViewModel)
这样做可以让您在其他应用程序中重复使用 CropControl
。如果您将 "selectionRect" 绘图代码放入您的 ViewModel(这可能是可能的,但会导致难以阅读和维护代码),那么您不能在其他应用程序中重用它,因为您的 ViewModels 是特定于您的应用程序的。
希望对您有所帮助。
作为 WPF 和 MVVM 的新手,我到处搜索以找到解决我的问题的好方法。我正在创建一个裁剪应用程序,但我正在尝试将代码隐藏代码迁移到视图模型。我能够通过使用混合交互触发器代码绑定我的鼠标按钮事件如下:
<Grid x:Name="GridLoadedImage" HorizontalAlignment="Left" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding MouseLeftButtonDownCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding MouseLeftButtonUpCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{Binding MouseMoveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=slider1, Path=Value}" ScaleY="{Binding ElementName=slider1, Path=Value}"/>
</Grid.LayoutTransform>
<Image x:Name="LoadedImage" Margin="10" Source="{Binding ImagePath}"/>
<Canvas x:Name="BackPanel" Margin="10">
<Rectangle x:Name="selectionRectangle" Stroke="LightBlue" Fill="#220000FF" Visibility="Collapsed"/>
</Canvas>
</Grid>
现在我的难题是如何从下面显示的代码中迁移我使用的实际代码:
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
Canvas.SetZIndex(selectionRectangle, BackPanel.Children.Count);
isDragging = true;
BackPanel.Cursor = Cursors.Cross;
}
}
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
Crop.IsEnabled = true;
Cut.IsEnabled = true;
BackPanel.Cursor = Cursors.Arrow;
}
}
}
如您所见,我需要能够访问 x 和 y 坐标以及矩形(称为选择矩形)的宽度和高度。我正在考虑在我的视图模型中创建 canvas 和矩形,但这会违反 mvvm 结构。我读到我可以使用附加属性但不熟悉它。关于 MVVM 模式,处理此问题的最佳方法是什么。目前我正在阅读 Adan Nathan WPF unleashed 4 的书,这本书对像我这样的初学者来说是一本好书,但我似乎找不到与我的问题相关的任何内容。感谢您的帮助
我有鼠标事件的视图模型代码:
#region MouseLeftButtonDown
private bool isDragging = false;
private Point anchorPoint = new Point();
private ICommand _mouseLeftButtonDownCommand;
public ICommand MouseLeftButtonDownCommand
{
get
{
if (_mouseLeftButtonDownCommand == null)
{
_mouseLeftButtonDownCommand = new RelayCommand(param => MouseLeftButtonDown());
}
return _mouseLeftButtonDownCommand;
}
}
public void MouseLeftButtonDown()
{
if (isDragging == false)
{
MessageBox.Show("THis is Mouse Down");
//anchorPoint.X = e.GetPosition(BackPanel).X;
//anchorPoint.Y = e.GetPosition(BackPanel).Y;
isDragging = true;
}
}
#endregion
#region MouseLeftButtonUp
private ICommand _mouseLeftButtonUpCommand;
public ICommand MouseLeftButtonUpCommand
{
get
{
if (_mouseLeftButtonUpCommand == null)
{
_mouseLeftButtonUpCommand = new RelayCommand(param => MouseLeftButtonUp((MouseButtonEventArgs)param));
}
return _mouseLeftButtonUpCommand;
}
}
public void MouseLeftButtonUp(MouseButtonEventArgs e)
{
if (isDragging)
{
MessageBox.Show(e.Source.ToString());
isDragging = false;
//if (selectionRectangle.Width > 0)
//{
// Crop.IsEnabled = true;
// Cut.IsEnabled = true;
// BackPanel.Cursor = Cursors.Arrow;
//}
}
}
#endregion
#region MouseMove
private ICommand _mouseMoveCommand;
public ICommand MouseMoveCommand
{
get
{
if (_mouseMoveCommand == null)
{
_mouseMoveCommand = new RelayCommand(param => MouseMove());
}
return _mouseMoveCommand;
}
}
public void MouseMove()
{
if (isDragging)
{
//MessageBox.Show("THis is Mouse Move");
//double x = e.GetPosition(BackPanel).X;
//double y = e.GetPosition(BackPanel).Y;
//selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
//selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
//selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
//selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
//if (selectionRectangle.Visibility != Visibility.Visible)
// selectionRectangle.Visibility = Visibility.Visible;
}
}
#endregion
我只是评论了实际代码并将其替换为消息框,只是为了测试我的触发器是否正常工作。一旦我弄清楚如何使它起作用,这 3 个函数就会在被裁剪的图像顶部绘制裁剪矩形。我确实有一个裁剪按钮,矩形完成后将启用该按钮,并且该按钮将绑定到另一个功能,该功能将是实际的裁剪功能。
MVVM的意思是View和ViewModel分离。您给出的示例通常是仅查看代码。
您的示例似乎是一种选择工具,我推断您想要取回所选内容,或者至少是裁剪坐标。所以最好的办法是在自定义控件中转换您的代码,公开一个 Rect
DependencyProperty for the crop coordinates, and in your view model, you should expose a Rect
属性 保存裁剪矩形坐标,然后将其绑定到您的裁剪控件 DepencyProperty。
视图是关于与视觉方面的交互。 ViewModel 是关于保存和处理视图使用的数据。
这比您想象的要简单。
您正在做的是用户定义行为的用户控件。因此,与其将 XAML 放入您的 Page/View,不如实现您自己的从 UserControl
派生的控件,并像您在代码隐藏中那样实现您的代码。
由于您是在制作自定义控件,因此您不必遵循 MVVM。事实上,不鼓励用户控件使用 MVVM 模式。在您定义的自定义控件中,您可以定义一个 Dependency 属性,它包含一个 "SelectionRect" 类型的对象(您不应该使用 Rect,因为它是一个结构,并且它不能很好地与数据绑定一起工作,因为它每次更改时都会创建一个新副本)。
public class CropControl : UserControl
{
public Rect Selection
{
get { return (Rect)GetValue(SelectionProperty); }
set { SetValue(SelectionProperty, value); }
}
public static readonly DependencyProperty SelectionProperty =
DependencyProperty.Register("Selection", typeof(Rect), typeof(CropControl), new PropertyMetadata(default(Rect)));
// this is used, to react on changes from ViewModel. If you assign a
// new Rect in your ViewModel you will have to redraw your Rect here
private static void OnSelectionChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)
{
Rect newRect = (Rect)e.NewValue;
Rectangle selectionRectangle = d as Rectangle;
if(selectionRectangle!=null)
return;
selectionRectangle.SetValue(Canvas.LeftProperty, newRect.X);
selectionRectangle.SetValue(Canvas.TopProperty, newRect.Y);
selectionRectangle.Width = newRect.Width;
selectionRectangle.Height = newRect.Height;
}
private void LoadedImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (isDragging == false)
{
anchorPoint.X = e.GetPosition(BackPanel).X;
anchorPoint.Y = e.GetPosition(BackPanel).Y;
Canvas.SetZIndex(selectionRectangle, BackPanel.Children.Count);
isDragging = true;
BackPanel.Cursor = Cursors.Cross;
}
}
private void LoadedImage_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging)
{
double x = e.GetPosition(BackPanel).X;
double y = e.GetPosition(BackPanel).Y;
selectionRectangle.SetValue(Canvas.LeftProperty, Math.Min(x, anchorPoint.X));
selectionRectangle.SetValue(Canvas.TopProperty, Math.Min(y, anchorPoint.Y));
selectionRectangle.Width = Math.Abs(x - anchorPoint.X);
selectionRectangle.Height = Math.Abs(y - anchorPoint.Y);
if (selectionRectangle.Visibility != Visibility.Visible)
selectionRectangle.Visibility = Visibility.Visible;
}
}
private void LoadedImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (isDragging)
{
isDragging = false;
if (selectionRectangle.Width > 0)
{
Crop.IsEnabled = true;
Cut.IsEnabled = true;
BackPanel.Cursor = Cursors.Arrow;
}
// Set the Selection to the new rect, when the mouse button has been released
Selection = new Rect(
selectionRectangle.GetValue(Canvas.LeftProperty),
selectionRectangle.GetValue(Canvas.TopProperty),
selectionRectangle.Width,
selectionRectangle.Height);
}
}
}
请注意,唯一的更改是添加 Selection = new Rect(...)
和依赖项 属性。
然后就可以在XAML里绑定了。
<my:CropControl Selection="{Binding Selection,Mode=TwoWay}"/>
更新: 您的 ViewModel 看起来像
public class MyViewModel : ViewModel
{
private Rect selection;
public Rect Selection
{
get
{
return selection;
}
set
{
selection = value;
// Or whatever the name of your framework/implementation the method is called
OnPropertyChanged("Selection");
// Cause ICommands to reevaluate their CanExecute methods
CommandManager.InvalidateRequerySuggested();
}
}
private ICommand cropCommand;
public ICommand CropCommand {
get
{
if(cropCommand==null)
cropCommand = new RelayCommand(Crop, () => Selection.Width > 0); // only allow execution when Selection width > 0
return cropCommand;
}
}
public void Crop()
{
// Get a copy of the selection in case it changes during execution
Rect cropSelection = Selection;
// use it to crop your image
...
}
}
绘图选择=查看逻辑(So View)
使用 CropControl
=> Presentation/Business 逻辑给出的 Rect 进行裁剪(因此 ViewModel)
这样做可以让您在其他应用程序中重复使用 CropControl
。如果您将 "selectionRect" 绘图代码放入您的 ViewModel(这可能是可能的,但会导致难以阅读和维护代码),那么您不能在其他应用程序中重用它,因为您的 ViewModels 是特定于您的应用程序的。
希望对您有所帮助。