トップ回答者
ControlTemplate内で定義したコントロールのイベント取得方法

質問
-
Application.ResourcesでListBox内にCheckBoxを配置するStyleの設定をしています。
<Application.Resources> <Style x:Key="CheckedListBoxStyle" TargetType="{x:Type ListBox}"> <Setter Property="SelectionMode" Value="Multiple" /> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <CheckBox Content="{Binding Name}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" /> </ControlTemplate> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> </Style> </Application.Resources>
各画面でListBoxにスタイルを適用した場合に、CheckBoxのクリックイベントを取得したいのですが、
どのように取得すればいいかがわかりません。
ご教示いただけますでしょうか。宜しくお願いします。
回答
-
直接イベントとる方法はVisualTreeいじくったりしないといけないのでめんどくさいですが以下の方法でViewModelで拾うことは出来ると思います。
- CheckBoxにEventTriggerBehaviorでClickイベントを拾う
- EventTriggerBehaviorにInvokeCommandActionを設定してViewModelのCommandにバインドする
- コマンドのExecuteが呼ばれたらClickされたとき
こんな感じでどうでしょうか。
かずき Blog:http://d.hatena.ne.jp/okazuki/
- 回答としてマーク れいじ 2015年7月1日 5:26
-
ToggleButton.CheckedとToggleButton.Uncheckedイベントはバブルイベントなので、外側でイベントを捕まえることもできます。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="300"> <DockPanel> <Button DockPanel.Dock="Bottom" Content="モデル側から変更してみるテスト" Click="Button_Click" Margin="10"/> <UniformGrid Columns="2"> <ListBox ItemsSource="{Binding}" Style="{StaticResource CheckedListBoxStyle}" x:Name="listBox1" ToggleButton.Checked="ListBox_CheckedChanged" ToggleButton.Unchecked="ListBox_CheckedChanged"> </ListBox> <ListBox ItemsSource="{Binding}" Style="{StaticResource CheckedListBoxStyle}" x:Name="listBox2" ToggleButton.Checked="ListBox_CheckedChanged" ToggleButton.Unchecked="ListBox_CheckedChanged"> </ListBox> </UniformGrid> </DockPanel> </Window>
C#
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.ComponentModel; namespace WpfApplication1 { public partial class MainWindow : Window { List<TestItem> testItems; public MainWindow() { InitializeComponent(); testItems = new List<TestItem>(); testItems.Add(new TestItem("A")); testItems.Add(new TestItem("B")); this.listBox1.DataContext = testItems; testItems = new List<TestItem>(); testItems.Add(new TestItem("C")); testItems.Add(new TestItem("D")); this.listBox2.DataContext = testItems; } private void Button_Click(object sender, RoutedEventArgs e) { testItems[0].IsSelected = !testItems[0].IsSelected; } private void ListBox_CheckedChanged(object sender, RoutedEventArgs e) { CheckBox checkBox = Check_CheckedListBox_CheckedChanged(sender, e); if (checkBox != null) { string text; if (checkBox.IsChecked == true) { text = "Checked"; } else if (checkBox.IsChecked == false) { text = "Unchecked"; } else { text = "Unknown"; } var listBox = (ListBox)sender; MessageBox.Show(string.Format("{0}\r\n{1}\r\n{2}", text, listBox.Name, checkBox.Content ?? "NULL")); } } /// <summary>CheckBoxがCheckedListBoxのCheckで変更されたのか調べる </summary> /// <returns>CheckedListBoxのであれば、そのCheckBoxを返す</returns> private static CheckBox Check_CheckedListBox_CheckedChanged(object sender, RoutedEventArgs e) { var checkBox = e.OriginalSource as CheckBox; if (checkBox != null)//CheckBoxのチェックがイベントを発生させた { ListBoxItem item = checkBox.TemplatedParent as ListBoxItem; //ListBoxItem,ListViewItem.ComboxItem..... if (item != null)//CheckBoxがListBoxのテンプレートで作られた { var panel = VisualTreeHelper.GetParent(item);//ListBoxItemの配置されているパネル if (panel != null)//パネルからItemsContorlを取得 { ItemsControl itemsControl = ItemsControl.GetItemsOwner(panel);//ListBoxを取得; if (itemsControl == sender)//ListBoxItemを作ったListBoxがこのイベントを呼び出した { return checkBox; } } } } return null; } } class TestItem : System.ComponentModel.INotifyPropertyChanged { public TestItem(string name) { this.Name = name; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { PropertyChangedEventHandler pc = PropertyChanged; if (pc != null) { pc(this, new PropertyChangedEventArgs(name)); } } public bool IsSelected { get { return _IsSelected; } set { if (_IsSelected != value) { _IsSelected = value; OnPropertyChanged("IsSelected"); } } } private bool _IsSelected; public string Name { get; set; } } }
VB
Class MainWindow Private testItems As List(Of TestItem) Sub New() InitializeComponent() testItems = New List(Of TestItem)() testItems.Add(New TestItem("A")) testItems.Add(New TestItem("B")) Me.listBox1.DataContext = testItems testItems = New List(Of TestItem)() testItems.Add(New TestItem("C")) testItems.Add(New TestItem("D")) Me.listBox2.DataContext = testItems End Sub Private Sub Button_Click(sender As Object, e As RoutedEventArgs) testItems(0).IsSelected = Not testItems(0).IsSelected End Sub Private Sub ListBox_CheckedChanged(sender As Object, e As RoutedEventArgs) Dim chk As CheckBox = Check_CheckedListBox_CheckedChanged(sender, e) If (chk IsNot Nothing) Then Dim text As String If (chk.IsChecked = True) Then text = "Checked" ElseIf (chk.IsChecked = False) Then text = "Unchecked" Else text = "Unknown" End If Dim lstBox = DirectCast(sender, ListBox) MessageBox.Show(String.Format("{0}" + vbCr + "{1}" + vbCr + "{2}", text, lstBox.Name, IIf(chk.Content Is Nothing, "NULL", chk.Content))) End If End Sub ''' <summary>CheckBoxがCheckedListBoxのCheckで変更されたのか調べる </summary> ''' <returns>CheckedListBoxのであれば、そのCheckBoxを返す</returns> Private Shared Function Check_CheckedListBox_CheckedChanged(ByVal sender As Object, ByVal e As RoutedEventArgs) As CheckBox Dim chk = TryCast(e.OriginalSource, CheckBox) If (chk IsNot Nothing) Then 'CheckBoxのチェックがイベントを発生させた Dim item = TryCast(chk.TemplatedParent, ListBoxItem) 'ListBoxItem,ListViewItem.ComboxItem..... If (item IsNot Nothing) Then 'CheckBoxがListBoxのテンプレートで作られた Dim Panel = VisualTreeHelper.GetParent(item) 'ListBoxItemの配置されているパネル If (Panel IsNot Nothing) Then 'パネルからItemsContorlを取得 Dim itemsCtl = ItemsControl.GetItemsOwner(Panel) 'ListBoxを取得; If (itemsCtl Is sender) Then 'ListBoxItemを作ったListBoxがこのイベントを呼び出した Return chk End If End If End If End If Return Nothing End Function End Class Class TestItem Implements System.ComponentModel.INotifyPropertyChanged Sub New(ByVal name As String) Me.Name = name End Sub Public Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Protected Overridable Sub OnPropertyChanged(ByVal name As String) RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(name)) End Sub Public Property IsSelected As Boolean Get Return _IsSelected End Get Set(value As Boolean) If (_IsSelected <> value) Then _IsSelected = value OnPropertyChanged("IsSelected") End If End Set End Property Private _IsSelected As Boolean Public Property Name As String End Class
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク れいじ 2015年7月1日 5:26
-
MVVMはデザインパターンであってテクノロジーではないので、MVVMでなくてもCommandは使えます。
以下、Visual Studio 2013でサンプルを書いてみました。<Application.Resources> <Style x:Key="CheckedListBoxStyle" TargetType="{x:Type ListBox}"> <Setter Property="SelectionMode" Value="Multiple" /> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <CheckBox Name="MyCheckBox" Content="{Binding Number}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding ClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding ElementName=MyCheckBox}" /> </i:EventTrigger> </i:Interaction.Triggers> </CheckBox> </ControlTemplate> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> </Style> </Application.Resources>
public partial class CheckBoxChecked : Window { public CheckBoxChecked() { InitializeComponent(); Numbers = Enumerable.Range(1, 3).Select(i => new MyNumber { Number = i, IsSelected = false }).ToList(); ClickCommand = new RelayCommand(CheckBox_Click); DataContext = this; } public List<MyNumber> Numbers { get; set; } public ICommand ClickCommand { get; private set; } void CheckBox_Click(object obj) { System.Diagnostics.Debug.WriteLine(obj.ToString()); } } public class MyNumber { public int Number { get; set; } public bool IsSelected { get; set; } }
#EventTriggerBehaviorを使うために、WPFのプロジェクトでMicrosoft.Xaml.Interactions.dllを参照設定しようとしたところ叱られてしまいました。ただ、Blendだと出来てしまいますね。BlendでもWPFの場合はデフォルトでは表示されていないので、推薦されていないのかもしれません。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク れいじ 2015年7月1日 5:26
すべての返信
-
直接イベントとる方法はVisualTreeいじくったりしないといけないのでめんどくさいですが以下の方法でViewModelで拾うことは出来ると思います。
- CheckBoxにEventTriggerBehaviorでClickイベントを拾う
- EventTriggerBehaviorにInvokeCommandActionを設定してViewModelのCommandにバインドする
- コマンドのExecuteが呼ばれたらClickされたとき
こんな感じでどうでしょうか。
かずき Blog:http://d.hatena.ne.jp/okazuki/
- 回答としてマーク れいじ 2015年7月1日 5:26
-
ToggleButton.CheckedとToggleButton.Uncheckedイベントはバブルイベントなので、外側でイベントを捕まえることもできます。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="300"> <DockPanel> <Button DockPanel.Dock="Bottom" Content="モデル側から変更してみるテスト" Click="Button_Click" Margin="10"/> <UniformGrid Columns="2"> <ListBox ItemsSource="{Binding}" Style="{StaticResource CheckedListBoxStyle}" x:Name="listBox1" ToggleButton.Checked="ListBox_CheckedChanged" ToggleButton.Unchecked="ListBox_CheckedChanged"> </ListBox> <ListBox ItemsSource="{Binding}" Style="{StaticResource CheckedListBoxStyle}" x:Name="listBox2" ToggleButton.Checked="ListBox_CheckedChanged" ToggleButton.Unchecked="ListBox_CheckedChanged"> </ListBox> </UniformGrid> </DockPanel> </Window>
C#
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.ComponentModel; namespace WpfApplication1 { public partial class MainWindow : Window { List<TestItem> testItems; public MainWindow() { InitializeComponent(); testItems = new List<TestItem>(); testItems.Add(new TestItem("A")); testItems.Add(new TestItem("B")); this.listBox1.DataContext = testItems; testItems = new List<TestItem>(); testItems.Add(new TestItem("C")); testItems.Add(new TestItem("D")); this.listBox2.DataContext = testItems; } private void Button_Click(object sender, RoutedEventArgs e) { testItems[0].IsSelected = !testItems[0].IsSelected; } private void ListBox_CheckedChanged(object sender, RoutedEventArgs e) { CheckBox checkBox = Check_CheckedListBox_CheckedChanged(sender, e); if (checkBox != null) { string text; if (checkBox.IsChecked == true) { text = "Checked"; } else if (checkBox.IsChecked == false) { text = "Unchecked"; } else { text = "Unknown"; } var listBox = (ListBox)sender; MessageBox.Show(string.Format("{0}\r\n{1}\r\n{2}", text, listBox.Name, checkBox.Content ?? "NULL")); } } /// <summary>CheckBoxがCheckedListBoxのCheckで変更されたのか調べる </summary> /// <returns>CheckedListBoxのであれば、そのCheckBoxを返す</returns> private static CheckBox Check_CheckedListBox_CheckedChanged(object sender, RoutedEventArgs e) { var checkBox = e.OriginalSource as CheckBox; if (checkBox != null)//CheckBoxのチェックがイベントを発生させた { ListBoxItem item = checkBox.TemplatedParent as ListBoxItem; //ListBoxItem,ListViewItem.ComboxItem..... if (item != null)//CheckBoxがListBoxのテンプレートで作られた { var panel = VisualTreeHelper.GetParent(item);//ListBoxItemの配置されているパネル if (panel != null)//パネルからItemsContorlを取得 { ItemsControl itemsControl = ItemsControl.GetItemsOwner(panel);//ListBoxを取得; if (itemsControl == sender)//ListBoxItemを作ったListBoxがこのイベントを呼び出した { return checkBox; } } } } return null; } } class TestItem : System.ComponentModel.INotifyPropertyChanged { public TestItem(string name) { this.Name = name; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { PropertyChangedEventHandler pc = PropertyChanged; if (pc != null) { pc(this, new PropertyChangedEventArgs(name)); } } public bool IsSelected { get { return _IsSelected; } set { if (_IsSelected != value) { _IsSelected = value; OnPropertyChanged("IsSelected"); } } } private bool _IsSelected; public string Name { get; set; } } }
VB
Class MainWindow Private testItems As List(Of TestItem) Sub New() InitializeComponent() testItems = New List(Of TestItem)() testItems.Add(New TestItem("A")) testItems.Add(New TestItem("B")) Me.listBox1.DataContext = testItems testItems = New List(Of TestItem)() testItems.Add(New TestItem("C")) testItems.Add(New TestItem("D")) Me.listBox2.DataContext = testItems End Sub Private Sub Button_Click(sender As Object, e As RoutedEventArgs) testItems(0).IsSelected = Not testItems(0).IsSelected End Sub Private Sub ListBox_CheckedChanged(sender As Object, e As RoutedEventArgs) Dim chk As CheckBox = Check_CheckedListBox_CheckedChanged(sender, e) If (chk IsNot Nothing) Then Dim text As String If (chk.IsChecked = True) Then text = "Checked" ElseIf (chk.IsChecked = False) Then text = "Unchecked" Else text = "Unknown" End If Dim lstBox = DirectCast(sender, ListBox) MessageBox.Show(String.Format("{0}" + vbCr + "{1}" + vbCr + "{2}", text, lstBox.Name, IIf(chk.Content Is Nothing, "NULL", chk.Content))) End If End Sub ''' <summary>CheckBoxがCheckedListBoxのCheckで変更されたのか調べる </summary> ''' <returns>CheckedListBoxのであれば、そのCheckBoxを返す</returns> Private Shared Function Check_CheckedListBox_CheckedChanged(ByVal sender As Object, ByVal e As RoutedEventArgs) As CheckBox Dim chk = TryCast(e.OriginalSource, CheckBox) If (chk IsNot Nothing) Then 'CheckBoxのチェックがイベントを発生させた Dim item = TryCast(chk.TemplatedParent, ListBoxItem) 'ListBoxItem,ListViewItem.ComboxItem..... If (item IsNot Nothing) Then 'CheckBoxがListBoxのテンプレートで作られた Dim Panel = VisualTreeHelper.GetParent(item) 'ListBoxItemの配置されているパネル If (Panel IsNot Nothing) Then 'パネルからItemsContorlを取得 Dim itemsCtl = ItemsControl.GetItemsOwner(Panel) 'ListBoxを取得; If (itemsCtl Is sender) Then 'ListBoxItemを作ったListBoxがこのイベントを呼び出した Return chk End If End If End If End If Return Nothing End Function End Class Class TestItem Implements System.ComponentModel.INotifyPropertyChanged Sub New(ByVal name As String) Me.Name = name End Sub Public Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Protected Overridable Sub OnPropertyChanged(ByVal name As String) RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(name)) End Sub Public Property IsSelected As Boolean Get Return _IsSelected End Get Set(value As Boolean) If (_IsSelected <> value) Then _IsSelected = value OnPropertyChanged("IsSelected") End If End Set End Property Private _IsSelected As Boolean Public Property Name As String End Class
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク れいじ 2015年7月1日 5:26
-
MVVMはデザインパターンであってテクノロジーではないので、MVVMでなくてもCommandは使えます。
以下、Visual Studio 2013でサンプルを書いてみました。<Application.Resources> <Style x:Key="CheckedListBoxStyle" TargetType="{x:Type ListBox}"> <Setter Property="SelectionMode" Value="Multiple" /> <Setter Property="ItemContainerStyle"> <Setter.Value> <Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <CheckBox Name="MyCheckBox" Content="{Binding Number}" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding ClickCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding ElementName=MyCheckBox}" /> </i:EventTrigger> </i:Interaction.Triggers> </CheckBox> </ControlTemplate> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> </Style> </Application.Resources>
public partial class CheckBoxChecked : Window { public CheckBoxChecked() { InitializeComponent(); Numbers = Enumerable.Range(1, 3).Select(i => new MyNumber { Number = i, IsSelected = false }).ToList(); ClickCommand = new RelayCommand(CheckBox_Click); DataContext = this; } public List<MyNumber> Numbers { get; set; } public ICommand ClickCommand { get; private set; } void CheckBox_Click(object obj) { System.Diagnostics.Debug.WriteLine(obj.ToString()); } } public class MyNumber { public int Number { get; set; } public bool IsSelected { get; set; } }
#EventTriggerBehaviorを使うために、WPFのプロジェクトでMicrosoft.Xaml.Interactions.dllを参照設定しようとしたところ叱られてしまいました。ただ、Blendだと出来てしまいますね。BlendでもWPFの場合はデフォルトでは表示されていないので、推薦されていないのかもしれません。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク れいじ 2015年7月1日 5:26