我希望用户能够在画布中自由移动项目. 我的应用程序正在使用Caliburn.Micro. 如果Items,我的MainViewModel有一个集合: public BindableCollectionItemViewModel Items { get; set; } 我通过ItemsControl在画布中显
我的应用程序正在使用Caliburn.Micro.
如果Items,我的MainViewModel有一个集合:
public BindableCollection<ItemViewModel> Items { get; set; }
我通过ItemsControl在画布中显示:
<ItemsControl x:Name="Items"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas Background="#FFCADEEF" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter"> <Setter Property="Canvas.Left" Value="{Binding Path=X}" /> <Setter Property="Canvas.Top" Value="{Binding Path=Y}" /> <Setter Property="Width" Value="{Binding Path=Width}" /> <Setter Property="Height" Value="{Binding Path=Height}" /> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <Border Background="{Binding Path=BackgroundColor}"> <Rectangle> <Rectangle.Fill> <VisualBrush Visual="{StaticResource appbar_cursor_move}" /> </Rectangle.Fill> </Rectangle> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
我已经成功地将事件(现在什么都不做)绑定到MouseLeftButtonDown,MouseLeftButtonUp和MouseMove,但我不知道如何从viewmodel获取光标的位置.
我已经使用了你的行为,并改变了一些东西,使其更加MVVM’y:<Window x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"> <ItemsControl ItemsSource="{Binding Path=Shapes}"> <ItemsControl.Resources> <DataTemplate DataType="{x:Type local:Rectangle}"> <Rectangle Canvas.Top="{Binding Top, Mode=TwoWay}" Canvas.Left="{Binding Left, Mode=TwoWay}" Width="{Binding Width}" Height="{Binding Height}" Fill="Red"> <i:Interaction.Behaviors> <local:DragBehavior/> </i:Interaction.Behaviors> </Rectangle> </DataTemplate> <DataTemplate DataType="{x:Type local:Circle}"> <Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Blue" Canvas.Top="{Binding Top, Mode=TwoWay}" Canvas.Left="{Binding Left, Mode=TwoWay}"> <i:Interaction.Behaviors> <local:DragBehavior/> </i:Interaction.Behaviors> </Ellipse> </DataTemplate> </ItemsControl.Resources> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter"> <Setter Property="Canvas.Top" Value="{Binding Path=Top, Mode=TwoWay}" /> <Setter Property="Canvas.Left" Value="{Binding Path=Left, Mode=TwoWay}" /> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> </Window>
this是我使用样式绑定到Canvas.Top和Left的原因.
这是我的ViewModel.我使用ReactiveUI for IPropertyChanged,但这并不重要.
public class MainViewModel : ReactiveObject { private ReactiveList<IShape> _shapes; public MainViewModel() { Shapes = new ReactiveList<IShape>(); Shapes.Add(new Rectangle { Top = 50, Left = 50, Height = 50, Width = 50 }); Shapes.Add(new Circle{Top = 100, Left = 100, Radius = 50}); } public ReactiveList<IShape> 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); } } }
我为反应角和圆创建了类,因为MVVM的重点在于区分层.在ViewModel中持有UI控件是违背这个想法的.
最后,我不得不改变你的MouseLeftButtonUp:
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; };
这将从RenderTransform进行更改并将其写入对象.然后,双向绑定将其转移到我们的Rectangle类中.
如果您想知道对象的位置,则只需要这样做,例如检查它们是否在VM中相交.
它工作得很好,就像你可以得到的MVVM一样.也许除了行var parent = Application.Current.MainWindow; – 这应该取代我认为绑定到您的行为的公共依赖属性.