我希望用户能够在画布中自由移动项目. 我的应用程序正在使用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; – 这应该取代我认为绑定到您的行为的公共依赖属性.
