狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

WPF/C#:如何實現拖拉元素

freeflydom
2024年9月6日 9:44 本文熱度 915

前言

在Canvas中放置了一些元素,需要能夠拖拉這些元素,在WPF Samples中的DragDropObjects項目中告訴了我們如何實現這種效果。

效果如下所示:

拖拉過程中的效果如下所示:

具體實現

xaml頁面

我們先來看看xaml:

 <Canvas Name="MyCanvas"

         PreviewMouseLeftButtonDown="MyCanvas_PreviewMouseLeftButtonDown" 

         PreviewMouseMove="MyCanvas_PreviewMouseMove"

         PreviewMouseLeftButtonUp="MyCanvas_PreviewMouseLeftButtonUp">

     <Rectangle Fill="Blue" Height="32" Width="32" Canvas.Top="8" Canvas.Left="8"/>

     <TextBox Text="This is a TextBox. Drag and drop me" Canvas.Top="100" Canvas.Left="100"/>

 </Canvas>

為了實現這個效果,在Canvas上使用了三個隧道事件(預覽事件)PreviewMouseLeftButtonDownPreviewMouseMovePreviewMouseLeftButtonUp

而什么是隧道事件(預覽事件)呢?

預覽事件,也稱為隧道事件,是從應用程序根元素向下遍歷元素樹到引發事件的元素的路由事件。

PreviewMouseLeftButtonDown當用戶按下鼠標左鍵時觸發。

PreviewMouseMove當用戶移動鼠標時觸發。

PreviewMouseLeftButtonUp當用戶釋放鼠標左鍵時觸發。

再來看看cs:

 private bool _isDown;

 private bool _isDragging;

 private UIElement _originalElement;

 private double _originalLeft;

 private double _originalTop;

 private SimpleCircleAdorner _overlayElement;

 private Point _startPoint;

定義了這幾個私有字段。

鼠標左鍵按下事件處理程序

鼠標左鍵按下事件處理程序:

 private void MyCanvas_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

 {

     if (e.Source == MyCanvas)

     {

     }

     else

     {

         _isDown = true;

         _startPoint = e.GetPosition(MyCanvas);

         _originalElement = e.Source as UIElement;

         MyCanvas.CaptureMouse();

         e.Handled = true;

     }

 }

最開始引發這個事件的是MyCanvas元素,當事件源是Canvas的時候,不做處理,因為我們只想處理發生在MyCanvas子元素上的鼠標左鍵按下事件。

鼠標移動事件處理程序

現在來看看鼠標移動事件處理程序:

  private void MyCanvas_PreviewMouseMove(object sender, MouseEventArgs e)

  {

      if (_isDown)

      {

          if ((_isDragging == false) &&

              ((Math.Abs(e.GetPosition(MyCanvas).X - _startPoint.X) >

                SystemParameters.MinimumHorizontalDragDistance) ||

               (Math.Abs(e.GetPosition(MyCanvas).Y - _startPoint.Y) >

                SystemParameters.MinimumVerticalDragDistance)))

          {

              DragStarted();

          }

          if (_isDragging)

          {

              DragMoved();

          }

      }

  }

鼠標左鍵已經按下了,但還沒開始移動事,執行DragStarted方法。

創建裝飾器

DragStarted方法如下:

 private void DragStarted()

 {

     _isDragging = true;

     _originalLeft = Canvas.GetLeft(_originalElement);

     _originalTop = Canvas.GetTop(_originalElement);


     _overlayElement = new SimpleCircleAdorner(_originalElement);

     var layer = AdornerLayer.GetAdornerLayer(_originalElement);

     layer.Add(_overlayElement);

 }

_overlayElement = new SimpleCircleAdorner(_originalElement);

創建了一個新的裝飾器(Adorner)并將其與一個特定的UI元素關聯起來。

而WPF中裝飾器是什么呢?

裝飾器是一種特殊類型的 FrameworkElement,用于向用戶提供視覺提示。 裝飾器有很多用途,可用來向元素添加功能句柄,或者提供有關某個控件的狀態信息。

Adorner 是綁定到 UIElement 的自定義 FrameworkElement。 裝飾器在 AdornerLayer 中呈現,它是始終位于裝飾元素或裝飾元素集合之上的呈現表面。 裝飾器的呈現獨立于裝飾器綁定到的 UIElement 的呈現。 裝飾器通常使用位于裝飾元素左上部的標準 2D 坐標原點,相對于其綁定到的元素進行定位。

裝飾器的常見應用包括:

  • 向 UIElement 添加功能句柄,使用戶能夠以某種方式操作元素(調整大小、旋轉、重新定位等)。

  • 提供視覺反饋以指示各種狀態,或者響應各種事件。

  • 在 UIElement 上疊加視覺裝飾。

  • 以視覺方式遮蓋或覆蓋 UIElement 的一部分或全部。

Windows Presentation Foundation (WPF) 為裝飾視覺元素提供了一個基本框架。

在這個Demo中裝飾器就是移動過程中四個角上出現的小圓以及內部不斷閃爍的顏色,如下所示:

這是如何實現的呢?

這個Demo中自定義了一個繼承自Adorner的SimpleCircleAdorner,代碼如下所示:

using System;

using System.Windows;

using System.Windows.Documents;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;


namespace DragDropObjects

{

    public class SimpleCircleAdorner : Adorner

    {

        private readonly Rectangle _child;

        private double _leftOffset;

        private double _topOffset;

        // Be sure to call the base class constructor.

        public SimpleCircleAdorner(UIElement adornedElement)

            : base(adornedElement)

        {

            var brush = new VisualBrush(adornedElement);


            _child = new Rectangle

            {

                Width = adornedElement.RenderSize.Width,

                Height = adornedElement.RenderSize.Height

            };



            var animation = new DoubleAnimation(0.3, 1, new Duration(TimeSpan.FromSeconds(1)))

            {

                AutoReverse = true,

                RepeatBehavior = RepeatBehavior.Forever

            };

            brush.BeginAnimation(Brush.OpacityProperty, animation);


            _child.Fill = brush;

        }


        protected override int VisualChildrenCount => 1;


        public double LeftOffset

        {

            get { return _leftOffset; }

            set

            {

                _leftOffset = value;

                UpdatePosition();

            }

        }


        public double TopOffset

        {

            get { return _topOffset; }

            set

            {

                _topOffset = value;

                UpdatePosition();

            }

        }


        // A common way to implement an adorner's rendering behavior is to override the OnRender

        // method, which is called by the layout subsystem as part of a rendering pass.

        protected override void OnRender(DrawingContext drawingContext)

        {

            // Get a rectangle that represents the desired size of the rendered element

            // after the rendering pass.  This will be used to draw at the corners of the 

            // adorned element.

            var adornedElementRect = new Rect(AdornedElement.DesiredSize);


            // Some arbitrary drawing implements.

            var renderBrush = new SolidColorBrush(Colors.Green) {Opacity = 0.2};

            var renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);

            const double renderRadius = 5.0;


            // Just draw a circle at each corner.

            drawingContext.DrawRectangle(renderBrush, renderPen, adornedElementRect);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius,

                renderRadius);

        }


        protected override Size MeasureOverride(Size constraint)

        {

            _child.Measure(constraint);

            return _child.DesiredSize;

        }


        protected override Size ArrangeOverride(Size finalSize)

        {

            _child.Arrange(new Rect(finalSize));

            return finalSize;

        }


        protected override Visual GetVisualChild(int index) => _child;


        private void UpdatePosition()

        {

            var adornerLayer = Parent as AdornerLayer;

            adornerLayer?.Update(AdornedElement);

        }


        public override GeneralTransform GetDesiredTransform(GeneralTransform transform)

        {

            var result = new GeneralTransformGroup();

            result.Children.Add(base.GetDesiredTransform(transform));

            result.Children.Add(new TranslateTransform(_leftOffset, _topOffset));

            return result;

        }

    }

}

  var animation = new DoubleAnimation(0.3, 1, new Duration(TimeSpan.FromSeconds(1)))

            {

                AutoReverse = true,

                RepeatBehavior = RepeatBehavior.Forever

            };

            brush.BeginAnimation(Brush.OpacityProperty, animation);


這里在元素內部添加了動畫。

 // Just draw a circle at each corner.

            drawingContext.DrawRectangle(renderBrush, renderPen, adornedElementRect);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius);

            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius,

                renderRadius);

這里在元素的四個角畫了小圓形。

  var layer = AdornerLayer.GetAdornerLayer(_originalElement);

      layer.Add(_overlayElement);


這段代碼的作用是將之前創建的裝飾器_overlayElement添加到與特定UI元素_originalElement相關聯的裝飾器層(AdornerLayer)中。一旦裝飾器被添加到裝飾器層中,它就會在_originalElement被渲染時顯示出來。

AdornerLayer是一個特殊的層,用于在UI元素上繪制裝飾器。每個UI元素都有一個與之關聯的裝飾器層,但并不是所有的UI元素都能直接看到這個層。

GetAdornerLayer方法會返回與_originalElement相關聯的裝飾器層。

裝飾器層會負責管理裝飾器的渲染和布局,確保裝飾器正確地顯示在UI元素上。

再來看看DragMoved方法:

 private void DragMoved()

 {

     var currentPosition = Mouse.GetPosition(MyCanvas);


     _overlayElement.LeftOffset = currentPosition.X - _startPoint.X;

     _overlayElement.TopOffset = currentPosition.Y - _startPoint.Y;

 }


計算元素的偏移。

鼠標左鍵松開事件處理程序

鼠標左鍵松開事件處理程序:

  private void MyCanvas_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)

  {

      if (_isDown)

      {

          DragFinished();

          e.Handled = true;

      }

  }


DragFinished方法如下:

private void DragFinished(bool cancelled = false)

 {

     Mouse.Capture(null);

     if (_isDragging)

     {

         AdornerLayer.GetAdornerLayer(_overlayElement.AdornedElement).Remove(_overlayElement);


         if (cancelled == false)

         {

             Canvas.SetTop(_originalElement, _originalTop + _overlayElement.TopOffset);

             Canvas.SetLeft(_originalElement, _originalLeft + _overlayElement.LeftOffset);

         }

         _overlayElement = null;

     }

     _isDragging = false;

     _isDown = false;

 }

 AdornerLayer.GetAdornerLayer(_overlayElement.AdornedElement).Remove(_overlayElement);

從與_overlayElement所裝飾的UI元素相關聯的裝飾器層中移除_overlayElement,從而使得裝飾器不再顯示在UI元素上。這樣,當UI元素被渲染時,裝飾器將不再影響其外觀或行為。

代碼來源

[WPF-Samples/Drag and Drop/DragDropObjects at main · microsoft/WPF-Samples (github.com)](https://github.com/microsoft/WPF-Samples/tree/main/Drag and Drop/DragDropObjects)

參考

1、預覽事件 - WPF .NET | Microsoft Learn

2、裝飾器概述 - WPF .NET Framework | Microsoft Learn

3、Adorner 類 (System.Windows.Documents) | Microsoft Learn

轉自https://www.cnblogs.com/mingupupu/p/18270547


該文章在 2024/9/6 9:44:55 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved