Pergunta

Eu tenho um TreeView que usa um HierarchicalDataTemplate para vincular os seus dados.

Parece que este:

<TreeView x:Name="mainTreeList" ItemsSource="{Binding MyCollection}>
  <TreeView.Resources>
    <HierarchicalDataTemplate 
     DataType="{x:Type local:MyTreeViewItemViewModel}" 
     ItemsSource="{Binding Children}">
      <!-- code code code -->
    </HierarchicalDataTemplate>
  </TreeView.Resources>
</TreeView>

Agora, a partir do código-behind de dizer que a janela principal, eu quero pegar a corrente TreeViewItem selecionado. No entanto, se eu usar:

this.mainTreeList.SelectedItem;

O selectedItem é do tipo MyTreeViewItemViewModel. Mas eu quero tirar o 'pai' ou TreeViewItem 'limite'. Eu não passar isso para o meu objeto TreeViewItemModel (nem sequer sei como).

Como posso fazer isso?

Foi útil?

Solução

A partir Bea Stollnitz 's entrada de blog sobre isso, tente

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

Outras dicas

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition));

não funciona (para mim) como mainTreeList.Items.CurrentPosition em um treeview usando um HierarchicalDataTemplate sempre será -1.

Nem abaixo como como mainTreeList.Items.CurrentItem em um treeview usando um HierarchicalDataTemplate sempre será nulo.

TreeViewItem item = (TreeViewItem)mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.Items.CurrentItem);

em vez eu tive que definir um último TreeViewItem selecionado no evento TreeViewItem.Selected encaminhado que borbulha até a vista de árvore (o TreeViewItem é em si não existe em tempo de design, como estamos usando um HierarchicalDataTemplate ).

O evento pode ser capturado em XAML assim:

<TreeView TreeViewItem.Selected="TreeViewItemSelected" .../> 

Em seguida, a última TreeViewItem selecionado pode ser definido no evento assim:

    private void TreeViewItemSelected(object sender, RoutedEventArgs e)
    {
        TreeViewItem tvi = e.OriginalSource as TreeViewItem;

        // set the last tree view item selected variable which may be used elsewhere as there is no other way I have found to obtain the TreeViewItem container (may be null)
        this.lastSelectedTreeViewItem = tvi;

        ...
     }

Corri para este mesmo problema. Eu precisava chegar ao TreeViewItem para que eu pudesse tê-lo ser selecionado. Então eu percebi que eu poderia apenas adicionar uma propriedade IsSelected ao meu ViewModel, que, em seguida, ligado ao TreeViewItems IsSelectedProperty. Isto pode ser conseguido com o ItemContainerStyle:

<TreeView>
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                </Style>
            </TreeView.ItemContainerStyle>
</TreeView>

Agora, se eu quiser ter um item na treeview selecionado, eu só chamar IsSelected em minha classe ViewModel diretamente.

Espero que ajude alguém.

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromIndex(mainTreeList.Items.CurrentPosition)); gives first item in the TreeView because CurrentPosition is always 0.

Como cerca

TreeViewItem item = (TreeViewItem)(mainTreeList
    .ItemContainerGenerator
    .ContainerFromItem(mainTreeList.SelectedItem)));

Isso funciona melhor para mim.

Inspirado pela resposta de Fëanor, eu tentei fazer o TreeViewItem facilmente acessível para cada item de dados que um TreeViewItem foi criado.

A idéia é adicionar um campo digitado-TreeViewItem ao modelo de vista, também expostos por meio de uma interface, e ter a TreeView preenchê-lo automaticamente sempre que o recipiente TreeViewItem é criado.

Isso é feito por subclasse TreeView e anexando um evento para o ItemContainerGenerator, que registra a TreeViewItem sempre que é criado. Gotchas incluem o fato de que TreeViewItems são criados preguiçosamente, então não há realmente não poderia ser um disponível em determinados momentos.

Uma vez que postar esta resposta, eu desenvolvi este mais e usado por um longo tempo em um projeto. Sem problemas até agora, além do fato de que esta viola MVVM (mas também poupa uma tonelada de clichê para os casos simples). Fonte aqui .

Uso

Selecione o pai do item selecionado e recolhê-la, garantindo que ele está no modo de exibição:

...
var selected = myTreeView.SelectedItem as MyItem;
selected.Parent.TreeViewItem.IsSelected = true;
selected.Parent.TreeViewItem.IsExpanded = false;
selected.Parent.TreeViewItem.BringIntoView();
...

As declarações:

<Window ...
        xmlns:tvi="clr-namespace:TreeViewItems"
        ...>
    ...
    <tvi:TreeViewWithItem x:Name="myTreeView">
        <HierarchicalDataTemplate DataType = "{x:Type src:MyItem}"
                                  ItemsSource = "{Binding Children}">
            <TextBlock Text="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
    </tvi:TreeViewWithItem>
    ...
</Window>
class MyItem : IHasTreeViewItem
{
    public string Name { get; set; }
    public ObservableCollection<MyItem> Children { get; set; }
    public MyItem Parent;
    public TreeViewItem TreeViewItem { get; set; }
    ...
}

Código

public class TreeViewWithItem : TreeView
{
    public TreeViewWithItem()
    {
        ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
    }

    private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        var generator = sender as ItemContainerGenerator;
        if (generator.Status == GeneratorStatus.ContainersGenerated)
        {
            int i = 0;
            while (true)
            {
                var container = generator.ContainerFromIndex(i);
                if (container == null)
                    break;

                var tvi = container as TreeViewItem;
                if (tvi != null)
                    tvi.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;

                var item = generator.ItemFromContainer(container) as IHasTreeViewItem;
                if (item != null)
                    item.TreeViewItem = tvi;

                i++;
            }
        }
    }
}

interface IHasTreeViewItem
{
    TreeViewItem TreeViewItem { get; set; }
}

Tente algo parecido com isto:

    public bool UpdateSelectedTreeViewItem(PropertyNode dateItem, ItemsControl itemsControl)
    {
        if (itemsControl == null || itemsControl.Items == null || itemsControl.Items.Count == 0)
        {
            return false;
        }
        foreach (var item in itemsControl.Items.Cast<PropertyNode>())
        {
            var treeViewItem = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (treeViewItem == null)
            {
                continue;
            }
            if (item == dateItem)
            {
                treeViewItem.IsSelected = true;
                return true;
            }
            if (treeViewItem.Items.Count > 0 && UpdateSelectedTreeViewItem(dateItem, treeViewItem))
            {
                return true;
            }
        }
        return false;
    }

A única 'ItemContainerGenerator.ContainerFromItem' ou chamada de 'ItemContainerGenerator.ItemContainerGenerator' não consegue encontrar o TreeViewItem associado pela exibição de árvore de objeto, uma vez que a separação de controlador de exibição de árvore item e os dados de exibição de árvore item. É necessário criar uma função recursiva para usar 'ItemContainerGenerator.ContainerFromItem' e 'ItemContainerGenerator.ItemContainerGenerator', para localizar TreeViewItem na exibição em árvore. Exemplo função recursiva poderia ser semelhante a:

private TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object tvio)
{
    var item = container.ContainerFromItem(tvio) as TreeViewItem;
    if (item != null)
    {
        return item;
    }

    for (int i = 0; i < container.Items.Count; i++)
    {
        var subContainer = (TreeViewItem)container.ContainerFromIndex(i);
        if (subContainer != null)
        {
            item = GetTreeViewItemFromObject(subContainer.ItemContainerGenerator, tvio);
            if (item != null)
            {
                return item;
            }
        }
    }

    return null;
}

chamado por alvo var = GetTreeViewItemFromObject (treeView.ItemContainerGenerator, item);

A função recursiva só funciona depois da exibição em árvore 'ItemContainerGenerator.Status' é 'ContainersGenerated'. Assim, durante o período vista inicialização, 'GetTreeViewItemFromObject' não funciona.

I modificado pesquisa recursiva de William para uma versão mais compacta:

public TreeViewItem GetTreeViewItemFromObject(ItemContainerGenerator container, object targetObject) {
    if (container.ContainerFromItem(targetObject) is TreeViewItem target) return target;
    for (int i = 0; i < container.Items.Count; i++)
        if ((container.ContainerFromIndex(i) as TreeViewItem)?.ItemContainerGenerator is ItemContainerGenerator childContainer)
            if (GetTreeViewItemFromObject(childContainer, targetObject) is TreeViewItem childTarget) return childTarget;
    return null;
}

Um chamaria isso fornecendo ItemContainerGenerator da instância TreeView eo objeto de dados de destino:

TreeViewItem tvi = GetTreeViewItemFromObject(treeView.ItemContainerGenerator, targetDataObject);

Você precisa do TreeViewItem porque você está indo para modificar o que está sendo exibido? Se for esse o caso, eu recomendo usar um estilo para alterar a forma como o item está sendo exibido em vez de usar code-behind em vez de diretamente modificar o TreeViewItem. Ele deve esperamos ser mais limpo.

Se você tem que encontrar item filhos dos filhos que você pode ter que usar a recursividade como este

public bool Select(TreeViewItem item, object select) // recursive function to set item selection in treeview
{
    if (item == null)
        return false;
    TreeViewItem child = item.ItemContainerGenerator.ContainerFromItem(select) as TreeViewItem;
    if (child != null)
    {
        child.IsSelected = true;
        return true;
    }
    foreach (object c in item.Items)
    {
        bool result = Select(item.ItemContainerGenerator.ContainerFromItem(c) as TreeViewItem, select);
        if (result == true)
            return true;
    }
    return false;
}

Aqui está a solução. rtvEsa é treeview. HierarchicalDataTemplate é modelo treeview Tag fazer consumation real do item atual. Este item não está selecionado é o item atual no controle de árvore que o uso HierarchicalDataTemplate.

Items.CurrentItem faz parte da colecção árvore interna. Você não pode obter muitas e diferentes dados. Por exemplo Items.ParenItem também.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

  <TextBox Tag="{Binding ElementName=rtvEsa, Path=Items.CurrentItem }" />

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top