web-dev-qa-db-ja.com

ContextMenuを表示する前に、右クリックでTreeView Nodeを選択します。

ContextMenuが表示される直前に、右クリックでWPF TreeView Nodeを選択します。

WinFormsの場合、次のようなコードを使用できます コンテキストメニューでノードを検索 、WPFの代替手段は何ですか?

94
alex2k8

ツリーの設定方法に応じて、 送信者とe.Sourceの値は異なる場合があります

可能な解決策の1つは、e.OriginalSourceを使用し、VisualTreeHelperを使用してTreeViewItemを見つけることです。

private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);

    if (treeViewItem != null)
    {
        treeViewItem.Focus();
        e.Handled = true;
    }
}

static TreeViewItem VisualUpwardSearch(DependencyObject source)
{
    while (source != null && !(source is TreeViewItem))
        source = VisualTreeHelper.GetParent(source);

    return source as TreeViewItem;
}
126
alex2k8

XAMLのみのソリューションが必要な場合は、Blend Interactivityを使用できます。

TreeViewは、BooleanプロパティIsSelectedおよびStringプロパティNameを持つビューモデルの階層コレクションにバインドされたデータであると仮定します。 Childrenという名前の子アイテムのコレクション。

<TreeView ItemsSource="{Binding Items}">
  <TreeView.ItemContainerStyle>
    <Style TargetType="TreeViewItem">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
    </Style>
  </TreeView.ItemContainerStyle>
  <TreeView.ItemTemplate>
    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <TextBlock Text="{Binding Name}">
        <i:Interaction.Triggers>
          <i:EventTrigger EventName="PreviewMouseRightButtonDown">
            <ei:ChangePropertyAction PropertyName="IsSelected" Value="true" TargetObject="{Binding}"/>
          </i:EventTrigger>
        </i:Interaction.Triggers>
      </TextBlock>
    </HierarchicalDataTemplate>
  </TreeView.ItemTemplate>
</TreeView>

2つの興味深い部分があります。

  1. TreeViewItem.IsSelectedプロパティは、ビューモデルのIsSelectedプロパティにバインドされます。ビューモデルのIsSelectedプロパティをtrueに設定すると、ツリー内の対応するノードが選択されます。

  2. PreviewMouseRightButtonDownがノードの視覚部分(このサンプルではTextBlock)で発生すると、view-modelのIsSelectedプロパティがtrueに設定されます。 1.に戻ると、ツリーでクリックされた対応するノードが選択されたノードになることがわかります。

プロジェクトでBlend Interactivityを取得する1つの方法は、NuGetパッケージ nofficial.Blend.Interactivity を使用することです。

20

「item.Focus();」を使用する「item.IsSelected = true;」を使用して、100%動作しないようです。します。

16
Erlend

Alex2k8の元のアイデアを使用して、Wieser Software Ltdの非ビジュアル、StefanのXAML、ErlendのIsSelectedを正しく処理し、静的メソッドをGenericにすることの私の貢献:

XAML:

<TreeView.ItemContainerStyle> 
    <Style TargetType="{x:Type TreeViewItem}"> 
        <!-- We have to select the item which is right-clicked on --> 
        <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown"
                     Handler="TreeViewItem_PreviewMouseRightButtonDown"/> 
    </Style> 
</TreeView.ItemContainerStyle>

C#コードビハインド:

void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem treeViewItem = 
              VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject);

    if(treeViewItem != null)
    {
        treeViewItem.IsSelected = true;
        e.Handled = true;
    }
}

static T VisualUpwardSearch<T>(DependencyObject source) where T : DependencyObject
{
    DependencyObject returnVal = source;

    while(returnVal != null && !(returnVal is T))
    {
        DependencyObject tempReturnVal = null;
        if(returnVal is Visual || returnVal is Visual3D)
        {
            tempReturnVal = VisualTreeHelper.GetParent(returnVal);
        }
        if(tempReturnVal == null)
        {
            returnVal = LogicalTreeHelper.GetParent(returnVal);
        }
        else returnVal = tempReturnVal;
    }

    return returnVal as T;
}

編集:このシナリオでは以前のコードは常に正常に機能していましたが、別のシナリオでは、LogicalTreeHelperが値を返したときにVisualTreeHelper.GetParentがnullを返していたため、修正しました。

12
Sean Hall

XAMLで、XAMLにPreviewMouseRightButtonDownハンドラーを追加します。

    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <!-- We have to select the item which is right-clicked on -->
            <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
        </Style>
    </TreeView.ItemContainerStyle>

次に、次のようにイベントを処理します。

    private void TreeViewItem_PreviewMouseRightButtonDown( object sender, MouseEventArgs e )
    {
        TreeViewItem item = sender as TreeViewItem;
        if ( item != null )
        {
            item.Focus( );
            e.Handled = true;
        }
    }
11
Stefan

ほぼ右 、ただし、ツリー内の非ビジュアルに注意する必要があります(たとえば、Runなど)。

static DependencyObject VisualUpwardSearch<T>(DependencyObject source) 
{
    while (source != null && source.GetType() != typeof(T))
    {
        if (source is Visual || source is Visual3D)
        {
            source = VisualTreeHelper.GetParent(source);
        }
        else
        {
            source = LogicalTreeHelper.GetParent(source);
        }
    }
    return source; 
}
7
Anthony Wieser

クラスハンドラを登録することでうまくいくと思います。次のように、app.xaml.csコードファイルのTreeViewItemのPreviewMouseRightButtonDownEventにルーティングイベントハンドラーを登録するだけです。

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof(TreeViewItem), TreeViewItem.PreviewMouseRightButtonDownEvent, new RoutedEventHandler(TreeViewItem_PreviewMouseRightButtonDownEvent));

        base.OnStartup(e);
    }

    private void TreeViewItem_PreviewMouseRightButtonDownEvent(object sender, RoutedEventArgs e)
    {
        (sender as TreeViewItem).IsSelected = true;
    }
}
6
Nathan Swannet

MVVMを使用してそれを解決する別の方法は、ビューモデルを右クリックするためのbindコマンドです。そこで、source.IsSelected = trueと同様に他のロジックを指定できます。これは、xmlns:i="http://schemas.Microsoft.com/expression/2010/intera‌​ctivity"からのSystem.Windows.Interactivityのみを使用します。

ビューのXAML:

<TreeView ItemsSource="{Binding Items}">
  <TreeView.ItemContainerStyle>
    <Style TargetType="TreeViewItem">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
    </Style>
  </TreeView.ItemContainerStyle>
  <TreeView.ItemTemplate>
    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <TextBlock Text="{Binding Name}">
        <i:Interaction.Triggers>
          <i:EventTrigger EventName="PreviewMouseRightButtonDown">
            <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.TreeViewItemRigthClickCommand}" CommandParameter="{Binding}" />
          </i:EventTrigger>
        </i:Interaction.Triggers>
      </TextBlock>
    </HierarchicalDataTemplate>
  </TreeView.ItemTemplate>
</TreeView>

モデルを表示:

    public ICommand TreeViewItemRigthClickCommand
    {
        get
        {
            if (_treeViewItemRigthClickCommand == null)
            {
                _treeViewItemRigthClickCommand = new RelayCommand<object>(TreeViewItemRigthClick);
            }
            return _treeViewItemRigthClickCommand;
        }
    }
    private RelayCommand<object> _treeViewItemRigthClickCommand;

    private void TreeViewItemRigthClick(object sourceItem)
    {
        if (sourceItem is Item)
        {
            (sourceItem as Item).IsSelected = true;
        }
    }
1
benderto

HierarchicalDataTemplateメソッドで子を選択する際に問題が発生していました。ノードの子を選択すると、その子のルート親が何らかの形で選択されます。 MouseRightButtonDownイベントは、子がいたすべてのレベルで呼び出されることがわかりました。たとえば、次のようなツリーがある場合:

アイテム1
-子供1
-子供2
-サブアイテム1
-サブアイテム2

Subitem2を選択した場合、イベントは3回発生し、アイテム1が選択されます。これをブール値と非同期呼び出しで解決しました。

private bool isFirstTime = false;
    protected void TaskTreeView_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        var item = sender as TreeViewItem;
        if (item != null && isFirstTime == false)
        {
            item.Focus();
            isFirstTime = true;
            ResetRightClickAsync();
        }
    }

    private async void ResetRightClickAsync()
    {
        isFirstTime = await SetFirstTimeToFalse();
    }

    private async Task<bool> SetFirstTimeToFalse()
    {
        return await Task.Factory.StartNew(() => { Thread.Sleep(3000); return false; });
    }

少し不格好に感じますが、基本的には最初のパススルーでブール値をtrueに設定し、別のスレッドで数秒でリセットします(この場合は3)。これは、次のパスがツリーを上に移動しようとする場所を通過すると、正しいノードが選択されたままになることを意味します。今のところうまくいくようです:-)

1
Zoey

On mouse downイベントで選択できます。これにより、コンテキストメニューが起動する前に選択がトリガーされます。

0
Scott Thurlow