32

(注 - 私の最初の質問が間違った見出しの下に投稿されたため、これは再投稿です:ここで申し訳ありません!)

標準の WPF ツリービューがあり、アイテムをビュー モデル クラスにバインドしています。

アイテムがダブルクリックされたときの動作を処理したいと考えています (ドキュメントを Visual Studio スタイルで開く)。

ツリービュー (xaml を表示) を格納するコントロールでイベント ハンドラーを起動することはできますが、ビュー モデル クラス (ProjectViewModel など) の特定の動作にバインドするにはどうすればよいですか?

これは他の場所で使用されるため、ICommand-implementer にバインドすることをお勧めします...

<TreeView ItemsSource="{Binding Projects}" MouseDoubleClick="TreeView_MouseDoubleClick">
    <TreeView.ItemContainerStyle>
        <!-- 
This Style binds a TreeViewItem to a TreeViewItemViewModel. 
-->
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight" Value="Normal" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TreeView.ItemContainerStyle>

    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type Implementations:ProjectViewModel}" ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" />
                <TextBlock Text="{Binding DisplayName}" />
            </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type Implementations:PumpViewModel}" ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <Image Width="16" Height="16" Margin="3,0" Source="Images\State.png" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </HierarchicalDataTemplate>

        <DataTemplate DataType="{x:Type Implementations:PumpDesignViewModel}">
            <StackPanel Orientation="Horizontal">
                <Image Width="16" Height="16" Margin="3,0" Source="Images\City.png" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </TreeView.Resources>
</TreeView>
4

8 に答える 8

65

私の答えを少し更新します。

私はこれに対して多くの異なるアプローチを試しましたが、今でもAttached Behaviorsが最良の解決策であると感じています。最初はオーバーヘッドが大きいように見えるかもしれませんが、実際にはそうではありません。すべてのビヘイビアをICommands同じ場所に保持し、別のイベントのサポートが必要なときはいつでも、PropertyChangedCallback.

のオプションのサポートも追加しましたCommandParameter

デザイナーでは、目的のイベントを選択するだけです

ここに画像の説明を入力

これはTreeViewTreeViewItemまたは好きな場所に設定できます。

例。にセットします。TreeView

<TreeView commandBehaviors:MouseDoubleClick.Command="{Binding YourCommand}"
          commandBehaviors:MouseDoubleClick.CommandParameter="{Binding}"
          .../>

例。オンに設定TreeViewItem

<TreeView ItemsSource="{Binding Projects}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="commandBehaviors:MouseDoubleClick.Command"
                    Value="{Binding YourCommand}"/>
            <Setter Property="commandBehaviors:MouseDoubleClick.CommandParameter"
                    Value="{Binding}"/>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

そして、ここにアタッチされた動作があります MouseDoubleClick

public class MouseDoubleClick
{
    public static DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command",
        typeof(ICommand),
        typeof(MouseDoubleClick),
        new UIPropertyMetadata(CommandChanged));

    public static DependencyProperty CommandParameterProperty =
        DependencyProperty.RegisterAttached("CommandParameter",
                                            typeof(object),
                                            typeof(MouseDoubleClick),
                                            new UIPropertyMetadata(null));

    public static void SetCommand(DependencyObject target, ICommand value)
    {
        target.SetValue(CommandProperty, value);
    }

    public static void SetCommandParameter(DependencyObject target, object value)
    {
        target.SetValue(CommandParameterProperty, value);
    }
    public static object GetCommandParameter(DependencyObject target)
    {
        return target.GetValue(CommandParameterProperty);
    }

    private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        Control control = target as Control;
        if (control != null)
        {
            if ((e.NewValue != null) && (e.OldValue == null))
            {
                control.MouseDoubleClick += OnMouseDoubleClick;
            }
            else if ((e.NewValue == null) && (e.OldValue != null))
            {
                control.MouseDoubleClick -= OnMouseDoubleClick;
            }
        }
    }

    private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
    {
        Control control = sender as Control;
        ICommand command = (ICommand)control.GetValue(CommandProperty);
        object commandParameter = control.GetValue(CommandParameterProperty);
        command.Execute(commandParameter);
    }
}
于 2010-12-21T09:56:17.203 に答える
9

私はこれに遅れていますが、別のソリューションを使用しました。繰り返しますが、それは最善ではないかもしれませんが、これが私がやった方法です。

まず、Meleak からの以前の回答はクールですが、MouseDoubleClick のような基本的なもののためだけに AttachedBehaviors を追加することを余儀なくされるのは非常に重いように感じます。これにより、アプリで新しいパターンを使用せざるを得なくなり、すべてがさらに複雑になります。

私の目標は、できるだけシンプルにとどまることです。したがって、私は非常に基本的なことを行いました (私の例は DataGrid 用ですが、さまざまなコントロールで使用できます)。

<DataGrid MouseDoubleClick="DataGrid_MouseDoubleClick">
   <!-- ... -->
</DataGrid>

コード ビハインドで:

private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    //Execute the command related to the doubleclick, in my case Edit
    (this.DataContext as VmHome).EditAppCommand.Execute(null);
}

MVVM パターンを壊していないように感じるのはなぜですか? 私の意見では、コード ビハインドに入れる必要があるのは、viewModel へのブリッジ、つまり UI に非常に固有のものだけです。この場合、ダブルクリックすると、関連するコマンドが起動されるだけです。Command="{Binding EditAppCommand}" とほぼ同じですが、この動作をシミュレートしただけです。

これについて意見をお聞かせください。この考え方に対する批判を聞いてうれしいですが、今のところ、MVVM を壊さずに実装する最も簡単な方法だと思います。

于 2011-03-23T10:04:53.890 に答える
6

Meleak と ígor の両方の推奨事項は優れていますが、ダブルクリックイベント ハンドラーがバインドされているTreeViewItem場合、このイベント ハンドラーはアイテムのすべての親要素 (クリックされた要素だけでなく) に対して呼び出されます。望ましくない場合は、別の追加があります。

private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
    Control control = sender as Control;
    ICommand command = (ICommand)control.GetValue(CommandProperty);
    object commandParameter = control.GetValue(CommandParameterProperty);

    if (sender is TreeViewItem)
    {
        if (!((TreeViewItem)sender).IsSelected)
            return;
    }

    if (command.CanExecute(commandParameter))
    {
        command.Execute(commandParameter);
    }
}
于 2012-09-17T12:38:06.707 に答える
6

それは本当に簡単で、これが私が TreeView でダブルクリックを処理した方法です:

<Window x:Class="TreeViewWpfApplication.MainWindow"
    ...
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    ...>

      <TreeView ItemsSource="{Binding Departments}" >
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <ei:CallMethodAction MethodName="SomeMethod" TargetObject="{Binding}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
      </TreeView>
</Window>

System.Windows.Interactivity.dllは、C:\Program Files (x86)\Microsoft SDKs\Expression\Blend.NETFramework\v4.0\Libraries\System.Windows.Interactivity.dll から、またはNuGetによって取得されます。

私のビューモデル:

public class TreeViewModel : INotifyPropertyChanged
{   
    private List<Department> departments;
    public TreeViewModel()
    {
        Departments = new List<Department>()
        {
            new Department("Department1"),
            new Department("Department2"),
            new Department("Department3")
        };
    }

    public List<Department> Departments
    {
        get
        {
            return departments;
        }
        set
        {
            departments = value;
            OnPropertyChanged("Departments");
        }
    }

    public void SomeMethod()
    {
        MessageBox.Show("*****");
    }
}   
于 2015-10-16T12:09:58.613 に答える
2

Meleakソリューションは素晴らしいです!, しかし、私はチェックを追加しました

    private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
    {
        Control control = sender as Control;
        ICommand command = (ICommand)control.GetValue(CommandProperty);
        object commandParameter = control.GetValue(CommandParameterProperty);
       //Check command can execute!!  
      if(command.CanExecute(commandParameter ))
         command.Execute(commandParameter);
    }
于 2012-07-26T09:28:46.650 に答える
1

TextBlock でのマウス バインディング

ビューの TreeView.Resources で:

   <HierarchicalDataTemplate 
      DataType="{x:Type treeview:DiscoveryUrlViewModel}" 
      ItemsSource="{Binding Children}">

      <StackPanel Orientation="Horizontal">
           <Image Width="16" Height="16" Margin="3,0" Source="../Images/ic_search.png" />

           <TextBlock Text="{Binding DisplayText}" >
               <TextBlock.InputBindings>
                     <MouseBinding Gesture="LeftDoubleClick"
                                   Command="{Binding DoubleClickCopyCommand}"
                                   CommandParameter="{Binding }" />
               </TextBlock.InputBindings>
            </TextBlock>
       </StackPanel>
 </HierarchicalDataTemplate>

そのビュー (DiscoveryUrlViewModel.cs) の ViewModel で:

private RelayCommand _doubleClickCommand;   
public ICommand DoubleClickCopyCommand
        {
            get
            {
                if (_doubleClickCommand == null)
                    _doubleClickCommand = new RelayCommand(OnDoubleClick);
                return _doubleClickCommand;
            }
        }

        private void OnDoubleClick(object obj)
        {
            var clickedViewModel = (DiscoveryUrlViewModel)obj;
        }
于 2018-02-07T09:28:22.117 に答える