18100


WPFのListBoxでMVVM的なイベント追加をしたいと思っています。  
そこで、ListBoxのアイテムにダブルクリックイベントをCommandを使用して追加しました。  
以下の様な形で実装できたのですが、もう少しコンパクトな形にできないでしょうか？

・ItemsSourceを使用する  
・ListBoxのアイテム行のどこをダブルクリックしてもイベントが発生する  
（TextBlockにイベントをつけたら文字の部分しかダメでした）  
・Commandのパラメータにはダブルクリックしたアイテムが入る  
の条件を満たせれば構わないのでアドバイスお願いします。

  
ListBox

[code]

    <ListBox Width="150" ItemsSource="{Binding ソース}" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding 表示文字}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    
[/code]

  
リソース部分

[code]

    <local:RestorableWindow.Resources>
    <Style x:Key="FocusVisual">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
    <SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
    <SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
    <SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
    <SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
    <SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
    <Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
        <Setter Property="SnapsToDevicePixels" Value="True"/>
        <Setter Property="Padding" Value="4,1"/>
        <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="Transparent"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                        <!-- 追加部分　ここから -->
                        <Border.InputBindings>
                            <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding DataContext.ダブルクリックイベント, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding .}"/>
                        </Border.InputBindings>
                        <!-- ここまで -->
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsMouseOver" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
                            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="Selector.IsSelectionActive" Value="False"/>
                                <Condition Property="IsSelected" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
                            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="Selector.IsSelectionActive" Value="True"/>
                                <Condition Property="IsSelected" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
                            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
                        </MultiTrigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
[/code]


CC BY-SA 3.0
2015-10-15T04:25:44.467
17669
2015-10-27T01:44:14.417
9331
question
c# wpf xaml
MVVMでのListBoxItemのダブルクリックCommadの指定方法


既に挙げられている方法以外では、Blend SDKを使う方法が一番スマートそうです。  
(普通のビヘイビアでもいいと思います)

まず、`Border`等の`XXXDoubleClick`系イベントを持たないControlはXAML上でダブルクリックを検出できないので、`System.Windows.Interactivity.Behavior`を適当に継承して以下のようなビヘイビアを作成します。

[code]

    public class DoubleClickBehavior : Behavior<UIElement>
    {
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
    
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(DoubleClickBehavior), new PropertyMetadata(null));
    
    
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
    
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(DoubleClickBehavior), new PropertyMetadata(null));
    
    
        protected override void OnAttached()
        {
            base.OnAttached();
    
            this.AssociatedObject.MouseLeftButtonDown += onMouseButtonDown;
        }
    
        private void onMouseButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount != 2)
                return;
    
            if (this.Command == null)
                return;
    
            if(this.Command.CanExecute(this.CommandParameter))
            {
                this.Command.Execute(this.CommandParameter);
            }
        }
    }
    
[/code]

あとは単純にXAML上で以下のように使えば動くかと。  
※外見系の実装は全て省略しています

[code]

    <ListBox Width="150" ItemsSource="{Binding ソース}">
        <ListBox.ItemContainerStyle>
            <Style TargetType="ListBoxItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <Border Background="Red">
                                <i:Interaction.Behaviors>
                                    <v:DoubleClickBehavior Command="{Binding DataContext.DoubleClickCommand,RelativeSource={RelativeSource AncestorType=ListBox}}"
                                                                CommandParameter="{Binding}"/>
                                </i:Interaction.Behaviors>
                                <ContentPresenter/>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding 表示文字}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    
[/code]


CC BY-SA 3.0
2015-10-24T03:11:42.003
18010
2015-10-24T03:17:10.970
2015-10-24T03:17:10.970
6119
6119
17669
answer


いろいろ試しましたが、こんな形で落ち着きました。

[code]

    public class DoubleClickBehavior {
        public static ICommand GetCommand( DependencyObject obj ) {
            return (ICommand)obj.GetValue( CommandProperty );
        }
    
        public static void SetCommand( DependencyObject obj, object value ) {
            obj.SetValue( CommandProperty, value );
        }
    
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached( "Command", typeof( ICommand ), typeof( DoubleClickBehavior ), new UIPropertyMetadata( null, OnCommandPropertyChanged ) );
    
        static void OnCommandPropertyChanged( DependencyObject depObj, DependencyPropertyChangedEventArgs e ) {
            var control = depObj as Control;
            if( control == null )
                return;
    
            if( e.NewValue is ICommand == false )
                return;
    
            control.MouseDoubleClick += control_MouseDoubleClick;
        }
    
        static void control_MouseDoubleClick( object sender, MouseButtonEventArgs e ) {
            var control = (Control)sender;
            ICommand command = DoubleClickBehavior.GetCommand( control );
            if( command != null && command.CanExecute( DoubleClickBehavior.GetCommandParameter( control ) ) ) {
                command.Execute( DoubleClickBehavior.GetCommandParameter( control ) );
            }
        }
    
        public static object GetCommandParameter( DependencyObject obj ) {
            return obj.GetValue( CommandParameterProperty );
        }
    
        public static void SetCommandParameter( DependencyObject obj, object value ) {
            obj.SetValue( CommandParameterProperty, value );
        }
    
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached( "CommandParameter", typeof( object ), typeof( DoubleClickBehavior ), new UIPropertyMetadata( null ) );
    
    }
    
[/code]

Xaml:

[code]

    <ListBox ItemsSource="{Binding ソース}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding 表示文字}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
        <ListBox.ItemContainerStyle>
            <Style>
                <Setter Property="local:DoubleClickBehavior.Command" Value="{Binding DataContext.DoubleClick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
                <Setter Property="local:DoubleClickBehavior.CommandParameter" Value="{Binding .}"/>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>
    
[/code]


CC BY-SA 3.0
2015-10-27T01:44:14.417
18100
2015-10-27T01:44:14.417
9331
17669
answer