【问题标题】:How to migrate crop coding from code behind to view model wpf如何将裁剪编码从后面的代码迁移到查看模型 wpf
【发布时间】:2015-03-02 12:53:59
【问题描述】:

作为 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 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 个功能将在被裁剪的图像顶部绘制裁剪矩形。我确实有一个裁剪按钮,一旦矩形完成,该按钮将被启用,并且该按钮将绑定到另一个功能,该功能将是实际的裁剪功能。

【问题讨论】:

    标签: c# wpf xaml mvvm


    【解决方案1】:

    这比您想象的要简单。

    您正在做的是用户定义行为的 UserControl。因此,您无需将该 XAML 放入您的页面/视图中,而是实现您自己的 Control,它派生自 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(...) 和依赖属性。

    然后你可以在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 给出的 Rect 进行裁剪 => 演示/业务逻辑(因此是 ViewModel)

    这样做,您可以在其他应用程序中重复使用您的CropControl。如果您将“selectionRect”绘图代码放入您的 ViewModel(这可能是可能的,但会导致代码难以阅读和维护),那么您不能在其他应用程序中重用它,因为您的 ViewModel 是特定于您的应用程序的。

    希望对您有所帮助。

    【讨论】:

    • 首先,Rect 结构已经包含了所需的属性。其次,MVVM 模式是一种表示模型模式,因此说“您不必为应用程序控件遵循 MVVM”是一个大错误:UI 控件位于 MVVM 模式的视图端,它们是其中的一部分。
    • 当您创建用户控件时,不应将 MVVM 应用于 UserControl。控件本身可以是 MVVM 的一部分。但是您不会将 MVVM 应用于 UserControl 本身
    • 创建 UserControl 时,不会像开发应用程序时那样将其逻辑拆分为 ViewModel。 @mcvanta 在他的步骤中试图做的正是:他试图将 MVVM 应用于“CropControl”,它不适用于控件。这就是为什么他想将他的代码隐藏代码移动到 ViewModel 中,这就是他卡住的地方
    • 当人们尝试在 UserControls 上使用依赖注入,然后用该 UserControl 的逻辑“注入”他们的 ViewModel 时,也会发生同样的问题。不上班
    • 对您的答案投反对票的人显然不了解 WPF 中的 UserControls。我完全同意你的看法,因此我 +1。
    【解决方案2】:

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

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

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

    【讨论】:

    • 我展示的示例只是程序的一部分,它具有裁剪、保存、导出和导入图像等多种不同功能。目前,我的视图模型文件夹中有一个带有两个委托(执行和可以执行)和视图模型类的中继命令。在我的视图模型中唯一起作用的是导入图像功能。我正在尝试一次迁移或使用一个功能,因为我对 wpf 和 mvvm 还很陌生,而且我确信我很容易在这个过程中迷失方向。我要检索的是在被裁剪的图像上绘制矩形的鼠标坐标。
    猜你喜欢
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多