我想在画布上包裹一些矩形。矩形的详细信息位于我的视图模型中的列表中。我成功绘制了矩形,但未能使它们可移动。在我的事件处理程序 Canvas.GetLeft() 和 Canvas.SetLeft 中不工作。
这是我的 ItemsControl:
<ItemsControl Width="1920"
Height="1080"
ItemsSource="{Binding Controls,Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<UniformGrid Rows="2"
PreviewMouseUp="Container_Control_MouseUp"
PreviewMouseMove="Container_Control_MouseMove"
PreviewMouseDown="Container_Control_MouseDown">
<Rectangle Width="{Binding Width}"
Height="{Binding Height}"
Fill="Blue" />
<TextBlock Grid.Row="1"
Text="move it" />
</UniformGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top"
Value="{Binding X}" />
<Setter Property="Canvas.Left"
Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
这是我的事件处理程序,它们不适用于项目控件中的元素:
public partial class ControlsView : UserControl
{
bool _isMouseDown = false;
Point _mouseDownPosition;
Point _mouseDownControlPosition;
public ControlsView(ControlsViewModel vm)
{
this.DataContext = vm;
InitializeComponent();
}
private void Container_Control_MouseUp(object sender, MouseButtonEventArgs e)
{
var c = sender as UIElement;
_isMouseDown = false;
c.ReleaseMouseCapture();
}
private void Container_Control_MouseMove(object sender, MouseEventArgs e)
{
if (_isMouseDown)
{
var c = sender as UIElement;
var pos = e.GetPosition(this);
var dp = pos - _mouseDownPosition;
Canvas.SetLeft(c, _mouseDownControlPosition.X + dp.X);
Canvas.SetTop(c, _mouseDownControlPosition.Y + dp.Y);
}
}
private void Container_Control_MouseDown(object sender, MouseButtonEventArgs e)
{
var c = sender as UIElement;
_isMouseDown = true;
_mouseDownPosition = e.GetPosition(this);
_mouseDownControlPosition = new Point(double.IsNaN(Canvas.GetLeft(c)) ? 0 : Canvas.GetLeft(c), double.IsNaN(Canvas.GetTop(c)) ? 0 : Canvas.GetTop(c));
c.CaptureMouse();
}
}
在这种情况下,项目容器不是 ItemsControl.ItemTemplate 中定义的
UniformGrid
,而是托管 ContentPresenter
的 UniformGrid
。这意味着 Canvas 的子项不是 UniformGrid
而是 ContentPresenter
。因此,当您获取或设置 Canvas.Left 或 Canvas.Top 时,您需要处理这个ContentPresenter
。
您可以按如下方式从
ContentPresenter
获取UniformGrid
。
var uniformGrid = sender as UIElement;
var contentPresenter = VisualTreeHelper.GetParent(uniformGrid) as UIElement;
UniformGrid
不是Canvas
的直接子代。您必须将鼠标事件处理程序附加到项目容器。您可以使用 ItemsControl.ItemContainerStyle
使用 EventSetter
来注册它们:
<ItemsControl Width="1920"
Height="1080"
ItemsSource="{Binding Controls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<UniformGrid Rows="2">
<Rectangle Width="{Binding Width}"
Height="{Binding Height}"
Fill="Blue" />
<TextBlock Grid.Row="1"
Text="move it" />
</UniformGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="ItemContainer_PreviewMouseLeftButtonDown" />
<EventSetter Event="PreviewMouseMove"
Handler="ItemContainer_PreviewMouseMove" />
<Setter Property="Canvas.Top"
Value="{Binding X}" />
<Setter Property="Canvas.Left"
Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
您还可以简化您的算法:
// The initial mouse offset relative to the container position
private Point mousePositionOffset;
private void ItemContainer_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_ = Mouse.Capture(sender as IInputElement);
this.mousePositionOffset = e.GetPosition(sender as IInputElement);
}
private void ContentPresenter_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton is not MouseButtonState.Pressed
|| sender is not UIElement itemContainer)
{
Mouse.Capture(null);
return;
}
var canvas = (Canvas)VisualTreeHelper.GetParent(itemContainer);
Point currentMousePosition = e.GetPosition(canvas);
Canvas.SetLeft(itemContainer, currentMousePosition.X - this.mousePositionOffset.X);
Canvas.SetTop(itemContainer, currentMousePosition.Y - this.mousePositionOffset.Y);
}