上饶网站建设推广,站内seo优化,设计师必备的网站有哪些,网站信用认证可以自己做吗在 WPF 应用程序中#xff0c;拖放操作是实现用户交互的重要组成部分。通过拖放操作#xff0c;用户可以轻松地将数据从一个位置移动到另一个位置#xff0c;或者将控件从一个容器移动到另一个容器。然而#xff0c;WPF 中默认的拖放操作可能并不是那么好用。为了解决这个问…在 WPF 应用程序中拖放操作是实现用户交互的重要组成部分。通过拖放操作用户可以轻松地将数据从一个位置移动到另一个位置或者将控件从一个容器移动到另一个容器。然而WPF 中默认的拖放操作可能并不是那么好用。为了解决这个问题我们可以自定义一个 Panel 来实现更简单的拖拽操作。 自定义 Panel 的优点有很多。首先我们可以根据自己的需求来设计 Panel 的外观和行为。其次我们可以使用代码来控制拖放操作的细节比如拖放的开始和结束位置、拖放过程中控件的显示方式等等。最后我们可以将自定义 Panel 作为一个控件方便地应用到不同的应用程序中。 在本教程中我们将一步一步地创建一个自定义 Panel 来实现更简单的拖拽操作。我们将学习如何定义 Panel 的布局、如何处理拖放事件以及如何将自定义 Panel 应用到不同的应用程序中。按照本教程的步骤操作您将能够创建一个功能强大且易于使用的自定义 Panel从而使您的 WPF 应用程序更加友好和易用。 1.定义一个继承自Panel的类。
public class DragStackPanel : Panel
{/// summary/// 获取或设置方向/// /summarypublic Orientation Orientation{get { return (Orientation)GetValue(OrientationProperty); }set { SetValue(OrientationProperty, value); }}public static readonly DependencyProperty OrientationProperty DependencyProperty.Register(Orientation, typeof(Orientation), typeof(DragStackPanel), new PropertyMetadata(Orientation.Vertical));
} 2.重写Panel类的MeasureOverride方法测量控件Size。
public class DragStackPanel : Panel
{protected override Size MeasureOverride(Size availableSize){var panelDesiredSize new Size();foreach (UIElement child in InternalChildren){child.Measure(availableSize);if (this.Orientation Orientation.Horizontal){panelDesiredSize.Width child.DesiredSize.Width;panelDesiredSize.Height double.IsInfinity(availableSize.Height) ? child.DesiredSize.Height : availableSize.Height;}else{panelDesiredSize.Width double.IsInfinity(availableSize.Width) ? child.DesiredSize.Width : availableSize.Width;panelDesiredSize.Height child.DesiredSize.Height;}}return panelDesiredSize;}
} 3.重写Panel类的ArrangeOverride方法排列控件位置。
public class DragStackPanel : Panel
{protected override Size ArrangeOverride(Size finalSize){double x 0, y 0;foreach (FrameworkElement child in InternalChildren){// 坐标var position new Point(x, y);// 宽度var width child.DesiredSize.Width;// 高度var height child.DesiredSize.Height;// 通过排列方向计算宽度和高度if (this.Orientation Orientation.Vertical){width finalSize.Width;}else{height finalSize.Height;}// 尺寸var size new Size(width, height);// 排列位置及尺寸child.Arrange(new Rect(position, size));// 计算位置if (this.Orientation Orientation.Horizontal){x child.DesiredSize.Width;}else{y child.DesiredSize.Height;}}return finalSize;}
} 查看运行效果
UniformGrid Rows2local:DragStackPanel OrientationHorizontalButtontest1/ButtonButtontest2/Button/local:DragStackPanellocal:DragStackPanel OrientationVerticalButtontest3/ButtonButtontest4/Button/local:DragStackPanel
/UniformGrid 4.重写PreviewMouseLeftButtonDown方法。 该方法在按下鼠标左键时触发我们需要在该方法中获取第一次按下鼠标的坐标并且通过命中测试找到我们要拖拽的控件最后还要在装饰层中添加一个元素该元素的背景用原控件的外观来填充VisualBrush这样就可以覆盖原来的控件以便在拖拽控件时能跨越控件的边界。以下为参考代码 public class DragStackPanel : Panel
{private FrameworkElement draggingElement;private Point mouseRelativePosition;private int draggingElementzIndex;protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e){// 获取鼠标相对于Panel的坐标var mousePosition e.GetPosition(this);// 通过命中测试获取当前鼠标位置下的元素var hitTestResult this.InputHitTest(mousePosition) as FrameworkElement;// 通过命中测试结果找到当前拖拽的控件子项draggingElement FindChild(hitTestResult);if (draggingElement ! null this.InternalChildren.Contains(draggingElement)){// 记录鼠标相对位置以供后续使用mouseRelativePosition e.GetPosition(draggingElement);// 暂存ZIndexdraggingElementzIndex Panel.GetZIndex(draggingElement);// 将ZIndex置顶Panel.SetZIndex(draggingElement, this.InternalChildren.Count);// 添加遮罩防止拖拽时覆盖AddOverlay(draggingElement);e.Handled true;}base.OnPreviewMouseLeftButtonDown(e);}
} 5.重写PreviewMouseMove方法。 该方法在鼠标移动时触发我们需要在鼠标被按下移动时根据当前的坐标与第一次按下的坐标实时计算出被拖拽元素的偏移量这样该元素就能跟随鼠标移动实现拖拽效果。以下为参考代码 public class DragStackPanel : Panel
{private FrameworkElement draggingElement;private Point mouseRelativePosition;private int draggingElementzIndex;protected override void OnPreviewMouseMove(MouseEventArgs e){var mousePosition e.GetPosition(this);if (e.LeftButton MouseButtonState.Pressed draggingElement ! null){// 当前拖拽控件置为不可鼠标命中以供命中下一层的换位控件draggingElement.IsHitTestVisible false;// 判断当前拖拽的控件是否为顶层控件if (Panel.GetZIndex(draggingElement) this.InternalChildren.Count){// 计算出当前拖拽控件相对于this的位置控件左上角var targetPosition new Point(mousePosition.X - mouseRelativePosition.X - draggingElement.Margin.Left, mousePosition.Y - mouseRelativePosition.Y - draggingElement.Margin.Top);// 获取当前拖拽控件在this中的原始位置var draggingElementOriginalPosition GetDraggingElementOriginalPosition(draggingElement);// 计算拖拽控件移动时的偏移量var offset new Point(targetPosition.X - draggingElementOriginalPosition.X, targetPosition.Y - draggingElementOriginalPosition.Y);// 应用位移draggingElement.RenderTransform new TranslateTransform(offset.X, offset.Y);}e.Handled true;}base.OnPreviewMouseMove(e);}
} 6.重写PreviewMouseLeftButtonUp方法。 该方法在鼠标左健抬起时触发我们需要在该方法中将一些参数重置。 public class DragStackPanel : Panel
{private FrameworkElement draggingElement;private Point mouseRelativePosition;private int draggingElementzIndex;protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e){mouseRelativePosition default;RemoveOverlay(draggingElement);Panel.SetZIndex(draggingElement, draggingElementzIndex);draggingElement.IsHitTestVisible true;draggingElement.RenderTransform null;draggingElement null;e.Handled true;base.OnPreviewMouseLeftButtonUp(e);}
} 以下为运行效果 7.处理控件的拖拽换位。 拖拽换位的思路就是将当前正在拖拽的元素放置到新的Index中并把该Index后面的所有元素整体后移一位。该功能在PreviewMouseMove方法中实现。 public class DragStackPanel : Panel
{private FrameworkElement draggingElement;private FrameworkElement hitElement;private Point mouseRelativePosition;private int draggingElementzIndex;protected override void OnPreviewMouseMove(MouseButtonEventArgs e){...// 命中当前拖拽控件的下一层控件var hitTestResult this.InputHitTest(mousePosition) as FrameworkElement;// 查找被命中的下一层换位控件hitElement FindChild(hitTestResult);// 判断是否有效if (hitElement ! null this.InternalChildren.Contains(hitElement)){// 应用换位MoveChild(draggingElement, hitElement);}}private void MoveChild(FrameworkElement element1, FrameworkElement element2){var index1 this.InternalChildren.IndexOf(element1);var index2 this.InternalChildren.IndexOf(element2);if (index1 0 index2 0){this.InternalChildren.RemoveAt(index1);this.InternalChildren.Insert(index2, element1);}}
} 在ArrangeOverride方法中处理重新排列时当前拖拽元素的坐标。
public class DragStackPanel : Panel
{private FrameworkElement draggingElement;private FrameworkElement hitElement;private Point mouseRelativePosition;private int draggingElementzIndex;protected override Size ArrangeOverride(Size finalSize){double x 0, y 0;foreach (FrameworkElement child in InternalChildren){...// 获取当前正在拖拽元素的位置坐标var dragElementPosition GetDraggingElementMovingPosition(child);if (dragElementPosition ! default){// 处理拖拽元素坐标var offset new Point(dragElementPosition.X - position.X, dragElementPosition.Y - position.Y);child.RenderTransform new TranslateTransform(offset.X, offset.Y);SetDraggingElementMovingPosition(child, dragElementPosition);}...}return finalSize;}
} 运行效果 8.处理跨Panel拖拽。 到目前为止已经实现了本Panel内的控件随意拖拽换位处理从A控件拖到B控件也类似这里需要用到一个静态变量来保存正在拖拽的控件当B控件检测到鼠标进入时只需要在A控件移除正在拖拽的控件在B控件添加正在拖拽的控件就可以实现了。以下为核心代码
public class DragStackPanel : Panel
{// 通过拖拽传递到下一个Panel的控件private static FrameworkElement draggingTransferElement;private void Control_MouseEnter(object sender, MouseEventArgs e){panel.Children.Remove(draggingTransferElement);panel.DraggingElement null;Panel.SetZIndex(draggingTransferElement, this.InternalChildren.Count 1);this.Children.Add(draggingTransferElement);this.AddOverlay(draggingTransferElement);}
} 以下为运行效果 9.在ListBox、ListView、DataGrid等ItemsControl中使用拖拽功能。 所有继承自ItemsControl的控件都有一个ItemsPanel属性该属性可以指定一个Panel类型的控件来对ItemsControl进行排列。理论上只要将ItemsControl.ItemsPanel设置为我们自己开发的Panel控件就可以实现排列及拖拽功能但是这里直接使用的话并不会有效果。原因就是我们并没有对数据绑定的情况下做处理。它的处理逻辑也与上面的类似首先找到ItemsControl控件通过对ItemsSource进行操作就可以实现排列功能由于代码大同小异这里就不再赘述。以下为ListBox控件拖拽的案例效果。 ListBox ItemsSource{Binding Items}ListBox.ItemsPanelItemsPanelTemplateDragStackPanel AllowCrossBorderDragTrue CanDragAndSortTrue IsItemsHostTrue//ItemsPanelTemplate/ListBox.ItemsPanelListBox.ItemTemplateDataTemplateTextBlock Text{Binding Property1} //DataTemplate/ListBox.ItemTemplate
/ListBox 10.添加动画效果。 至此基本功能已经开发完成了下面我们为它添加上动画效果让它更具有观赏性。动画的核心思想就是记录每个元素旧位置的坐标当元素移动到新位置时启动一个动画从旧坐标过渡到新坐标由于代码太过基础这里就不展示了直接上效果。 DragStackPanel AllowCrossBorderDragTrue CanDragAndSortTrue IsItemsHostTrueDragStackPanel.ChildMoveBehaviorChildMoveBehavior Duration0:0:0.5ChildMoveBehavior.EaseXQuinticEase EasingModeEaseOut //ChildMoveBehavior.EaseXChildMoveBehavior.EaseYQuinticEase EasingModeEaseOut //ChildMoveBehavior.EaseY/ChildMoveBehavior/DragStackPanel.ChildMoveBehavior
/DragStackPanel 文章转载自趋时软件 原文链接https://www.cnblogs.com/qushi2020/p/18098514 体验地址引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构