如何将crop编码从代码背后迁移到视图模型wpf

本文关键字:迁移 视图 模型 wpf 背后 代码 crop 编码 | 更新日期: 2024-09-22 06:24:13

作为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坐标以及矩形的宽度和高度(称为选择矩形)。我想在我的视图模型中创建画布和矩形,但这将违反mvvm结构。我读到我可以使用附加的属性,但不熟悉它。关于MVVM模式,最好的处理方式是什么。目前,我正在阅读Adan Nathan WPF released 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个功能一旦我弄清楚如何使其工作,就会在被裁剪的图像上绘制裁剪矩形。我确实有一个裁剪按钮,一旦矩形完成,它就会被启用,这个按钮将被绑定到另一个实际裁剪功能上。

如何将crop编码从代码背后迁移到视图模型wpf

这比你想象的要简单。

您正在做的是一个用户定义行为的UserControl。因此,与其将XAML放入您的Page/View中,不如实现您自己的控件,该控件派生自UserControl,并像在代码后面那样实现您的代码。

既然你正在制作一个自定义控件,你就不必遵循MVVM。事实上,用户控件的MVVM模式是不鼓励的。在您定义的自定义控件中,可能会定义一个包含"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(...)和Dependency属性。

然后可以将其绑定到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
        ... 
    }
}

图形选择=视图逻辑(因此视图)用CropControl=>Presentation/Business Logic(so ViewModel)给出的矩形裁剪

这样,您就可以在其他应用程序中重用CropControl。如果将"selectionRect"绘图代码放入ViewModel中(这可能是可能的,但会导致代码难以读取和维护),则无法在其他应用程序中重用它,因为ViewModel是特定于应用程序的。

希望能有所帮助。

MVVM意味着将View与ViewModel分离。您给出的示例通常是一个仅查看代码。

你的例子似乎是一种选择工具,我推断你想取回所选的内容,或者至少是裁剪坐标。因此,最好的方法是在一个自定义控件中转换代码,该控件为裁剪坐标公开一个RectDependencyProperty,在视图模型中,应该公开一个包含裁剪矩形坐标的Rect属性,然后将其绑定到裁剪控件DepencyProperty。

该视图是关于与视觉方面交互的。ViewModel是关于保存和处理视图所使用的数据。