如何更改 ItemsControl 中项目的 Canvas.left?

问题描述 投票:0回答:2

我想在画布上包裹一些矩形。矩形的详细信息位于我的视图模型中的列表中。我成功绘制了矩形,但未能使它们可移动。在我的事件处理程序 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();
        }
    }
wpf canvas itemscontrol
2个回答
0
投票

在这种情况下,项目容器不是 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;

0
投票

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);
}
© www.soinside.com 2019 - 2024. All rights reserved.