ContextMenu'yu görüntülemeden önce sağ tıklamayla TreeView Düğümünü seçin


Yanıtlar:


130

Ağacın doldurulma şekline bağlı olarak, gönderen ve e.Kaynak değerleri değişebilir .

Olası çözümlerden biri, e.OriginalSource'u kullanmak ve VisualTreeHelper'ı kullanarak TreeViewItem'i bulmaktır:

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

bu olay TreeView veya TreeViewItem için mi?
Louis Rhys

1
Sağ tıklama boş bir konumdaysa her şeyin seçimini nasıl kaldıracağınız hakkında bir fikriniz var mı?
Louis Rhys

Diğer 5 tanesine yardımcı olan tek cevap ... Treeview popülasyonunda gerçekten yanlış bir şeyler yapıyorum, teşekkürler.

3
Louis Rhys'in sorusuna yanıt olarak: if (treeViewItem == null) treeView.SelectedIndex = -1veya treeView.SelectedItem = null. Ben de çalışması gerektiğine inanıyorum.
James M

24

Yalnızca XAML'li bir çözüm istiyorsanız, Karışım Etkileşimi'ni kullanabilirsiniz.

TreeViewVerinin, bir Booleanözelliğe IsSelectedve bir Stringözelliğe sahip olan görünüm modellerinin hiyerarşik bir koleksiyonuna ve Nameayrıca adlandırılmış bir alt öğe koleksiyonuna bağlı olduğunu varsayın 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>

İki ilginç kısım var:

  1. TreeViewItem.IsSelectedMülkiyet bağlı olduğu IsSelectedgörünümü-model üzerinde mülkiyet. Ayar IsSelectedtrue view-model üzerinde mülkiyet ağacında karşılık gelen düğümü seçecektir.

  2. Tüm PreviewMouseRightButtonDown(Bu örnek, bir düğümün görsel kısmı ile tetiklenir TextBlock) IsSelected, görüntüleme modeline özelliği doğru olarak ayarlanır. 1'e geri dönersek, ağaçta tıklanan ilgili düğümün seçili düğüm olduğunu görebilirsiniz.

Projenizde Harman Etkileşimi elde etmenin bir yolu NuGet paketini Unofficial.Blend.Interactivity kullanmaktır .


2
Harika cevap, teşekkürler! Göstermek için yararlı olacağını ive eihangi montajlar onlar varsayalım bulunabilir olsa için ad eşleştirmeleri kararlılık. xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"Ve xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"sırasıyla System.Windows.Interactivity ve Microsoft.Expression.Interactions meclisleri bulunan hangi.
prlc

Bu , kullanıcı arabiriminin parçası olmayan bağlı veri nesnesinin ChangePropertyActionbir IsSelectedözelliğini ayarlamaya çalıştığı için yardımcı olmadı , bu yüzden IsSelectedözelliği yok. Yanlış bir şey mi yapıyorum?
Antonín Procházka

@ AntonínProcházka: Cevabım, "veri nesnenizin" (veya görünüm modelinizin) IsSelectedcevabımın ikinci paragrafında belirtildiği gibi bir özelliğe sahip olmasını gerektiriyor : Verinin, Boole özelliğine sahip hiyerarşik bir görünüm modelleri koleksiyonuna bağlı olduğunu varsayın ...TreeViewIsSelected (vurgum).
Martin Liversage

16

"İtem.Focus ();" kullanarak "item.IsSelected = true;" kullanılarak% 100 çalışmıyor gibi görünüyor yapar.


Bu ipucu için teşekkürler. Bana yardımcı oldu.
i8abug

İyi bahşiş. Önce Focus () 'u çağırıyorum ve sonra IsSelected = true ayarlıyorum.
Jim Gomes

12

XAML'de, XAML'de PreviewMouseRightButtonDown işleyicisi ekleyin:

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

Ardından olayı şu şekilde ele alın:

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

2
Beklendiği gibi çalışmıyor, her zaman gönderici olarak kök öğeyi alıyorum. Benzer bir çözüm buldum social.msdn.microsoft.com/Forums/en-US/wpf/thread/… Bu şekilde eklenen olay işleyicileri beklendiği gibi çalışıyor. Kabul etmek için kodunuzda herhangi bir değişiklik var mı? :-)
alex2k8

Görünüşe göre ağaç görünümünü nasıl doldurduğunuza bağlı. Gönderdiğim kod çalışıyor, çünkü araçlarımdan birinde kullandığım kod tam olarak bu.
Stefan

Burada bir hata ayıklama noktası ayarlarsanız, göndericinizin ne tür olduğunu görebilirsiniz, bu da elbette ağacı nasıl

Bu, işe yaradığında en basit çözüm gibi görünüyor. Benim için çalıştı. Aslında, göndereni bir TreeViewItem olarak atmalısınız çünkü değilse, bu bir hata.
craftworkgames

12

Alex2k8'deki orijinal fikri kullanarak, Wieser Software Ltd'den görsel olmayanları doğru şekilde işlemek, Stefan'dan XAML, Erlend'den IsSelected ve gerçekten statik yöntemi Genel yapma konusundaki katkım:

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>

Arkasındaki C # kodu:

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

Düzenleme: Önceki kod bu senaryo için her zaman iyi çalıştı, ancak başka bir senaryoda VisualTreeHelper.GetParent, LogicalTreeHelper bir değer döndürdüğünde null döndürdü, bu yüzden düzeltildi.


1
Bunu daha da ileri götürmek için, bu cevap bunu bir DependencyProperty uzantısında uygular: stackoverflow.com/a/18032332/84522
Terrence

7

Neredeyse Doğru , ancak ağaçtaki görsel olmayanlara dikkat etmeniz gerekiyor (örneğin a gibi 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; 
}

bu genel yöntem biraz garip görünüyor, TreeViewItem yazarken onu nasıl kullanabilirim treeViewItem = VisualUpwardSearch <TreeViewItem> (e.OriginalSource as DependencyObject); bana dönüştürme hatası veriyor
Rati_Ge

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

6

Bence bir sınıf eğiticisinin kaydedilmesi hile yapmalı. TreeViewItem'in PreviewMouseRightButtonDownEvent öğesinde app.xaml.cs kod dosyanıza şu şekilde yönlendirilmiş bir olay işleyicisi kaydedin:

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

Benim için çalıştı! Ve çok da basit.
dvallejo

2
Merhaba Nathan. Kodun küresel olduğu ve her TreeView'ı etkileyeceği gibi görünüyor. Yalnızca yerel olan bir çözüme sahip olmak daha iyi olmaz mıydı? Yan etkiler yaratabilir mi?
Eric Ouellet

Bu kod, tüm WPF uygulaması için gerçekten geneldir. Benim durumumda, bu gerekli bir davranıştı, bu nedenle uygulamada kullanılan tüm ağaç görünümleri için tutarlıydı. Bununla birlikte, bu olayı bir ağaç görünümü örneğinin kendisine kaydedebilirsiniz, böylece yalnızca o ağaç görünümünde uygulanabilir.
Nathan Swannet

2

MVVM kullanarak çözmenin başka bir yolu, görünüm modelinize sağ tıklama için bağlama komutudur. Orada diğer mantığı da belirtebilirsiniz source.IsSelected = true. Bu yalnızca ' xmlns:i="http://schemas.microsoft.com/expression/2010/intera‌​ctivity"dan kullanır System.Windows.Interactivity.

Görünüm için 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>

Modeli görüntüle:

    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 yöntemiyle çocukları seçerken sorun yaşıyordum. Bir düğümün alt öğesini seçersem, bir şekilde o çocuğun kök ebeveynini seçerdi. MouseRightButtonDown olayının çocuğun olduğu her seviye için çağrılacağını öğrendim. Örneğin bir ağacınız varsa şuna benzer:

Öğe 1
   - Çocuk 1
   - Çocuk 2
      - Alt Öğe1
      - Alt Öğe2

Alt öğe2'yi seçersem, olay üç kez tetiklenir ve öğe 1 seçilir. Bunu bir boole ve bir asenkron çağrı ile çözdüm.

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

Biraz daha fazla hissediyor ama temelde boolean ilk geçişte doğru olarak ayarlıyorum ve birkaç saniye içinde başka bir iş parçacığında sıfırlanmasını sağlıyorum (bu durumda 3). Bu, ağacın yukarı çıkmaya çalışacağı bir sonraki geçişlerin atlanacağı ve sizi doğru düğüm seçili bırakacağı anlamına gelir. Şimdiye kadar çalışıyor gibi görünüyor :-)


Cevap setine olduğu MouseButtonEventArgs.Handlediçin true. Çocuk aranacak ilk kişi olduğu için. Bu özelliğin true olarak ayarlanması, üst öğeye yapılan diğer çağrıları devre dışı bırakır.
Basit Anwer

0

Fare ile aşağı inme olayıyla bunu seçebilirsiniz. Bu, içerik menüsü devreye girmeden önce seçimi tetikleyecektir.


0

MVVM kalıbı içinde kalmak istiyorsanız, aşağıdakileri yapabilirsiniz:

Görünüm:

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

Arkasındaki Kod:

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

Artık ClickedTreeElement özellik değişikliğine tepki verebilir veya ClickedTreeElement ile dahili olarak çalışan bir komut kullanabilirsiniz.

Genişletilmiş Görünüm:

<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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.