F#でWPF --- 好きな図形のコントロール作成

下記記事ではXAML上でPathを使って好きな図形を描きました。
XAMLで好きな形を描く - 何でもプログラミング

今回はコード側でコントロールを作成してみます。

作成する図形

Pathの時と同様の図形のコントロールを作成します。
f:id:any-programming:20170221002105p:plain

DrawingVisual

Frameworkを継承し、内部でDrawingVisualを利用しています。

AddLogicalChild、AddVisualChildをすることにより、描画部分がマウスイベントを発行するようになります。

open System.Windows
open System.Windows.Media

type CustomShape() as this =
    inherit FrameworkElement()
    let visual = DrawingVisual()
    do  use g = visual.RenderOpen()
        let pen = Pen(Brushes.Red, 5.0)
        g.DrawLine(pen, Point(15.0, 15.0), Point(85.0, 85.0))
        g.DrawLine(pen, Point(85.0, 15.0), Point(15.0, 85.0))
        g.DrawEllipse(null, pen, Point(50.0, 50.0), 50.0, 50.0)

        this.AddLogicalChild(visual)
        this.AddVisualChild(visual)

    override this.VisualChildrenCount = 1

    override this.GetVisualChild _ = visual :> Visual


図形を動的に変更

依存プロパティ等によって図形を動的に変える場合、その都度RenderOpenしても更新されません。

間にDrawingGroupを挟むことにより可能となります。

コンストラクタ

let visual = DrawingVisual()
let group = DrawingGroup()
do  use g = visual.RenderOpen()
    g.DrawDrawing(group)

図形更新

use g = group.Open() 
// g.Draw...


独自のヒットテスト

HitTestCoreをoverrideすることにより、独自のヒットテストを実行することができます。

有効の場合はPointHitTestResultを、無効の場合はnullを返します。

現在の実装のままではDrawingVisualがHitTestを処理してしまうため、新たにHitTestDisabledDrawingVisualを用意します。

AddLogicalChildとAddVisualChildはこの場合必要ありません。

type HitTestDisabledDrawingVisual() =
    inherit DrawingVisual()
    override this.HitTestCore(hitTestParameters : PointHitTestParameters) = null :> HitTestResult

type CustomShape() as this =
    inherit FrameworkElement()
    let visual = HitTestDisabledDrawingVisual()

    ...

    // this.AddLogicalChild(visual)
    // this.AddVisualChild(visual)    

    ...

    override this.HitTestCore(hitTestParameters : PointHitTestParameters) = 
        PointHitTestResult(this, hitTestParameters.HitPoint) :> HitTestResult