none
WPF comboboxのボタンの配色を変更したい RRS feed

  • 質問

  • ACCESS2000で製作したプログラムをWPFアプリケーションに移植しており、現在UIのデザインを行っています。

    WPFのcomboboxはドロップダウンボタン(▼)の枠が無く、これがボタンであるというアピールがとても弱く気に入らないので、ACCESS2000と同じ外観を踏襲したいと思っています。

    ドロップダウンボタンに枠をつけるか、ドロップダウンボタン全体の配色を設定する方法を教えて下さい。

    できれば、XAMLを使わずコードビハインドのみで御回答下さい。よろしくお願い致します。

    ※ここに画像を貼り付けできれば良かったのですが、アカウントが確認できないという理由で拒否されました。 画像のリンク先を↓に示します。

    https://box.yahoo.co.jp/guest/viewer?sid=box-l-kxdvxo7paoypvum2halnj35oei-1001&uniqid=75ca4629-c374-47aa-9aa4-78e3617392df&viewtype=detail



    2016年3月30日 8:21

回答

  • こんな

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <StackPanel >
            <ComboBox Loaded="ComboBox_Loaded" IsEditable="False"/>
            <ComboBox Loaded="ComboBox_Loaded" IsEditable="True"/>
            <!-- .Net 4.0以降であれば PresentationFramework.Classicを参照(ローカルコピーも)してやるという手も
            <ComboBox >
                <ComboBox.Resources>
                    <ResourceDictionary>
                        <ResourceDictionary.MergedDictionaries>                        
                            <ResourceDictionary Source="/presentationframework.Classic;component/themes/classic.xaml" />
                        </ResourceDictionary.MergedDictionaries>
                    </ResourceDictionary>
                </ComboBox.Resources>
            </ComboBox>
            -->
        </StackPanel>
    </Window>

    Imports System.Windows.Controls.Primitives
    Class MainWindow
        Private Sub ComboBox_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim combo As ComboBox = CType(sender, ComboBox)
            ComboBoxButtonDecorator.AddButtonBorderDecorator(combo)
        End Sub
    End Class
    
    Class ComboBoxButtonDecorator
        Inherits Adorner
    
        Public Shared Sub AddButtonBorderDecorator(ByVal combo As ComboBox)
            Dim toggle As ToggleButton = GetToggleButton(combo)
    
            If (toggle IsNot Nothing) Then
                Dim decorateTarget As FrameworkElement
                decorateTarget = toggle
    
                Dim c As Integer = VisualTreeHelper.GetChildrenCount(toggle)
                Dim list As New List(Of Panel)
                Find(Of Panel)(toggle, list)
                If (list.Count > 0) Then
                    Dim p As Panel = list(0)
                    If (p.Children.Count = 1 AndAlso Not TypeOf p.Children(0) Is Shape) Then
                        decorateTarget = p.Children(0)
                    Else
                        decorateTarget = p
                    End If
                Else
                    Dim paths As New List(Of Path)
                    Find(Of Path)(toggle, paths)
                    If (paths.Count = 1) Then
                        Dim path As Path = paths(0)
                        decorateTarget = path.Parent
                    End If
                End If
                'End If
                Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(decorateTarget)
                If (layer IsNot Nothing) Then
                    Dim decorator As New ComboBoxButtonDecorator(decorateTarget)
                    layer.Add(decorator)
                End If
            End If
        End Sub
    
        Private Shared Function GetToggleButton(ByVal combo As ComboBox) As ToggleButton
            Dim list As New List(Of ToggleButton)
            Find(Of ToggleButton)(combo, list)
            For Each t As ToggleButton In list
                If (t.TemplatedParent Is combo) Then
                    Return t
                End If
            Next
            Return Nothing
        End Function
    
        Private Shared Sub Find(Of T As {DependencyObject})(ByVal d As DependencyObject, ByVal list As List(Of T))
            Dim count = VisualTreeHelper.GetChildrenCount(d)
            For i As Integer = 0 To count - 1
                Dim child As DependencyObject = VisualTreeHelper.GetChild(d, i)
                If (TypeOf child Is T) Then
                    list.Add(CType(child, T))
                End If
                Find(child, list)
            Next
        End Sub
    
    
    
        Sub New(ByVal ui As FrameworkElement)
            MyBase.New(ui)
    
            border = New Border()
            border.BorderThickness = New Thickness(1)
            border.BorderBrush = Brushes.Red
            'border.Background = New SolidColorBrush(Color.FromArgb(20, 0, 0, 255))
    
            Dim bnd As New Binding
            bnd.Path = New PropertyPath(FrameworkElement.ActualWidthProperty)
            bnd.Source = ui
            border.SetBinding(border.WidthProperty, bnd)
    
            bnd = New Binding()
            bnd.Path = New PropertyPath(FrameworkElement.ActualHeightProperty)
            bnd.Source = ui
            border.SetBinding(border.HeightProperty, bnd)
    
            'Dim w As Integer = CInt(ui.ActualWidth - border.BorderThickness.Left - border.BorderThickness.Right)
            'Dim h As Integer = CInt(ui.ActualHeight - border.BorderThickness.Top - border.BorderThickness.Bottom)
            'If (w > 0 AndAlso h > 0) Then
            '    Dim bmp As New System.Drawing.Bitmap(w, h)
            '    Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
            '        System.Windows.Forms.ControlPaint.DrawComboButton(g, 0, 0, bmp.Width, bmp.Height, Forms.ButtonState.Normal)
            '    End Using
    
            '    Using ms As New System.IO.MemoryStream()
    
            '        bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp)
            '        ms.Position = 0
            '        Dim bimg As BitmapImage = New BitmapImage()
            '        bimg.BeginInit()
            '        bimg.StreamSource = ms
            '        bimg.CacheOption = BitmapCacheOption.OnLoad
            '        bimg.EndInit()
    
            '        Dim img As New Image
            '        img.Source = bimg
            '        border.Child = img
            '    End Using
            'End If
        End Sub
    
        Private border As Border
    
        Protected Overrides ReadOnly Property VisualChildrenCount As Integer
            Get
                Return 1
            End Get
        End Property
        Protected Overrides Function GetVisualChild(index As Integer) As System.Windows.Media.Visual
            If (index <> 0) Then
                Return Nothing
            End If
            Return border
        End Function
        Protected Overrides Function MeasureOverride(constraint As System.Windows.Size) As System.Windows.Size
            border.Measure(constraint)
            Return border.DesiredSize
        End Function
        Protected Overrides Function ArrangeOverride(finalSize As System.Windows.Size) As System.Windows.Size
            border.Arrange(New Rect(New Point(0, 0), finalSize))
            Return New Size(border.ActualWidth, border.ActualHeight)
        End Function
    End Class

    Win7 + .net 3.5/4.0/4.5 + クラシック/Aero 
    Win8.1+ .net 3.5/4.0/4.6
    Win10 + .net 3.5/4.0/4.6
    で確認
    #テーマによってはまだ見逃しがあるかも。

    おまけ
    ComboBoxにclassicをコードで適用してみた。

    Class MainWindow
        Sub New()
            InitializeComponent()
    
            Dim uri As New Uri("/PresentationFramework.Classic;component/themes/classic.xaml", UriKind.Relative)
            Dim s As System.IO.Stream = Application.GetResourceStream(uri).Stream
            Dim bamlReader As New Baml2006.Baml2006Reader(s)
            Dim writer As New Xaml.XamlObjectWriter(bamlReader.SchemaContext)
            While (bamlReader.Read())
                writer.WriteNode(bamlReader)
            End While
            classic = CType(writer.Result(), ResourceDictionary)
        End Sub
    
        Private classic As ResourceDictionary
    
        Private Sub ComboBox_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim combo As ComboBox = CType(sender, ComboBox)
            combo.Resources.MergedDictionaries.Add(classic)
        End Sub
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2016年4月1日 11:57 おまけ追加
    • 回答としてマーク huahi11112 2016年4月1日 13:39
    2016年4月1日 11:32

すべての返信

  • こんにちは。

    コンボボックスのテンプレートから仰っているドロップダウンボタンを探して変更してみました。

    ToggleButton内のBorderがドロップダウンボタンに該当しますので、
    FindNameで検索して境界線と背景を変えました。

    var toggle = comboBox.Template.FindName("toggleButton", comboBox) as ToggleButton;
    var border = toggle.Template.FindName("splitBorder", toggle) as Border;
    border.BorderBrush = new SolidColorBrush(Colors.Red);
    border.Background = new SolidColorBrush(Colors.Blue);
    

    テンプレート適用後でないと検索が出来ないので、WindowのLoad後などに上記処理を行うのが良いでしょうか。

    指定があったのでコードビハインドにて回答しましたが、
    見た目の変更のみであればXAMLでスタイル適用でどうにかすることをお勧めします。

    2016年3月30日 8:46
    モデレータ
  • アプリケーション全体でComboBoxの外観を変更したいようですので、ControlTemplateで対応するのが良いように思います。
    もちろん全てのComboBoxの外観を変えた後、個別にComboBoxの外観を変更することも可能です。
    ControlTemplateの例は公開されており、例えば以下のページに書かれている例を適応すると、以下の画像のような外観になります。

    ComboBox のスタイルとテンプレート
    https://msdn.microsoft.com/ja-jp/library/ms752094(v=vs.110).aspx

    ComboBoxに上記のControlTemplateを適用するには、ソリューションエクスプローラーからApp.xamlを探し、その<Application.Resources>タグ内に、上記のページの「前の例では、次の 1 つ以上のリソースを使用しています。」の部分のXAMLをコピーしてまず貼り付け、次に、「次の例は、ComboBox コントロールの ControlTemplate と関連の型を定義する方法を示しています。」と書かれている下のXAMLを貼り付けるだけでOKです。
    上記のページでXAMLが掲載されているパートが2つに分かれていますが、最初に下のパートから貼り付けるようにして下さい。

    コードで外観の変更を行う理由があるにしても、ベースとなる外観を変更したいのであれば、まずは上記のことをお試しください。
    また、条件によってXAMLでも外観を動的に変更することも可能です。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年3月31日 1:26
    モデレータ
  • 御回答ありがとうございます。リンク先のサイトは質問前に確認しておりますが、情報量が多過ぎ、私の望む解決法がどの部分に記載されているのか分からず、またXAMLを使用しているため、御教示いただいた方法は見送らせていただいております。

    2016年3月31日 2:56
  • よろしければコードで実現する方法に拘る理由を教えていただけませんでしょうか?
    その理由によっては、よりスマートな実現方法があるかもしれません。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年3月31日 3:17
    モデレータ
  • 御回答ありがとうございました。

    var toggle = comboBox.Template.FindName("toggleButton", comboBox) as ToggleButton;

    この行なのですが、ComboBox.Template.Childnamesプロパティーには始めから"toggleButton"は存在しないので、toggle=Nothingとなり、その後の処理が正常に実行できません。

    ’Comboboxの文字入力用カーソル(キャレット)の色を赤にする

    dim textboxOfCB1 as Textbox= ComboBox.Template.FindName("PART_EditableTextBox", ComboBox)

    textboxOfCB1 .CaretBrush = Brushes.Red

    ↑この処理は正常に実行されています。VSのバージョンはVS 2010です。

    動作可能なコードを御教示下さい。お願い致します。

    2016年4月1日 2:31
  • そのFindNameメソッドはどのタイミングで実行していますか。
    テンプレート適用後である必要がありますが、それは確認済みでしょうか。

    2016年4月1日 2:46
    モデレータ
  • MainWindow_Loadedイベント内で記述しています。

    テンプレート適用後とは、どういう意味でしょうか。

    動作をブレークしてComboBox.Template.Childnamesを見ると、内容はこうなっています。

    画像を貼り付けたいのですが、「お客様のアカウントが確認されるまで、本文に画像やリンクを含むことはできません。」というエラーが出て投稿できませんので、下のリンクから画像を御覧下さい。

    https://box.yahoo.co.jp/guest/viewer?sid=box-l-kxdvxo7paoypvum2halnj35oei-1001&uniqid=9dadcae1-ed26-4749-875f-1f35eb772263&viewtype=detail

    この画像の中に"toggleButton"は出てきません。

    2016年4月1日 6:38
  • テンプレートは理解されていますか? コードで操作するにしても、結局はテンプレート(XAML)を操作することになります。
    端的に言えば、テンプレートで指定されている"toggleButton"を検索し、それを見つけて背景色などを変更するコードを実行しようとされています。しかし、"toggleButton"が見つからないのであれば、現在お使いのテンプレートには別の名前が指定されているのだと思います。

    Visual Studioのバージョンは何でしょうか? 現在のComboBox内のToggleButtonに何という名前が付けられているかは、VS2015のプロフェッショナル以上だとライブビジュアルツリーで表示できますし、そうでなければsnoop等で調べることができます。

    しかし、いずにしても今回そのようにして名前を見つけてコードが動くようになったとしても、将来に渡ってその名前が使われる保障はありません。今回のコードをいろいろなところに書いた場合、名前が変更されて動作しなくなった時の修正量が小さくない可能性があります。
    コードで外観の変更を行うというのは、あくまでもそういう名前のオブジェクトが存在することが前提です。そうではなく、テンプレートを指定して外観を変更する場合は、他の人がどのような名前を内部のコントロールに付けているのかを気にする必要がありません。

    #ちなみにWindowのLoadedイベントで問題ありません。そこで"toggleButton"が見つからないという事は、"toggleButton"という名前ではない可能性が高いように思います。


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年4月1日 7:07
    モデレータ
  • .NET Frameworkのバージョンを下げるとtoggleButtonが見つかりませんでした。
    正確にはtoggleButtonは名前なしでした。

    とりあえずVisualTreeHelperを使ってToggleButtonの探索は出来ましたが、
    その中にsplitBorderが存在せず、旧バージョンと現バージョンではテンプレート構成が異なっているようです。

    よって私が提案した方法は難しそうです。


    2016年4月1日 7:15
    モデレータ
  •     

     ComboBox.Template.Childnamesの中に"DropDownBorder"という要素があったので、直接border を操作して

         Dim border As Border
         border = ComboBox.Template.FindName("DropDownBorder", ComboBox)
         border.Background = Brushes.Blue

    としましたが、Tak1waさんにお示しいただいた

    border.Background = new SolidColorBrush(Colors.Blue); ではドロップダウンボタンの外観が変わらないことが分かりました。

    2016年4月1日 7:25
  • こんな

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <StackPanel >
            <ComboBox Loaded="ComboBox_Loaded" IsEditable="False"/>
            <ComboBox Loaded="ComboBox_Loaded" IsEditable="True"/>
            <!-- .Net 4.0以降であれば PresentationFramework.Classicを参照(ローカルコピーも)してやるという手も
            <ComboBox >
                <ComboBox.Resources>
                    <ResourceDictionary>
                        <ResourceDictionary.MergedDictionaries>                        
                            <ResourceDictionary Source="/presentationframework.Classic;component/themes/classic.xaml" />
                        </ResourceDictionary.MergedDictionaries>
                    </ResourceDictionary>
                </ComboBox.Resources>
            </ComboBox>
            -->
        </StackPanel>
    </Window>

    Imports System.Windows.Controls.Primitives
    Class MainWindow
        Private Sub ComboBox_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim combo As ComboBox = CType(sender, ComboBox)
            ComboBoxButtonDecorator.AddButtonBorderDecorator(combo)
        End Sub
    End Class
    
    Class ComboBoxButtonDecorator
        Inherits Adorner
    
        Public Shared Sub AddButtonBorderDecorator(ByVal combo As ComboBox)
            Dim toggle As ToggleButton = GetToggleButton(combo)
    
            If (toggle IsNot Nothing) Then
                Dim decorateTarget As FrameworkElement
                decorateTarget = toggle
    
                Dim c As Integer = VisualTreeHelper.GetChildrenCount(toggle)
                Dim list As New List(Of Panel)
                Find(Of Panel)(toggle, list)
                If (list.Count > 0) Then
                    Dim p As Panel = list(0)
                    If (p.Children.Count = 1 AndAlso Not TypeOf p.Children(0) Is Shape) Then
                        decorateTarget = p.Children(0)
                    Else
                        decorateTarget = p
                    End If
                Else
                    Dim paths As New List(Of Path)
                    Find(Of Path)(toggle, paths)
                    If (paths.Count = 1) Then
                        Dim path As Path = paths(0)
                        decorateTarget = path.Parent
                    End If
                End If
                'End If
                Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(decorateTarget)
                If (layer IsNot Nothing) Then
                    Dim decorator As New ComboBoxButtonDecorator(decorateTarget)
                    layer.Add(decorator)
                End If
            End If
        End Sub
    
        Private Shared Function GetToggleButton(ByVal combo As ComboBox) As ToggleButton
            Dim list As New List(Of ToggleButton)
            Find(Of ToggleButton)(combo, list)
            For Each t As ToggleButton In list
                If (t.TemplatedParent Is combo) Then
                    Return t
                End If
            Next
            Return Nothing
        End Function
    
        Private Shared Sub Find(Of T As {DependencyObject})(ByVal d As DependencyObject, ByVal list As List(Of T))
            Dim count = VisualTreeHelper.GetChildrenCount(d)
            For i As Integer = 0 To count - 1
                Dim child As DependencyObject = VisualTreeHelper.GetChild(d, i)
                If (TypeOf child Is T) Then
                    list.Add(CType(child, T))
                End If
                Find(child, list)
            Next
        End Sub
    
    
    
        Sub New(ByVal ui As FrameworkElement)
            MyBase.New(ui)
    
            border = New Border()
            border.BorderThickness = New Thickness(1)
            border.BorderBrush = Brushes.Red
            'border.Background = New SolidColorBrush(Color.FromArgb(20, 0, 0, 255))
    
            Dim bnd As New Binding
            bnd.Path = New PropertyPath(FrameworkElement.ActualWidthProperty)
            bnd.Source = ui
            border.SetBinding(border.WidthProperty, bnd)
    
            bnd = New Binding()
            bnd.Path = New PropertyPath(FrameworkElement.ActualHeightProperty)
            bnd.Source = ui
            border.SetBinding(border.HeightProperty, bnd)
    
            'Dim w As Integer = CInt(ui.ActualWidth - border.BorderThickness.Left - border.BorderThickness.Right)
            'Dim h As Integer = CInt(ui.ActualHeight - border.BorderThickness.Top - border.BorderThickness.Bottom)
            'If (w > 0 AndAlso h > 0) Then
            '    Dim bmp As New System.Drawing.Bitmap(w, h)
            '    Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
            '        System.Windows.Forms.ControlPaint.DrawComboButton(g, 0, 0, bmp.Width, bmp.Height, Forms.ButtonState.Normal)
            '    End Using
    
            '    Using ms As New System.IO.MemoryStream()
    
            '        bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp)
            '        ms.Position = 0
            '        Dim bimg As BitmapImage = New BitmapImage()
            '        bimg.BeginInit()
            '        bimg.StreamSource = ms
            '        bimg.CacheOption = BitmapCacheOption.OnLoad
            '        bimg.EndInit()
    
            '        Dim img As New Image
            '        img.Source = bimg
            '        border.Child = img
            '    End Using
            'End If
        End Sub
    
        Private border As Border
    
        Protected Overrides ReadOnly Property VisualChildrenCount As Integer
            Get
                Return 1
            End Get
        End Property
        Protected Overrides Function GetVisualChild(index As Integer) As System.Windows.Media.Visual
            If (index <> 0) Then
                Return Nothing
            End If
            Return border
        End Function
        Protected Overrides Function MeasureOverride(constraint As System.Windows.Size) As System.Windows.Size
            border.Measure(constraint)
            Return border.DesiredSize
        End Function
        Protected Overrides Function ArrangeOverride(finalSize As System.Windows.Size) As System.Windows.Size
            border.Arrange(New Rect(New Point(0, 0), finalSize))
            Return New Size(border.ActualWidth, border.ActualHeight)
        End Function
    End Class

    Win7 + .net 3.5/4.0/4.5 + クラシック/Aero 
    Win8.1+ .net 3.5/4.0/4.6
    Win10 + .net 3.5/4.0/4.6
    で確認
    #テーマによってはまだ見逃しがあるかも。

    おまけ
    ComboBoxにclassicをコードで適用してみた。

    Class MainWindow
        Sub New()
            InitializeComponent()
    
            Dim uri As New Uri("/PresentationFramework.Classic;component/themes/classic.xaml", UriKind.Relative)
            Dim s As System.IO.Stream = Application.GetResourceStream(uri).Stream
            Dim bamlReader As New Baml2006.Baml2006Reader(s)
            Dim writer As New Xaml.XamlObjectWriter(bamlReader.SchemaContext)
            While (bamlReader.Read())
                writer.WriteNode(bamlReader)
            End While
            classic = CType(writer.Result(), ResourceDictionary)
        End Sub
    
        Private classic As ResourceDictionary
    
        Private Sub ComboBox_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim combo As ComboBox = CType(sender, ComboBox)
            combo.Resources.MergedDictionaries.Add(classic)
        End Sub
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2016年4月1日 11:57 おまけ追加
    • 回答としてマーク huahi11112 2016年4月1日 13:39
    2016年4月1日 11:32
  • ありがとうございます。gekkaさんには、1月4日の御回答を含めて感謝致します。VB.netで御回答いただくと私としてはスラスラと理解できますし充分参考になりました。開発はまだ始まったばかりなので、今後色々な問題が出るでしょうが他の回答者の皆様にも、引き続いてアドバイスをいただけるようお願いします。
    2016年4月1日 13:49
  • ComboBoxButtonDecoratorクラスを改造してみました。
    ToggleButton要素のプロパティーに直接アタッチできることが分かりました。

    VisualTreeHelperクラスの使い方はよく分かりました。どうもありがとうございました。



    'comboboxのドロップダウンボタンの配色を変えるためのクラス
    Class ComboBoxButtonDecorator

        '処理メイン
        Public Shared Sub AddButtonBorderDecorator(ByVal combo As ComboBox)
            'ToggleButtonクラスはCombobox右側のボタン▼を構成する要素である
            Dim toggle As ToggleButton = GetToggleButton(combo)
            '配色変更
            toggle.Background = Brushes.BlanchedAlmond
            toggle.BorderBrush = Brushes.Black

        End Sub

        'TemplatedParentの形式がComboBoxであるVisualTreeのDependencyObject形式要素を求める
        Private Shared Function GetToggleButton(ByVal combo As ComboBox) As ToggleButton
            Dim list As New List(Of ToggleButton)
            Find(Of ToggleButton)(combo, list)
            For Each t As ToggleButton In list
                If (t.TemplatedParent Is combo) Then
                    Return t
                End If
            Next
            Return Nothing
        End Function

        'dにはComboBox形式の変数が入る
        Private Shared Sub Find(Of T As {DependencyObject})(ByVal d As DependencyObject, ByVal list As List(Of T))
            Dim count = VisualTreeHelper.GetChildrenCount(d)
            For i As Integer = 0 To count - 1
                Dim child As DependencyObject = VisualTreeHelper.GetChild(d, i)
                If (TypeOf child Is T) Then
                    list.Add(CType(child, T))
                End If
                Find(child, list)
            Next
        End Sub

    End Class

    2016年4月2日 8:43