Domanda

Ho un TreeView che usa un HierarchicalDataTemplate per legare i suoi dati.

Sembra così:

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

Ora, dal code-behind di dire la finestra principale, voglio ottenere l'attuale TreeViewItem . Tuttavia, se uso:

this.mainTreeList.SelectedItem;

L'elemento selezionato è di tipo MyTreeViewItemViewModel . Ma voglio ottenere il TreeViewItem "parent" o "bound". Non lo passo al mio oggetto TreeViewItemModel (non saprei nemmeno come).

Come posso farlo?

È stato utile?

Soluzione

Dal il blog di Bea Stollnitz su questo, prova

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

Altri suggerimenti

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

NON FUNZIONA (per me) come mainTreeList.Items.CurrentPosition in una treeview utilizzando un HierarchicalDataTemplate sarà sempre -1.

NESSUNO FA di seguito come mainTreeList.Items.CurrentItem in un treeview utilizzando un HierarchicalDataTemplate sarà sempre nullo.

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

INSTEAD Ho dovuto impostare un ultimo TreeViewItem selezionato nell'evento TreeViewItem.Selected indirizzato che bolle fino alla vista ad albero (gli stessi TreeViewItem non esistono in fase di progettazione poiché stiamo usando un HierarchicalDataTemplate ).

L'evento può essere catturato in XAML in questo modo:

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

Quindi l'ultimo TreeViewItem selezionato può essere impostato nell'evento in questo modo:

    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;

        ...
     }

Ho riscontrato questo stesso problema. Avevo bisogno di accedere a TreeViewItem in modo da poterlo selezionare. Mi sono quindi reso conto che potevo semplicemente aggiungere una proprietà IsSelected al mio ViewModel, che ho quindi associato a IsViewStemperPro TreeViewItems. Ciò può essere ottenuto con 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>

Ora, se voglio avere un elemento selezionato nella vista ad albero, chiamo direttamente IsSelected sulla mia classe ViewModel.

Spero che aiuti qualcuno.

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

Che ne dici

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

Questo funziona meglio per me.

Ispirato dalla risposta di Anor & # 8217; ho tentato di rendere TreeViewItem facilmente accessibile per ogni elemento di dati che ha un TreeViewItem stato creato per.

L'idea è di aggiungere un campo a forma di TreeViewItem al modello di vista, anch'esso esposto tramite un'interfaccia, e fare in modo che TreeView lo compili automaticamente ogni volta che Il contenitore TreeViewItem è stato creato.

Questo viene fatto sottoclassando TreeView e collegando un evento a ItemContainerGenerator , che registra il TreeViewItem ogni volta che viene creato. I trucchi includono il fatto che TreeViewItem viene creato pigramente, quindi potrebbe non essercene davvero uno disponibile in determinati momenti.

Da quando ho pubblicato questa risposta, l'ho sviluppata ulteriormente e l'ho usata a lungo in un progetto. Nessun problema finora, a parte il fatto che questo viola MVVM (ma consente anche di risparmiare un sacco di boilerplate per i casi semplici). Fonte qui .

Utilizzo

Seleziona l'elemento principale dell'elemento selezionato e comprimilo, assicurandoti che sia nella vista:

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

Dichiarazione:

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

Codice

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

Prova qualcosa del genere:

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

Una singola chiamata 'ItemContainerGenerator.ContainerFromItem' o 'ItemContainerGenerator.ItemContainerGenerator' non è in grado di trovare TreeViewItem associato all'oggetto vista albero, poiché la separazione tra il controller elemento vista albero e i dati elemento vista albero. È necessario creare una funzione ricorsiva per utilizzare 'ItemContainerGenerator.ContainerFromItem' e 'ItemContainerGenerator.ItemContainerGenerator', per individuare TreeViewItem nella vista ad albero. La funzione ricorsiva di esempio potrebbe apparire come:

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

chiamato da     var target = GetTreeViewItemFromObject (treeView.ItemContainerGenerator, item);

La funzione ricorsiva funziona solo dopo che 'ItemContainerGenerator.Status' nella vista ad albero è 'ContainersGenerated'. Pertanto, durante il periodo di visualizzazione dell'inizializzazione, "GetTreeViewItemFromObject" non funziona.

Ho modificato la ricerca ricorsiva di William in una versione più compatta:

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

Uno lo chiamerebbe fornendo ItemContainerGenerator dell'istanza TreeView e l'oggetto dati di destinazione:

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

Hai bisogno di TreeViewItem perché hai intenzione di modificare ciò che viene visualizzato? In tal caso, consiglierei di utilizzare uno stile per modificare la modalità di visualizzazione dell'elemento anziché utilizzare code-behind anziché modificare direttamente TreeViewItem. Si spera che dovrebbe essere più pulito.

Se devi trovare un oggetto nei figli dei bambini, potresti dover ricorrere alla ricorsione in questo modo

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

Ecco la soluzione. rtvEsa è treeview. HierarchicalDataTemplate è un modello treeview Il tag rende reale il consumo dell'articolo corrente. Questo non è l'elemento selezionato, è l'elemento corrente nel controllo dell'albero che utilizza HierarchicalDataTemplate.

Items.CurrentItem fa parte della raccolta interna degli alberi. Non puoi ottenere molti e diversi dati. Ad esempio Items.ParenItem.

  <HierarchicalDataTemplate ItemsSource="{Binding ChildItems}">

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top