ContextMenu를 표시하기 전에 오른쪽 클릭에서 TreeView 노드를 선택하십시오.


답변:


130

트리가 채워진 방식에 따라 보낸 사람과 e.Source 값이 다를 수 있습니다 .

가능한 솔루션 중 하나는 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;
}

TreeView 또는 TreeViewItem에 대한 이벤트입니까?
Louis Rhys 2012

1
오른쪽 클릭이 빈 위치에있는 경우 모든 선택을 취소하는 방법을 알고 계십니까?
Louis Rhys 2012

다른 5 명 중 도움이 된 유일한 답변 ... 저는 트 리뷰 인구에 대해 정말 잘못하고 있습니다. 감사합니다.

3
Louis Rhys의 질문에 대한 답변 : if (treeViewItem == null) treeView.SelectedIndex = -1또는 treeView.SelectedItem = null. 둘 다 작동해야한다고 생각합니다.
James M

24

XAML 전용 솔루션을 원하는 경우 Blend Interactivity를 사용할 수 있습니다.

가정 TreeView가진 뷰 모델의 계층 컬렉션에 데이터 바인딩이다 Boolean재산 IsSelectedString재산 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>

두 가지 흥미로운 부분이 있습니다.

  1. TreeViewItem.IsSelected속성은 바인딩 된 IsSelected뷰 모델에 대한 속성입니다. IsSelected뷰 모델 의 속성을 true로 설정하면 트리에서 해당 노드가 선택됩니다.

  2. 경우 PreviewMouseRightButtonDown(이 샘플 A의 노드의 시각적 부분 화재 TextBlock)를 IsSelected뷰 모델에 속성이 참으로 설정된다. 1로 돌아 가면 트리에서 클릭 한 해당 노드가 선택된 노드가되는 것을 볼 수 있습니다.

프로젝트에서 Blend Interactivity를 얻는 한 가지 방법은 NuGet 패키지 Unofficial.Blend.Interactivity 를 사용하는 것 입니다.


2
좋은 대답, 감사합니다! 보여주기 위해 도움이 될 것입니다 무엇을 i하고 ei어느 어셈블리 그들은 내가 가정에서 발견 될 수 있지만에 대한 네임 스페이스 매핑 해결 :. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"그리고 xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"각각 System.Windows.Interactivity 및 Microsoft.Expression.Interactions 어셈블리에서 발견되는.
prlc

UI의 일부가 아닌 바인딩 된 데이터 개체 ChangePropertyActionIsSelected속성 을 설정하려고했기 때문에이 방법은 도움이되지 않았으므로 속성이 없습니다 IsSelected. 내가 뭘 잘못하고 있니?
Antonín Procházka

@ AntonínProcházka : 내 대답은 "데이터 개체"(또는 뷰 모델) IsSelected에 내 대답의 두 번째 단락에 명시된 속성 이 있어야합니다. 데이터가 부울 속성을 가진 뷰 모델의 계층 적 컬렉션에 바인딩되어 있다고 가정합니다 .TreeViewIsSelected (내 강조).
마틴 Liversage

16

"item.Focus ();"사용 "item.IsSelected = true;"를 사용하여 100 % 작동하지 않는 것 같습니다. 않습니다.


이 팁에 감사드립니다. 나를 도와 주었다.
i8abug 2010

좋은 팁. 먼저 Focus ()를 호출 한 다음 IsSelected = true로 설정합니다.
Jim Gomes

12

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;
        }
    }

2
예상대로 작동하지 않으며 항상 루트 요소를 보낸 사람으로 가져옵니다. 비슷한 솔루션 하나의 social.msdn.microsoft.com/Forums/en-US/wpf/thread/… 이 방식으로 추가 된 이벤트 처리기가 예상대로 작동합니다. 수락하기 위해 코드가 변경 되었습니까? :-)
alex2k8

분명히 트리 뷰를 채우는 방법에 따라 다릅니다. 내가 게시 한 코드는 내 도구 중 하나에서 사용하는 정확한 코드이기 때문에 작동합니다.
Stefan

여기에서 디버그 지점을 설정하면 트리를 설정하는 방법에 따라 물론 다른 발신자 유형을 확인할 수 있습니다

이것은 작동 할 때 가장 간단한 해결책처럼 보입니다. 그것은 나를 위해 일했습니다. 실제로 보낸 사람을 TreeViewItem으로 캐스팅해야합니다. 그렇지 않은 경우 버그이기 때문입니다.
craftworkgames

12

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을 반환하므로이를 수정했습니다.


1
이를 위해이 답변은 DependencyProperty 확장에서 이것을 구현합니다. stackoverflow.com/a/18032332/84522
Terrence

7

거의 맞지만, 트리에서 비 시각적 요소 (예 :)를주의해야합니다 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; 
}

이 일반 메서드는 TreeViewItem을 쓸 때 어떻게 사용할 수 있는지 조금 이상해 보입니다. treeViewItem = VisualUpwardSearch <TreeViewItem> (e.OriginalSource as DependencyObject); 그것은 나에게 변환 오류를 제공
Rati_Ge

TreeViewItem treeViewItem = VisualUpwardSearch <TreeViewItem> (e.OriginalSource as DependencyObject) as TreeViewItem;
Anthony Wieser

6

클래스 핸들러를 등록하는 것이 트릭을해야한다고 생각합니다. 다음과 같이 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;
    }
}

나를 위해 일했습니다! 그리고 간단합니다.
dvallejo

2
안녕 네이선. 코드가 전역 적이며 모든 TreeView에 영향을 미치는 것처럼 들립니다. 로컬 전용 솔루션을 갖는 것이 더 낫지 않을까요? 부작용을 일으킬 수 있습니까?
Eric Ouellet

이 코드는 실제로 전체 WPF 애플리케이션에 대해 전역 적입니다. 필자의 경우 이것은 필수 동작이므로 애플리케이션 내에서 사용되는 모든 트 리뷰에 대해 일관 적이었습니다. 그러나이 이벤트를 트 리뷰 인스턴스 자체에 등록 할 수 있으므로 해당 트 리뷰에만 적용 할 수 있습니다.
Nathan Swannet

2

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

HierarchicalDataTemplate 메서드로 자식을 선택하는 데 문제가있었습니다. 노드의 자식을 선택하면 어떻게 든 해당 자식의 루트 부모를 선택합니다. 나는 MouseRightButtonDown 이벤트가 아이가있는 모든 레벨에 대해 호출된다는 것을 알았습니다. 예를 들어 다음과 같은 나무가있는 경우 :

항목 1
   - 자녀 1
   - 자녀 2
      -의 SubItem1
      - Subitem2

Subitem2를 선택하면 이벤트가 세 번 실행되고 항목 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). 즉, 트리 위로 이동하려는 다음 패스를 건너 뛰고 올바른 노드를 선택한 상태로 둡니다. 지금까지 작동하는 것 같습니다 :-)


대답은로 설정 MouseButtonEventArgs.Handled하는 것 true입니다. 아이가 처음으로 부름을 받기 때문입니다. 이 속성을 true로 설정하면 부모에 대한 다른 호출이 비활성화됩니다.
바싯 대변인 Anwer

0

on mouse down 이벤트로 선택할 수 있습니다. 컨텍스트 메뉴가 시작되기 전에 선택을 트리거합니다.


0

MVVM 패턴을 유지하려면 다음을 수행 할 수 있습니다.

전망:

<TreeView x:Name="trvName" ItemsSource="{Binding RootElementListView}" Tag="{Binding ClickedTreeElement, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type models:YourTreeElementClass}" ItemsSource="{Binding Path=Subreports}">
            <TextBlock Text="{Binding YourTreeElementDisplayProperty}" PreviewMouseRightButtonDown="TreeView_PreviewMouseRightButtonDown"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

비하인드 코드 :

private void TreeView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
    if (sender is TextBlock tb && tb.DataContext is YourTreeElementClass te)
    {
        trvName.Tag = te;
    }
}

ViewModel :

private YourTreeElementClass _clickedTreeElement;

public YourTreeElementClass ClickedTreeElement
{
    get => _clickedTreeElement;
    set => SetProperty(ref _clickedTreeElement, value);
}

이제 ClickedTreeElement 속성 변경에 반응하거나 ClickedTreeElement와 내부적으로 작동하는 명령을 사용할 수 있습니다.

확장보기 :

<UserControl ...
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <TreeView x:Name="trvName" ItemsSource="{Binding RootElementListView}" Tag="{Binding ClickedTreeElement, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseRightButtonUp">
                <i:InvokeCommandAction Command="{Binding HandleRightClickCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type models:YourTreeElementClass}" ItemsSource="{Binding Path=Subreports}">
                <TextBlock Text="{Binding YourTreeElementDisplayProperty}" PreviewMouseRightButtonDown="TreeView_PreviewMouseRightButtonDown"/>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</UserControl>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.