トップ回答者
DataTemplateに使用されているコントロールの操作から、どのデータが操作されたのか取得する方法

質問
-
例えば次のような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:56
回答
-
選択に使用する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!)
- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2:17
-
コードビハインドにベタ書きで良いなら、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 に詳しいことが載ってます。
あと、コードビハインドにベタ書きの場合のコードもコメントアウトして含まれてますので、そっちで良ければそのほうが簡単です。- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2:17
-
私もみっとさんと同じで、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/
- 編集済み trapemiyaModerator 2012年12月18日 5:38
- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2: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!)
- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2:17
-
コードビハインドにベタ書きで良いなら、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 に詳しいことが載ってます。
あと、コードビハインドにベタ書きの場合のコードもコメントアウトして含まれてますので、そっちで良ければそのほうが簡単です。- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2:17
-
私もみっとさんと同じで、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/
- 編集済み trapemiyaModerator 2012年12月18日 5:38
- 回答の候補に設定 ひらぽんModerator 2012年12月19日 3:12
- 回答としてマーク ひらぽんModerator 2012年12月20日 2:17