午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFとVB.NET、動的追加時にControlTemplateの中のControlを取得するにはApplyTemplate

Pixtack紫陽花2ndの画像の表示は
XAMLの方でWindow.Resourcesに
Imageコントロールを入れたControlTemplateをThumbコントロールに指定しておいて
 
VBコードで
  1. Thumb作成
  2. Window.ResourcesからControlTemplateを取得
  3. ThumbにControlTemplateを指定
  4. ControlTemplateの中のImage取得
  5. 画像ファイルからBitmapImage作成
  6. ImageのSourceにBitmapImageを指定
  7. ThumbをCanvasに追加して表示
この流れで表示している
問題は4番のところでたまにImageを取得できないことがあった
今回これを解決できたっぽいので方法
 
4番の前、つまり中のImageを取得する前にApplyTemplateを実行する
Thumb.ApplyTemplate
これで解決できたっぽい
 
FrameworkElement.ApplyTemplate メソッド(System.Windows)
https://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.applytemplate(v=vs.110).aspx
アプリケーションでこのメソッドを呼び出すと、要素のビジュアル ツリーが完全であることが保証されます。この保証のステップは、コードでツリーの子要素をチェックする場合に必要になることがあります。テンプレートは要素の有効期間の適切な時点で自動的に要素に適用されるため、アプリケーション内の一般的な要素のロジックで ApplyTemplate を呼び出す必要はありません。
なんか適切な時に自動で処理しているから一般的には呼び出す必要ないって言われているw
でも、どうやらこれを実行するとControlTemplateが再構築されるみたいなので取得に失敗することが無くなった
 
 
 
 
XAMLとデザイン画面
f:id:gogowaten:20191025141054p:plain
 
 
VBコード
今回で必要なところは48から97行目まで
それ以外はこういうのができたらいいなあのデモ用
f:id:gogowaten:20191025141104p:plain
 
'FrameworkElement.ApplyTemplate メソッド(System.Windows)
'https://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.applytemplate(v=vs.110).aspx
'/Knowledge/Programming/WPF/コントロールテンプレート上のボタンのスタイルの動的変更
'http://www.geocities.jp/kandou_quester/Knowledge/Programming/WPF/_change_style_of_button_on_control_template_dinamically.html
'↑参照したところ
Imports System.Windows.Controls.Canvas
Imports System.Windows.Controls.Primitives


Class MainWindow

    'Thumbのドラッグ移動用
    Private Sub tmb_DragDelta(sender As Object, e As DragDeltaEventArgs)
        Dim t As Thumb = DirectCast(sender, Thumb)

        Dim x As Double = e.HorizontalChange
        Dim y As Double = e.VerticalChange
        Dim tx As Double = GetLeft(t)
        Dim ty As Double = GetTop(t)
        x += tx
        y += ty

        SetTop(t, y)
        SetLeft(t, x)
    End Sub
    'Thumbの中に画像追加
    Private Sub AddImage(cc As Canvas, path As List(Of String), p As List(Of Point))
        For i As Integer = 0 To p.Count - 1
            Dim b As New BitmapImage(New Uri(path(i)))
            Dim img As New Image With {
                .Source = b,
                .Stretch = Stretch.None,
                .Width = b.PixelWidth,
                .Height = b.PixelHeight}
            SetLeft(img, p(i).X) : SetTop(img, p(i).Y) '描画位置指定
            cc.Children.Add(img) '追加
        Next
    End Sub

    ''' <summary>
    ''' Templateを指定したThumbを作成してcanvas1に追加、Templateの中のcCanvasを返す
    ''' </summary>
    ''' <param name="p">Thumbの描画位置指定</param>
    ''' <returns>ThumbのTemplateの中のcCanvas</returns>
    Private Function AddThumb(p As Point) As Canvas
        Dim t As New Thumb 'Thumb新規作成
        'ThumbにTemplate設定
        Dim tmp As ControlTemplate = Me.Resources.Item("ct")
        t.Template = tmp 'Resources.Item("ct")

        'ThumbをCanvasに追加、描画位置指定
        SetLeft(t, p.X) : SetTop(t, p.Y)
        canvas1.Children.Add(t)
        'マウスドラッグ移動イベント用
        AddHandler t.DragDelta, AddressOf tmb_DragDelta

        'TemplateのVisualTreeを再構築してからでないと中のCanvasを取得できないので
        t.ApplyTemplate() '再構築!!!
        'そして中のCanvasを取得!!!
        Dim cc As Canvas = t.Template.FindName("cCanvas", t)
        'cc.Background = New SolidColorBrush(Colors.White) '背景白色

        Return cc

    End Function
    'Button1クリック
    'Templateの中のコントロールの取得テスト
    Private Sub bt1_Click(sender As Object, e As RoutedEventArgs) Handles bt1.Click
        Dim t As New Thumb 'Thumb新規作成
        t.Name = "ControlTemplateを適用したThumbです" '目印
        'リソースの中からControlTemplateを取得
        Dim tmp As ControlTemplate = Me.Resources.Item("ct")
        'ThumbにTemplate設定
        t.Template = tmp 'Resources.Item("ct")


        'ここからTemplateの中のコントロールの取得
        Dim emp As Canvas = t.Template.FindName("cCanvas", t)
        '↑ここでは取得できない
        '先にTemplateのVisualTreeを再構築する必要があるので
        t.ApplyTemplate() '再構築!!!
        'そして中のコントロールを取得!!!
        Dim cc As Canvas = t.Template.FindName("cCanvas", t)
        Dim cb As Button = t.Template.FindName("cButton", t)
        Dim cl As ListBox = t.Template.FindName("cListbox", t)

        Dim cctp As Thumb = DirectCast(cc.TemplatedParent, Thumb)
        Dim cbtp As Thumb = DirectCast(cb.TemplatedParent, Thumb)
        Dim cltp As Thumb = DirectCast(cl.TemplatedParent, Thumb)

        Dim cctpn As String = cctp.Name

    End Sub
    'Button2クリック
    'canvas1に画像追加したThumbを追加
    Private Sub bt2_Click(sender As Object, e As RoutedEventArgs) Handles bt2.Click
        Dim t As New Thumb
        SetLeft(t, 0) : SetTop(t, 0)
        canvas1.Children.Add(t)
        t.Template = Resources.Item("ct")
        t.ApplyTemplate() '再構築!!!
        AddHandler t.DragDelta, AddressOf tmb_DragDelta

        Dim cc As Canvas = t.Template.FindName("cCanvas", t)

        Dim path As New List(Of String)({"D:\ブログ用\テスト用画像\collection_1.png",
                                        "D:\ブログ用\テスト用画像\collection_2.png",
                                        "D:\ブログ用\テスト用画像\collection_3.png"})
        Dim p As New List(Of Point)({New Point(10, 10),
                                    New Point(50, 60),
                                    New Point(80, 20)})
        Call AddImage(cc, path, p)
    End Sub
    'Button3クリック
    'canvas1に画像追加したThumbを追加
    Private Sub bt3_Click(sender As Object, e As RoutedEventArgs) Handles bt3.Click
        Dim t As New Thumb
        SetLeft(t, 0) : SetTop(t, 0)
        canvas1.Children.Add(t)
        t.Template = Resources.Item("ct")
        t.ApplyTemplate() '再構築!!!
        AddHandler t.DragDelta, AddressOf tmb_DragDelta

        Dim cc As Canvas = t.Template.FindName("cCanvas", t)

        Dim path As New List(Of String)({"D:\ブログ用\テスト用画像\hueRectT210.png",
                                        "D:\ブログ用\テスト用画像\hueRectT255.png",
                                        "D:\ブログ用\テスト用画像\hueRectT300.png"})
        Dim p As New List(Of Point)({New Point(10, 10),
                                    New Point(30, 30),
                                    New Point(80, 20)})
        Call AddImage(cc, path, p)
    End Sub

End Class
 
 
 
イメージ 6
ControlTemplateの中にはCanvasとButtonとListBoxの3つ入れてみた
それぞれ名前を付けておく、この名前を使って取得することになる
 
アプリを起動して中のControlを取得して確認してみる
イメージ 4
ボタン1が確認ボタン
押すとコードの方に一時停止が指定してあるので
 
イメージ 5
赤の中身が青
85行目でApplyTemplateを実行しているので、その前の82行目では中のControlを取得できていないのでNothingになっている
87行目からの3行ではそれぞれ3つのControlが取得できている(cc, cb, cl)
91行目からの3行では取得したControlのTemplateParentを取得していて、すべてThumbになっているのがわかる(cctp, cbtp, cltp)
これで作成したThumbに適用したControlTemplateの中のControlが取得できていることがわかった
 
 
 
ボタン2とボタン3は押すと3つの画像を一つにまとめた画像を表示する
イメージ 1
画像をグループ化してまとめて移動できたらいいなあ

イメージ 7
Thumbの中のCanvasにImageを3つ追加したものを表示
 
こういう画像や表示場所の決め打ちならできたけど、実際にはグループ化解除や画像の選択方法、その編集状態の保存とか入ってくるから難しいし、その前に今のPixtack紫陽花2ndはThumbの中に直接Imageを入れているから複数画像を入れられないから、ImageをCanvasに変更してそこにImageを追加する方法に書き直さないとできない
グループ化も無印の時にはできなかったからなんとかしたいんだよねえ
 
 
今回のコード