欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

WPF/C#:如何實(shí)現(xiàn)拖拉元素

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

前言

在Canvas中放置了一些元素,需要能夠拖拉這些元素,在WPF Samples中的DragDropObjects項(xiàng)目中告訴了我們?nèi)绾螌?shí)現(xiàn)這種效果。

效果如下所示:

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

具體實(shí)現(xiàn)

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>

為了實(shí)現(xiàn)這個(gè)效果,在Canvas上使用了三個(gè)隧道事件(預(yù)覽事件)PreviewMouseLeftButtonDownPreviewMouseMovePreviewMouseLeftButtonUp

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

預(yù)覽事件,也稱為隧道事件,是從應(yīng)用程序根元素向下遍歷元素樹到引發(fā)事件的元素的路由事件。

PreviewMouseLeftButtonDown當(dāng)用戶按下鼠標(biāo)左鍵時(shí)觸發(fā)。

PreviewMouseMove當(dāng)用戶移動(dòng)鼠標(biāo)時(shí)觸發(fā)。

PreviewMouseLeftButtonUp當(dāng)用戶釋放鼠標(biāo)左鍵時(shí)觸發(fā)。

再來看看cs:

 private bool _isDown;

 private bool _isDragging;

 private UIElement _originalElement;

 private double _originalLeft;

 private double _originalTop;

 private SimpleCircleAdorner _overlayElement;

 private Point _startPoint;

定義了這幾個(gè)私有字段。

鼠標(biāo)左鍵按下事件處理程序

鼠標(biāo)左鍵按下事件處理程序:

 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;

     }

 }

最開始引發(fā)這個(gè)事件的是MyCanvas元素,當(dāng)事件源是Canvas的時(shí)候,不做處理,因?yàn)槲覀冎幌胩幚戆l(fā)生在MyCanvas子元素上的鼠標(biāo)左鍵按下事件。

鼠標(biāo)移動(dòng)事件處理程序

現(xiàn)在來看看鼠標(biāo)移動(dòng)事件處理程序:

  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();

          }

      }

  }

鼠標(biāo)左鍵已經(jīng)按下了,但還沒開始移動(dòng)事,執(zhí)行DragStarted方法。

創(chuàng)建裝飾器

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);

創(chuàng)建了一個(gè)新的裝飾器(Adorner)并將其與一個(gè)特定的UI元素關(guān)聯(lián)起來。

而WPF中裝飾器是什么呢?

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

Adorner 是綁定到 UIElement 的自定義 FrameworkElement。 裝飾器在 AdornerLayer 中呈現(xiàn),它是始終位于裝飾元素或裝飾元素集合之上的呈現(xiàn)表面。 裝飾器的呈現(xiàn)獨(dú)立于裝飾器綁定到的 UIElement 的呈現(xiàn)。 裝飾器通常使用位于裝飾元素左上部的標(biāo)準(zhǔn) 2D 坐標(biāo)原點(diǎn),相對(duì)于其綁定到的元素進(jìn)行定位。

裝飾器的常見應(yīng)用包括:

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

  • 提供視覺反饋以指示各種狀態(tài),或者響應(yīng)各種事件。

  • 在 UIElement 上疊加視覺裝飾。

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

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

在這個(gè)Demo中裝飾器就是移動(dòng)過程中四個(gè)角上出現(xiàn)的小圓以及內(nèi)部不斷閃爍的顏色,如下所示:

這是如何實(shí)現(xiàn)的呢?

這個(gè)Demo中自定義了一個(gè)繼承自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);


這里在元素內(nèi)部添加了動(dòng)畫。

 // 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);

這里在元素的四個(gè)角畫了小圓形。

  var layer = AdornerLayer.GetAdornerLayer(_originalElement);

      layer.Add(_overlayElement);


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

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

GetAdornerLayer方法會(huì)返回與_originalElement相關(guān)聯(lián)的裝飾器層。

裝飾器層會(huì)負(fù)責(zé)管理裝飾器的渲染和布局,確保裝飾器正確地顯示在UI元素上。

再來看看DragMoved方法:

 private void DragMoved()

 {

     var currentPosition = Mouse.GetPosition(MyCanvas);


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

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

 }


計(jì)算元素的偏移。

鼠標(biāo)左鍵松開事件處理程序

鼠標(biāo)左鍵松開事件處理程序:

  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元素相關(guān)聯(lián)的裝飾器層中移除_overlayElement,從而使得裝飾器不再顯示在UI元素上。這樣,當(dāng)UI元素被渲染時(shí),裝飾器將不再影響其外觀或行為。

代碼來源

[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、預(yù)覽事件 - WPF .NET | Microsoft Learn

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

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

轉(zhuǎn)自https://www.cnblogs.com/mingupupu/p/18270547


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