裝飾器學習筆記

猝不及防發表於2021-06-11

裝飾器學習筆記

0 定義

裝飾器可以用來給顯示的控制元件新增一些裝飾的效果;

1 效果

1 框選

滑鼠按下獲取當前點

   startPoint = parameter.GetPosition(Application.Current.MainWindow);

隨著滑鼠移動獲取末位點,不斷重新畫矩形

 Point tempEndPoint = parameter.GetPosition(Application.Current.MainWindow);

1.1 畫臨時矩形

 private void DrawRect(Point endPoint, Point startPoint)
        {
            if (selectRect == null)
            {
                selectRect = new Rectangle();
                selectRect.Fill = new SolidColorBrush(Colors.Gray) { Opacity = 0.1 };
                selectRect.Margin = new Thickness(startPoint.X, startPoint.Y, 0, 0);
                Controls.Add(selectRect);
            }
            selectRect.Width = Math.Abs(endPoint.X - startPoint.X);
            selectRect.Height = Math.Abs(endPoint.Y - startPoint.Y);
        }

2 新增裝飾器

裝飾器是 FrameworkElement 繫結到的自定義 UIElement , 裝飾器呈現在裝飾器層中。

  • 裝飾器層是始終位於裝飾元素或裝飾元素集合之上。
  • 裝飾器通常使用位於裝飾元素左上部的標準 2D 座標原點,相對於其繫結到的元素進行定位。
  • 裝飾器始終以可見的方式位於控制元件頂部,無法使用 z 順序重寫。

2.1 常規新增裝飾器

常規做法一般在裝飾器層新增裝飾器後,在裝飾器中會以重寫Render的方式新增效果

// Adorners must subclass the abstract base class Adorner.
public class SimpleCircleAdorner : Adorner
{
  // Be sure to call the base class constructor.
  public SimpleCircleAdorner(UIElement adornedElement)
    : base(adornedElement)
  {
  }

  // A common way to implement an adorner's rendering behavior is to override the OnRender
  // method, which is called by the layout system as part of a rendering pass.
  protected override void OnRender(DrawingContext drawingContext)
  {
    Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);

    // Some arbitrary drawing implements.
    SolidColorBrush renderBrush = new SolidColorBrush(Colors.Green);
    renderBrush.Opacity = 0.2;
    Pen renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);
    double renderRadius = 5.0;

    // Draw a circle at each corner.
    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);
  }
}

2.1 裝飾器如何新增自定義控制元件?

裝飾器沒有給出一個直接的方法去新增自定義控制元件,且裝飾器與外界的聯絡只有一個被裝飾的控制元件,那麼我們如何在視覺化樹上新增自定義控制元件呢?

我們可以通過間接的方法:

The AddVisualChild method is a notification to the base class that you've acquired a new child into your collection. It doesn't actually add the child to any collection, but it lets the Visual system know that it needs to redraw. This sounds a bit funny, but it's consistent with the way the logical tree is managed as well.

Similarly, when you remove a Visual from your child collection you must call RemoveVisualChild, so the Visual system knows to stop tracking and displaying that Visual. If you'd like, and it sounds appropriate in your scenario, you don't have to use an actual collection for your child visuals. You can simply have a member variable of type Visual, which you can think of as a collection of maximum length 1. Your implementation of GetVisualChild should return the value of that variable, and your implementation of VisualChildrenCount should probably return 0 if that variable is null, or 1 if it's non-null.

通過重寫GetVisualChild方法使視覺化樹重繪

2.2 控制元件和裝飾器如何實現聯動效果

如果想實現預期的效果,需要裝飾器B和被裝飾控制元件A聯動,比如當A發生形變時,B也發生形變

正常的,我們可以用A去通知B,但是裝飾器作為獨立的一層,不應該和A產生聯絡

我們可以通過重寫ArrangeOverride方法,當重新排序的時候給裝飾器內的自定義控制元件一個自己的大小(位置)

  public Rect AdonerArrange(Size finalSize)
        {
            int margin = 10;
            return new Rect(-margin, -margin, finalSize.Width + 2 * margin, finalSize.Height + 2 * margin);
        }

3 平移例子

在自定義控制元件層,我們通過裝飾器拿到被裝飾器的控制元件

自定義裝飾器控制元件-->裝飾器層-->被裝飾控制元件

被裝飾控制元件:grid

裝飾器層:layer

自定義裝飾器控制元件:contentControl

 			 var layer = AdornerLayer.GetAdornerLayer(grid);
            DragDropControl contentControl = new DragDropControl(grid);
            contentControl.Background = new SolidColorBrush(Colors.Black) { Opacity = 0.1 };
            layer.Add(new ContentAdorner(grid, contentControl));

之後我們在自定義裝飾器控制元件contentControl中操作被裝飾控制元件grid:

        bool isCanMove = false;

        private void thumMove_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
        {
            isCanMove = true;
        }

        private void thumMove_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            if (isCanMove)
            {
                InkCanvas.SetLeft(_adornedElement, InkCanvas.GetLeft(_adornedElement) + e.HorizontalChange);
                InkCanvas.SetTop(_adornedElement, InkCanvas.GetTop(_adornedElement) + e.VerticalChange);
            }
        }

        private void thumMove_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
        {
            isCanMove = false;
        }

此時我們通過contentControl控制元件平移被裝飾的控制元件grid

而contentControl通過AdonerArrange方法跟隨grid進行平移

 public Rect AdonerArrange(Size finalSize)
        {
            int margin = 10;
            return new Rect(-margin, -margin, finalSize.Width + 2 * margin, finalSize.Height + 2 * margin);

        }

實現了:

當控制元件沒有裝飾器時,並不具備平移能力。

當控制元件有改裝飾器修飾時,具備了平移能力。

即常規裝飾器可以新增效果,自定義控制元件裝飾器可以新增一些能力。

Demo:https://github.com/tiancai4652/MyDecorator

相關文章