none
DataTemplateに使用されているコントロールの操作から、どのデータが操作されたのか取得する方法 RRS feed

  • 質問

  • 例えば次のようなDataTemplateにおいて
    CheckBoxをチェックされたタイミングで、どのLabel(TestDataインスタンス)がチェックされたのか
    コード側に通知してくれるようなイベントを作れないかと模索しています。

    <DataTemplate DataType="TestData">
        <StackPanel Orientation="Horizontal">
            <CheckBox IsChecked="{Binding Path=IsChecked}" />
            <TextBlock Text="{Binding Path=Label}" />
        </StackPanel>
    </DataTemplate>

    添付イベントを使ってみたりと四苦八苦してみたのですがうまい方法が思いつきませんでした。
    何か良い方法をご存じの方がいらっしゃいましたらご教示願います。

    以下にもう少し具体的なソースコードを記します。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:t="clr-namespace:TestAssembly"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.Resources>
                <HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type t:TreeViewItemData}" ItemsSource="{Binding Path=Items}">
                    <StackPanel Orientation="Horizontal">
                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" />
                        <TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </Grid.Resources>
            <TreeView ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" />
        </Grid>
    </Window>
    Class MainWindow 
    
        Private Sub MainWindow_Initialized(sender As Object, e As System.EventArgs) Handles Me.Initialized
    
            Dim rootItem As TreeViewItemData = New TreeViewItemData
            Dim item1 As TreeViewItemData = New TreeViewItemData("項目1")
            Dim item1_1 As TreeViewItemData = New TreeViewItemData("項目1-1")
            Dim item1_2 As TreeViewItemData = New TreeViewItemData("項目1-2")
            Dim item1_3 As TreeViewItemData = New TreeViewItemData("項目1-3")
            Dim item2 As TreeViewItemData = New TreeViewItemData("項目2")
            Dim item2_1 As TreeViewItemData = New TreeViewItemData("項目2-1")
            Dim item2_2 As TreeViewItemData = New TreeViewItemData("項目2-2")
            Dim item2_1_1 As TreeViewItemData = New TreeViewItemData("項目2-1-1")
    
            item1.Items.Add(item1_1)
            item1.Items.Add(item1_2)
            item1.Items.Add(item1_3)
    
            item2_1.Items.Add(item2_1_1)
    
            item2.Items.Add(item2_1)
            item2.Items.Add(item2_2)
    
            rootItem.Items.Add(item1)
            rootItem.Items.Add(item2)
    
            Me.DataContext = rootItem
        End Sub
    End Class
    Imports System.Collections.ObjectModel
    
    Public Class TreeViewItemData
        Inherits DependencyObject
    
        Public Shared ReadOnly IsCheckedProperty As DependencyProperty = DependencyProperty.Register("IsChecked", GetType(Boolean), GetType(TreeViewItemData))
        Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(TreeViewItemData))
        Private _items As ObservableCollection(Of TreeViewItemData) = New ObservableCollection(Of TreeViewItemData)
    
        Public Sub New()
    
        End Sub
    
        Public Sub New(ByVal label As String)
            Me.Label = label
        End Sub
    
        Public Property IsChecked As Boolean
            Get
                Return GetValue(IsCheckedProperty)
            End Get
            Set(value As Boolean)
                SetValue(IsCheckedProperty, value)
            End Set
        End Property
        Public Property Label As String
            Get
                Return GetValue(LabelProperty)
            End Get
            Set(value As String)
                SetValue(LabelProperty, value)
            End Set
        End Property
        Public ReadOnly Property Items As ObservableCollection(Of TreeViewItemData)
            Get
                Return _items
            End Get
        End Property
    End Class

    ↑実行結果

    上記のようなツリービューにおいて
    「項目1-1」のチェックボックスをチェックされたタイミングで
    「項目1-1」がチェックされたということをコード側で拾いたいのです。

    ※可能であれば
    Private Sub TreeViewItemData_Checked(sender As Object, e As RoutedEventArgs)
    で、sender、あるいはe.OriginalSourceの型がTreeViewItemData型のようなイメージで…

    添付イベントで<TreeView CheckBox.Checked="~" />のようにすればチェックイベントを拾うことはできるのですが(この場合うまくやらないとTreeViewItemのToggleButtonのCheckedイベントも拾いますが)、肝心のどのTreeViewItemDataインスタンスがチェックされたのかということが分かりません。。。

    開発環境
    VS2010
    実行環境
    .NET4.0

    以上、よろしくお願い致します。




    2012年12月17日 11:37

回答

  • 選択に使用するCheckBoxに添付プロパティで目印を設定してやれば間違えることないと思いますけどね。

    それでも選択した要素を知るためのイベントがほしいなら、ToggleButton.Checkedイベントを捕まえて、選択されたということを通知するRoutedEventを新たに投げてやればできると思います。

    Public Class ItemsControlChecker
        Public Delegate Sub ItemCheckedRoutedEventHandler(ByVal sender As Object, ByVal e As ItemCheckedRoutedEventArgs)
    
        ''' <summary>>ItemsControlの要素で、要素のチェックがあった時に発生するイベント</summary>
        Public Shared ReadOnly ItemCheckedEvent As RoutedEvent = _
                          EventManager.RegisterRoutedEvent("ItemChecked", _
                          RoutingStrategy.Bubble, _
                          GetType(ItemCheckedRoutedEventHandler), GetType(ItemsControlChecker))
    
        Public Shared Sub AddItemCheckedHandler(ByVal d As DependencyObject, ByVal handler As ItemCheckedRoutedEventHandler)
            Dim uie As UIElement = TryCast(d, UIElement)
            If uie IsNot Nothing Then
                uie.AddHandler(ItemCheckedEvent, handler)
            End If
        End Sub
        Public Shared Sub RemoveItemCheckedHandler(ByVal d As DependencyObject, ByVal handler As ItemCheckedRoutedEventHandler)
            Dim uie As UIElement = TryCast(d, UIElement)
            If uie IsNot Nothing Then
                uie.RemoveHandler(ItemCheckedEvent, handler)
            End If
        End Sub
    
        Public Shared Function GetIsItemChecker(ByVal element As DependencyObject) As Boolean
            Return element.GetValue(IsItemCheckerProperty)
        End Function
    
        Public Shared Sub SetIsItemChecker(ByVal element As DependencyObject, ByVal value As Boolean)
            element.SetValue(IsItemCheckerProperty, value)
        End Sub
    
    
        ''' <summary>ItemsControlの要素でこの添付プロパティがTrueのCheckBoxでItemCheckedRoutedEventが発生する</summary>
        Public Shared ReadOnly IsItemCheckerProperty As  _
                               DependencyProperty = DependencyProperty.RegisterAttached("IsItemChecker", _
                               GetType(Boolean), GetType(ItemsControlChecker), _
                               New FrameworkPropertyMetadata(False, AddressOf PropertyChangedCallback))
    
        Public Shared Sub PropertyChangedCallback(d As System.Windows.DependencyObject, e As System.Windows.DependencyPropertyChangedEventArgs)
            Dim uie As UIElement = TryCast(d, UIElement)
            If (DirectCast(e.NewValue, Boolean)) Then
                uie.AddHandler(Primitives.ToggleButton.CheckedEvent, New RoutedEventHandler(AddressOf Checked))
            Else
                uie.RemoveHandler(Primitives.ToggleButton.CheckedEvent, New RoutedEventHandler(AddressOf Checked))
            End If
        End Sub
    
        Private Shared Sub Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If (sender Is e.OriginalSource) Then
                e.Handled = True
                Dim uie As UIElement = TryCast(sender, UIElement)
                uie.RaiseEvent(New ItemCheckedRoutedEventArgs(uie, uie.GetValue(FrameworkElement.DataContextProperty)))
            End If
        End Sub
    End Class
    
    Public Class ItemCheckedRoutedEventArgs
        Inherits System.Windows.RoutedEventArgs
        Public Sub New(ByVal source As Object, ByVal datacontext As Object)
            MyBase.New(ItemsControlChecker.ItemCheckedEvent, source)
            Me.CheckedItemData = datacontext
        End Sub
        Public Property CheckedItemData As Object
    End Class

    <Grid>
        <Grid.Resources>
            <HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type app:TreeViewItemData}" ItemsSource="{Binding Path=Items}">
                <StackPanel Orientation="Horizontal">
                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" 
                            app:ItemsControlChecker.IsItemChecker="true"  />
                    <CheckBox Content="Dummy" />
                    <TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </Grid.Resources>
    
        <TreeView ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" 
            app:ItemsControlChecker.ItemChecked="treeview_ItemChecked"/>
    </Grid>
    #自作添付イベントはエラーが表示されっぱなしになるのがなぁ


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

    2012年12月17日 13:29
  • コードビハインドにベタ書きで良いなら、CheckBox.Checkedイベントを拾ってsender(中身はCheckBox)からDataContextを取ってくれば良いだけのような気がしますが・・・
    (添付イベントでも同様の方法で特定できるはず)

    あるいはTreeViewItemData内でイベントを拾いたいなら、RelayCommandというクラスを作って、BlendSDKのCallMethodBehaviorと組み合わせるという手段もあります。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:t="clr-namespace:TestAssembly"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.Resources>
                <HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type t:TreeViewItemData}" ItemsSource="{Binding Path=Items}">
                    <StackPanel Orientation="Horizontal">
                        <!--※ベタ書きの場合。一応参考までに-->
                        <!--<CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" Checked="CheckBox_Checked"/>-->
                        
                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Checked">
                                    <core:CallMethodAction TargetObject="{Binding}" MethodName="OnChecked"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </CheckBox>
                        <TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </Grid.Resources>
            <TreeView ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" />
        </Grid>
    </Window>


    Class MainWindow 
    
        Private Sub MainWindow_Initialized(sender As Object, e As System.EventArgs) Handles Me.Initialized
    
            Dim rootItem As TreeViewItemData = New TreeViewItemData
            Dim item1 As TreeViewItemData = New TreeViewItemData("項目1")
            Dim item1_1 As TreeViewItemData = New TreeViewItemData("項目1-1")
            Dim item1_2 As TreeViewItemData = New TreeViewItemData("項目1-2")
            Dim item1_3 As TreeViewItemData = New TreeViewItemData("項目1-3")
            Dim item2 As TreeViewItemData = New TreeViewItemData("項目2")
            Dim item2_1 As TreeViewItemData = New TreeViewItemData("項目2-1")
            Dim item2_2 As TreeViewItemData = New TreeViewItemData("項目2-2")
            Dim item2_1_1 As TreeViewItemData = New TreeViewItemData("項目2-1-1")
    
            item1.Items.Add(item1_1)
            item1.Items.Add(item1_2)
            item1.Items.Add(item1_3)
    
            item2_1.Items.Add(item2_1_1)
    
            item2.Items.Add(item2_1)
            item2.Items.Add(item2_2)
    
            rootItem.Items.Add(item1)
            rootItem.Items.Add(item2)
    
            Me.DataContext = rootItem
        End Sub
    
        '※ベタ書きの場合はここが呼ばれる。
        Private Sub CheckBox_Checked(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim checkBox As CheckBox = CType(sender, CheckBox)
            Dim item As TreeViewItemData = checkBox.DataContext
            MessageBox.Show(item.Label)
        End Sub
    
    End Class
    


    Imports System.Collections.ObjectModel
    
    Public Class TreeViewItemData
        Inherits DependencyObject
    
        Public Shared ReadOnly IsCheckedProperty As DependencyProperty = DependencyProperty.Register("IsChecked", GetType(Boolean), GetType(TreeViewItemData))
        Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(TreeViewItemData))
        Private _items As ObservableCollection(Of TreeViewItemData) = New ObservableCollection(Of TreeViewItemData)
    
        Public Sub New()
    
        End Sub
    
        Public Sub New(ByVal label As String)
            Me.Label = label
        End Sub
    
        Public Property IsChecked As Boolean
            Get
                Return GetValue(IsCheckedProperty)
            End Get
            Set(value As Boolean)
                SetValue(IsCheckedProperty, value)
            End Set
        End Property
        Public Property Label As String
            Get
                Return GetValue(LabelProperty)
            End Get
            Set(value As String)
                SetValue(LabelProperty, value)
            End Set
        End Property
        Public ReadOnly Property Items As ObservableCollection(Of TreeViewItemData)
            Get
                Return _items
            End Get
        End Property
    
        Public Sub OnChecked(sender As System.Object, e As System.Windows.RoutedEventArgs)
            MessageBox.Show(Me.Label)
        End Sub
    End Class


    Public Class RelayCommand
        Implements ICommand
    
        Private _canExecuteAction As Func(Of Object, Boolean)
        Private _executeAction As Action(Of Object)
    
        Public Sub New(ByVal executeAction As Action(Of Object),
                       ByVal canExecuteAction As Func(Of Object, Boolean))
            Me._executeAction = executeAction
            Me._canExecuteAction = canExecuteAction
        End Sub
    
        Public Function CanExecute(ByVal parameter As Object) As Boolean _
                                                            Implements ICommand.CanExecute
            Return _canExecuteAction(parameter)
        End Function
    
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
                                                            Implements ICommand.CanExecuteChanged
    
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
            _executeAction(parameter)
        End Sub
    
    End Class
    

    コードを実行するにはBlendSDKをインストールしてMicrosoft.Expression.InteractionsとSystem.Windows.Interactivityへの参照を追加して下さい。
    RelayCommandの詳細については http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx に詳しいことが載ってます。
    あと、コードビハインドにベタ書きの場合のコードもコメントアウトして含まれてますので、そっちで良ければそのほうが簡単です。

    2012年12月17日 13:31
  • 私もみっとさんと同じで、Microsoft.Expression.InteractionsとSystem.Windows.Interactivityを使うと思います。以下、TriggerActionクラスを継承し、RelayCommandによりハンドラを起動しています。

    <Window x:Class="DataTemplateCheckBox"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:t="clr-namespace:test2010wpfvb"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
    	Title="DataTemplateCheckBox" Height="300" Width="300">
        
    	<Grid Name="Grid1">
    		<Grid.Resources>
    			<HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type t:TreeViewItemData}" ItemsSource="{Binding Items}">
    				<StackPanel Orientation="Horizontal">
    					<CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False">
    						<i:Interaction.Triggers>
    							<i:EventTrigger EventName="Checked">
    								<t:MyCommandAction Command="{Binding DataContext.CheckBoxCheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Mode=OneWay}"
    												   CommandParameter="{Binding }" />
    							</i:EventTrigger>
    						</i:Interaction.Triggers>
    					</CheckBox>
    					<TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
    				</StackPanel>
    			</HierarchicalDataTemplate>
    		</Grid.Resources>
    		<TreeView ItemsSource="{Binding データ.Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" />
    	</Grid>
    	
    </Window>
    Imports System.Collections.ObjectModel
    Imports System.Windows.Interactivity
    Public Class DataTemplateCheckBox
        Private Sub DataTemplateCheckBox_Initialized(sender As System.Object, e As System.EventArgs) Handles MyBase.Initialized
            Dim rootItem As TreeViewItemData = New TreeViewItemData
            Dim item1 As TreeViewItemData = New TreeViewItemData("項目1")
            Dim item1_1 As TreeViewItemData = New TreeViewItemData("項目1-1")
            Dim item1_2 As TreeViewItemData = New TreeViewItemData("項目1-2")
            Dim item1_3 As TreeViewItemData = New TreeViewItemData("項目1-3")
            Dim item2 As TreeViewItemData = New TreeViewItemData("項目2")
            Dim item2_1 As TreeViewItemData = New TreeViewItemData("項目2-1")
            Dim item2_2 As TreeViewItemData = New TreeViewItemData("項目2-2")
            Dim item2_1_1 As TreeViewItemData = New TreeViewItemData("項目2-1-1")
            item1.Items.Add(item1_1)
            item1.Items.Add(item1_2)
            item1.Items.Add(item1_3)
            item2_1.Items.Add(item2_1_1)
            item2.Items.Add(item2_1)
            item2.Items.Add(item2_2)
            rootItem.Items.Add(item1)
            rootItem.Items.Add(item2)
            Me.データ = rootItem
            'コマンドの設定
            CheckBoxCheckedCommand = New RelayCommand(AddressOf CheckedChanged)
            Me.DataContext = Me
        End Sub
        'プロパティ
        Public Property データ As TreeViewItemData
        'コマンドの定義
        Public Property CheckBoxCheckedCommand As ICommand
        'コマンドハンドラ
        Private Sub CheckedChanged(ByVal obj As Object)
            System.Diagnostics.Debug.WriteLine(DirectCast(obj, TreeViewItemData).Label)
        End Sub
    End Class

    Public Class TreeViewItemData
        Inherits DependencyObject
        Public Shared ReadOnly IsCheckedProperty As DependencyProperty = DependencyProperty.Register("IsChecked", GetType(Boolean), GetType(TreeViewItemData))
        Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(TreeViewItemData))
        Private _items As ObservableCollection(Of TreeViewItemData) = New ObservableCollection(Of TreeViewItemData)
        Public Sub New()
        End Sub
        Public Sub New(ByVal label As String)
            Me.Label = label
        End Sub
        Public Property IsChecked As Boolean
            Get
                Return GetValue(IsCheckedProperty)
            End Get
            Set(value As Boolean)
                SetValue(IsCheckedProperty, value)
            End Set
        End Property
        Public Property Label As String
            Get
                Return GetValue(LabelProperty)
            End Get
            Set(value As String)
                SetValue(LabelProperty, value)
            End Set
        End Property
        Public ReadOnly Property Items As ObservableCollection(Of TreeViewItemData)
            Get
                Return _items
            End Get
        End Property
    End Class

    Public Class MyCommandAction
        Inherits TriggerAction(Of FrameworkElement)
        Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.Register("Command", GetType(ICommand), GetType(MyCommandAction), New UIPropertyMetadata())
        Public Property Command() As ICommand
            Get
                Return DirectCast(GetValue(CommandProperty), ICommand)
            End Get
            Set(value As ICommand)
                SetValue(CommandProperty, value)
            End Set
        End Property
        Public Shared ReadOnly CommandParameterProperty As DependencyProperty = DependencyProperty.Register("CommandParameter", GetType(Object), GetType(MyCommandAction), New UIPropertyMetadata())
        Public Property CommandParameter() As Object
            Get
                Return DirectCast(GetValue(CommandParameterProperty), Object)
            End Get
            Set(value As Object)
                SetValue(CommandParameterProperty, value)
            End Set
        End Property
        Protected Overrides Sub Invoke(obj As Object)
            Command.Execute(CommandParameter)
        End Sub
    End Class

    Imports System.ComponentModel
    Imports System.Windows.Input
    Public Class RelayCommand
        Implements ICommand
        Private _canExecuteAction As Func(Of Object, Boolean)
        Private _executeAction As Action(Of Object)
        Public Sub New(ByVal executeAction As Action(Of Object),
                       ByVal canExecuteAction As Func(Of Object, Boolean))
            Me._executeAction = executeAction
            Me._canExecuteAction = canExecuteAction
        End Sub
        Public Sub New(ByVal executeAction As Action(Of Object))
            Me._executeAction = executeAction
            _canExecuteAction = Function()
                                    Return True
                                End Function
        End Sub
        Public Function CanExecute(ByVal parameter As Object) As Boolean _
                                                            Implements ICommand.CanExecute
            Return _canExecuteAction(parameter)
        End Function
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
                                                            Implements ICommand.CanExecuteChanged
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
            _executeAction(parameter)
        End Sub
    End Class
    #う~ん、空白行がねぐられたり、うまくコードが載っけられないなぁ・・・・

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年12月18日 5:17
    モデレータ

すべての返信

  • 選択に使用するCheckBoxに添付プロパティで目印を設定してやれば間違えることないと思いますけどね。

    それでも選択した要素を知るためのイベントがほしいなら、ToggleButton.Checkedイベントを捕まえて、選択されたということを通知するRoutedEventを新たに投げてやればできると思います。

    Public Class ItemsControlChecker
        Public Delegate Sub ItemCheckedRoutedEventHandler(ByVal sender As Object, ByVal e As ItemCheckedRoutedEventArgs)
    
        ''' <summary>>ItemsControlの要素で、要素のチェックがあった時に発生するイベント</summary>
        Public Shared ReadOnly ItemCheckedEvent As RoutedEvent = _
                          EventManager.RegisterRoutedEvent("ItemChecked", _
                          RoutingStrategy.Bubble, _
                          GetType(ItemCheckedRoutedEventHandler), GetType(ItemsControlChecker))
    
        Public Shared Sub AddItemCheckedHandler(ByVal d As DependencyObject, ByVal handler As ItemCheckedRoutedEventHandler)
            Dim uie As UIElement = TryCast(d, UIElement)
            If uie IsNot Nothing Then
                uie.AddHandler(ItemCheckedEvent, handler)
            End If
        End Sub
        Public Shared Sub RemoveItemCheckedHandler(ByVal d As DependencyObject, ByVal handler As ItemCheckedRoutedEventHandler)
            Dim uie As UIElement = TryCast(d, UIElement)
            If uie IsNot Nothing Then
                uie.RemoveHandler(ItemCheckedEvent, handler)
            End If
        End Sub
    
        Public Shared Function GetIsItemChecker(ByVal element As DependencyObject) As Boolean
            Return element.GetValue(IsItemCheckerProperty)
        End Function
    
        Public Shared Sub SetIsItemChecker(ByVal element As DependencyObject, ByVal value As Boolean)
            element.SetValue(IsItemCheckerProperty, value)
        End Sub
    
    
        ''' <summary>ItemsControlの要素でこの添付プロパティがTrueのCheckBoxでItemCheckedRoutedEventが発生する</summary>
        Public Shared ReadOnly IsItemCheckerProperty As  _
                               DependencyProperty = DependencyProperty.RegisterAttached("IsItemChecker", _
                               GetType(Boolean), GetType(ItemsControlChecker), _
                               New FrameworkPropertyMetadata(False, AddressOf PropertyChangedCallback))
    
        Public Shared Sub PropertyChangedCallback(d As System.Windows.DependencyObject, e As System.Windows.DependencyPropertyChangedEventArgs)
            Dim uie As UIElement = TryCast(d, UIElement)
            If (DirectCast(e.NewValue, Boolean)) Then
                uie.AddHandler(Primitives.ToggleButton.CheckedEvent, New RoutedEventHandler(AddressOf Checked))
            Else
                uie.RemoveHandler(Primitives.ToggleButton.CheckedEvent, New RoutedEventHandler(AddressOf Checked))
            End If
        End Sub
    
        Private Shared Sub Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If (sender Is e.OriginalSource) Then
                e.Handled = True
                Dim uie As UIElement = TryCast(sender, UIElement)
                uie.RaiseEvent(New ItemCheckedRoutedEventArgs(uie, uie.GetValue(FrameworkElement.DataContextProperty)))
            End If
        End Sub
    End Class
    
    Public Class ItemCheckedRoutedEventArgs
        Inherits System.Windows.RoutedEventArgs
        Public Sub New(ByVal source As Object, ByVal datacontext As Object)
            MyBase.New(ItemsControlChecker.ItemCheckedEvent, source)
            Me.CheckedItemData = datacontext
        End Sub
        Public Property CheckedItemData As Object
    End Class

    <Grid>
        <Grid.Resources>
            <HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type app:TreeViewItemData}" ItemsSource="{Binding Path=Items}">
                <StackPanel Orientation="Horizontal">
                    <CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" 
                            app:ItemsControlChecker.IsItemChecker="true"  />
                    <CheckBox Content="Dummy" />
                    <TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
                </StackPanel>
            </HierarchicalDataTemplate>
        </Grid.Resources>
    
        <TreeView ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" 
            app:ItemsControlChecker.ItemChecked="treeview_ItemChecked"/>
    </Grid>
    #自作添付イベントはエラーが表示されっぱなしになるのがなぁ


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

    2012年12月17日 13:29
  • コードビハインドにベタ書きで良いなら、CheckBox.Checkedイベントを拾ってsender(中身はCheckBox)からDataContextを取ってくれば良いだけのような気がしますが・・・
    (添付イベントでも同様の方法で特定できるはず)

    あるいはTreeViewItemData内でイベントを拾いたいなら、RelayCommandというクラスを作って、BlendSDKのCallMethodBehaviorと組み合わせるという手段もあります。

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:t="clr-namespace:TestAssembly"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.Resources>
                <HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type t:TreeViewItemData}" ItemsSource="{Binding Path=Items}">
                    <StackPanel Orientation="Horizontal">
                        <!--※ベタ書きの場合。一応参考までに-->
                        <!--<CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" Checked="CheckBox_Checked"/>-->
                        
                        <CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False" >
                            <i:Interaction.Triggers>
                                <i:EventTrigger EventName="Checked">
                                    <core:CallMethodAction TargetObject="{Binding}" MethodName="OnChecked"/>
                                </i:EventTrigger>
                            </i:Interaction.Triggers>
                        </CheckBox>
                        <TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </Grid.Resources>
            <TreeView ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" />
        </Grid>
    </Window>


    Class MainWindow 
    
        Private Sub MainWindow_Initialized(sender As Object, e As System.EventArgs) Handles Me.Initialized
    
            Dim rootItem As TreeViewItemData = New TreeViewItemData
            Dim item1 As TreeViewItemData = New TreeViewItemData("項目1")
            Dim item1_1 As TreeViewItemData = New TreeViewItemData("項目1-1")
            Dim item1_2 As TreeViewItemData = New TreeViewItemData("項目1-2")
            Dim item1_3 As TreeViewItemData = New TreeViewItemData("項目1-3")
            Dim item2 As TreeViewItemData = New TreeViewItemData("項目2")
            Dim item2_1 As TreeViewItemData = New TreeViewItemData("項目2-1")
            Dim item2_2 As TreeViewItemData = New TreeViewItemData("項目2-2")
            Dim item2_1_1 As TreeViewItemData = New TreeViewItemData("項目2-1-1")
    
            item1.Items.Add(item1_1)
            item1.Items.Add(item1_2)
            item1.Items.Add(item1_3)
    
            item2_1.Items.Add(item2_1_1)
    
            item2.Items.Add(item2_1)
            item2.Items.Add(item2_2)
    
            rootItem.Items.Add(item1)
            rootItem.Items.Add(item2)
    
            Me.DataContext = rootItem
        End Sub
    
        '※ベタ書きの場合はここが呼ばれる。
        Private Sub CheckBox_Checked(sender As System.Object, e As System.Windows.RoutedEventArgs)
            Dim checkBox As CheckBox = CType(sender, CheckBox)
            Dim item As TreeViewItemData = checkBox.DataContext
            MessageBox.Show(item.Label)
        End Sub
    
    End Class
    


    Imports System.Collections.ObjectModel
    
    Public Class TreeViewItemData
        Inherits DependencyObject
    
        Public Shared ReadOnly IsCheckedProperty As DependencyProperty = DependencyProperty.Register("IsChecked", GetType(Boolean), GetType(TreeViewItemData))
        Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(TreeViewItemData))
        Private _items As ObservableCollection(Of TreeViewItemData) = New ObservableCollection(Of TreeViewItemData)
    
        Public Sub New()
    
        End Sub
    
        Public Sub New(ByVal label As String)
            Me.Label = label
        End Sub
    
        Public Property IsChecked As Boolean
            Get
                Return GetValue(IsCheckedProperty)
            End Get
            Set(value As Boolean)
                SetValue(IsCheckedProperty, value)
            End Set
        End Property
        Public Property Label As String
            Get
                Return GetValue(LabelProperty)
            End Get
            Set(value As String)
                SetValue(LabelProperty, value)
            End Set
        End Property
        Public ReadOnly Property Items As ObservableCollection(Of TreeViewItemData)
            Get
                Return _items
            End Get
        End Property
    
        Public Sub OnChecked(sender As System.Object, e As System.Windows.RoutedEventArgs)
            MessageBox.Show(Me.Label)
        End Sub
    End Class


    Public Class RelayCommand
        Implements ICommand
    
        Private _canExecuteAction As Func(Of Object, Boolean)
        Private _executeAction As Action(Of Object)
    
        Public Sub New(ByVal executeAction As Action(Of Object),
                       ByVal canExecuteAction As Func(Of Object, Boolean))
            Me._executeAction = executeAction
            Me._canExecuteAction = canExecuteAction
        End Sub
    
        Public Function CanExecute(ByVal parameter As Object) As Boolean _
                                                            Implements ICommand.CanExecute
            Return _canExecuteAction(parameter)
        End Function
    
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
                                                            Implements ICommand.CanExecuteChanged
    
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
            _executeAction(parameter)
        End Sub
    
    End Class
    

    コードを実行するにはBlendSDKをインストールしてMicrosoft.Expression.InteractionsとSystem.Windows.Interactivityへの参照を追加して下さい。
    RelayCommandの詳細については http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx に詳しいことが載ってます。
    あと、コードビハインドにベタ書きの場合のコードもコメントアウトして含まれてますので、そっちで良ければそのほうが簡単です。

    2012年12月17日 13:31
  • 私もみっとさんと同じで、Microsoft.Expression.InteractionsとSystem.Windows.Interactivityを使うと思います。以下、TriggerActionクラスを継承し、RelayCommandによりハンドラを起動しています。

    <Window x:Class="DataTemplateCheckBox"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:t="clr-namespace:test2010wpfvb"
    	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
    	Title="DataTemplateCheckBox" Height="300" Width="300">
        
    	<Grid Name="Grid1">
    		<Grid.Resources>
    			<HierarchicalDataTemplate x:Key="TreeViewItemDataTemplate" DataType="{x:Type t:TreeViewItemData}" ItemsSource="{Binding Items}">
    				<StackPanel Orientation="Horizontal">
    					<CheckBox VerticalAlignment="Center" IsChecked="{Binding Path=IsChecked}" Focusable="False">
    						<i:Interaction.Triggers>
    							<i:EventTrigger EventName="Checked">
    								<t:MyCommandAction Command="{Binding DataContext.CheckBoxCheckedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Mode=OneWay}"
    												   CommandParameter="{Binding }" />
    							</i:EventTrigger>
    						</i:Interaction.Triggers>
    					</CheckBox>
    					<TextBlock  VerticalAlignment="Center" Margin="3,0,0,0" Text="{Binding Path=Label}" />
    				</StackPanel>
    			</HierarchicalDataTemplate>
    		</Grid.Resources>
    		<TreeView ItemsSource="{Binding データ.Items}" ItemTemplate="{StaticResource TreeViewItemDataTemplate}" />
    	</Grid>
    	
    </Window>
    Imports System.Collections.ObjectModel
    Imports System.Windows.Interactivity
    Public Class DataTemplateCheckBox
        Private Sub DataTemplateCheckBox_Initialized(sender As System.Object, e As System.EventArgs) Handles MyBase.Initialized
            Dim rootItem As TreeViewItemData = New TreeViewItemData
            Dim item1 As TreeViewItemData = New TreeViewItemData("項目1")
            Dim item1_1 As TreeViewItemData = New TreeViewItemData("項目1-1")
            Dim item1_2 As TreeViewItemData = New TreeViewItemData("項目1-2")
            Dim item1_3 As TreeViewItemData = New TreeViewItemData("項目1-3")
            Dim item2 As TreeViewItemData = New TreeViewItemData("項目2")
            Dim item2_1 As TreeViewItemData = New TreeViewItemData("項目2-1")
            Dim item2_2 As TreeViewItemData = New TreeViewItemData("項目2-2")
            Dim item2_1_1 As TreeViewItemData = New TreeViewItemData("項目2-1-1")
            item1.Items.Add(item1_1)
            item1.Items.Add(item1_2)
            item1.Items.Add(item1_3)
            item2_1.Items.Add(item2_1_1)
            item2.Items.Add(item2_1)
            item2.Items.Add(item2_2)
            rootItem.Items.Add(item1)
            rootItem.Items.Add(item2)
            Me.データ = rootItem
            'コマンドの設定
            CheckBoxCheckedCommand = New RelayCommand(AddressOf CheckedChanged)
            Me.DataContext = Me
        End Sub
        'プロパティ
        Public Property データ As TreeViewItemData
        'コマンドの定義
        Public Property CheckBoxCheckedCommand As ICommand
        'コマンドハンドラ
        Private Sub CheckedChanged(ByVal obj As Object)
            System.Diagnostics.Debug.WriteLine(DirectCast(obj, TreeViewItemData).Label)
        End Sub
    End Class

    Public Class TreeViewItemData
        Inherits DependencyObject
        Public Shared ReadOnly IsCheckedProperty As DependencyProperty = DependencyProperty.Register("IsChecked", GetType(Boolean), GetType(TreeViewItemData))
        Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(TreeViewItemData))
        Private _items As ObservableCollection(Of TreeViewItemData) = New ObservableCollection(Of TreeViewItemData)
        Public Sub New()
        End Sub
        Public Sub New(ByVal label As String)
            Me.Label = label
        End Sub
        Public Property IsChecked As Boolean
            Get
                Return GetValue(IsCheckedProperty)
            End Get
            Set(value As Boolean)
                SetValue(IsCheckedProperty, value)
            End Set
        End Property
        Public Property Label As String
            Get
                Return GetValue(LabelProperty)
            End Get
            Set(value As String)
                SetValue(LabelProperty, value)
            End Set
        End Property
        Public ReadOnly Property Items As ObservableCollection(Of TreeViewItemData)
            Get
                Return _items
            End Get
        End Property
    End Class

    Public Class MyCommandAction
        Inherits TriggerAction(Of FrameworkElement)
        Public Shared ReadOnly CommandProperty As DependencyProperty = DependencyProperty.Register("Command", GetType(ICommand), GetType(MyCommandAction), New UIPropertyMetadata())
        Public Property Command() As ICommand
            Get
                Return DirectCast(GetValue(CommandProperty), ICommand)
            End Get
            Set(value As ICommand)
                SetValue(CommandProperty, value)
            End Set
        End Property
        Public Shared ReadOnly CommandParameterProperty As DependencyProperty = DependencyProperty.Register("CommandParameter", GetType(Object), GetType(MyCommandAction), New UIPropertyMetadata())
        Public Property CommandParameter() As Object
            Get
                Return DirectCast(GetValue(CommandParameterProperty), Object)
            End Get
            Set(value As Object)
                SetValue(CommandParameterProperty, value)
            End Set
        End Property
        Protected Overrides Sub Invoke(obj As Object)
            Command.Execute(CommandParameter)
        End Sub
    End Class

    Imports System.ComponentModel
    Imports System.Windows.Input
    Public Class RelayCommand
        Implements ICommand
        Private _canExecuteAction As Func(Of Object, Boolean)
        Private _executeAction As Action(Of Object)
        Public Sub New(ByVal executeAction As Action(Of Object),
                       ByVal canExecuteAction As Func(Of Object, Boolean))
            Me._executeAction = executeAction
            Me._canExecuteAction = canExecuteAction
        End Sub
        Public Sub New(ByVal executeAction As Action(Of Object))
            Me._executeAction = executeAction
            _canExecuteAction = Function()
                                    Return True
                                End Function
        End Sub
        Public Function CanExecute(ByVal parameter As Object) As Boolean _
                                                            Implements ICommand.CanExecute
            Return _canExecuteAction(parameter)
        End Function
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
                                                            Implements ICommand.CanExecuteChanged
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
            _executeAction(parameter)
        End Sub
    End Class
    #う~ん、空白行がねぐられたり、うまくコードが載っけられないなぁ・・・・

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年12月18日 5:17
    モデレータ
  • にーもにっくさん、こんにちは。
    返信下さった皆さまの投稿が参考になったと思われますので、勝手ながら私の方で回答マークを付けさせて頂きました。

    参考になった投稿には、質問者が回答マークを付けることができます。またマークは にーもにっくさんが後から外すことも可能です。
    他の方が後から閲覧した際参考にして頂くためにも、ぜひ回答をマークして頂くようお勧めいたします。


    ひらぽん http://d.hatena.ne.jp/hilapon/

    2012年12月20日 2:17
    モデレータ