Move items in a canvas using MVVM

后端 未结 4 1366
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-03 11:28

I want the user to be able to move items freely in a canvas.
My app is using Caliburn.Micro.

My MainViewModel has a collection if Items :

public          


        
4条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-03 12:04

    I've used your behaviour and changed few things to make it more MVVM'y:

    
        
            
                
                    
                        
                            
                        
                    
                
                
                    
                        
                            
                        
                    
                
            
            
                
                    
                
            
            
                
            
        
    
    

    this is why I used a style for binding to Canvas.Top and Left.

    This is my ViewModel. I used ReactiveUI for IPropertyChanged, but this doesn't matter really.

     public class MainViewModel : ReactiveObject
        {
            private ReactiveList _shapes;
    
            public MainViewModel()
            {
                Shapes = new ReactiveList();
    
                Shapes.Add(new Rectangle { Top = 50, Left = 50, Height = 50, Width = 50 });
                Shapes.Add(new Circle{Top = 100, Left = 100, Radius = 50});
            }
    
            public ReactiveList Shapes
            {
                get { return _shapes; }
                set { this.RaiseAndSetIfChanged(ref _shapes, value); }
            }
        }
    
        public interface IShape
        {
            int Top { get; set; }
            int Left { get; set; }
        }
    
        public abstract class Shape : ReactiveObject, IShape
        {
            private int _top;
            private int _left;
    
            public int Top
            {
                get { return _top; }
                set { this.RaiseAndSetIfChanged(ref _top, value); }
            }
    
            public int Left
            {
                get { return _left; }
                set { this.RaiseAndSetIfChanged(ref _left, value); }
            }
        }
    
        public class Circle : Shape
        {
            private int _radius;
    
            public int Radius
            {
                get { return _radius; }
                set { this.RaiseAndSetIfChanged(ref _radius, value); }
            }
        }
    
        public class Rectangle : Shape
        {
            private int _width;
            private int _height;
    
            public int Width
            {
                get { return _width; }
                set { this.RaiseAndSetIfChanged(ref _width, value); }
            }
    
            public int Height
            {
                get { return _height; }
                set { this.RaiseAndSetIfChanged(ref _height, value); }
            }
        }
    

    I created classes for reactangles and circles, because the whole point of MVVM is to make distinction between layers. Holding UI controls in ViewModel is deffinetely against the idea.

    Lastly, I had to change your MouseLeftButtonUp a little:

     AssociatedObject.MouseLeftButtonUp += (sender, e) =>
                {
                    AssociatedObject.ReleaseMouseCapture();
    
                    var diff = e.GetPosition(parent) - mouseStartPosition;
    
    
                    Canvas.SetTop(AssociatedObject, ElementStartPosition.Y + diff.Y);
                    Canvas.SetLeft(AssociatedObject, ElementStartPosition.X + diff.X);
    
                    transform.Y = 0;
                    transform.X = 0;
                };
    

    This takes changes from RenderTransform and writes them into object. Then, two way binding takes it down into our Rectangle class.

    This is needed only,if you want to know where objects are, for example to check if they intersect in VM.

    It works quite well, and is as MVVM as you can get. Maybe with exception of line var parent = Application.Current.MainWindow; - this should replaced I think with binding to public dependency property of your behaviour.

提交回复
热议问题