Para criar uma árvore de navegação rica e escalável em aplicações WPF, é essencial dominar a construção de HierarchicalDataTemplate com múltiplos níveis. Ao contrário de templates planos, modelos hierárquicos permitem representar relações pai-filho naturalmente — como pastas contendo subpastas, que por sua vez contêm arquivos — tudo com suporte nativo a notificação de alterações e binding bidirecional.
O segredo está na cadeia de ItemsSource vinculada a propriedades de coleção observável em cada nível. Cada template deve declarar explicitamente:
DataType: tipo CLR da classe que o template representa (ex:Department,Team,Employee);ItemsSource: caminho de propriedade que retorna a coleção de itens do nível imediatamente inferior;- Conteúdo visual: elementos de UI que exibem dados da instância atual (ex:
TextBlockligado à propriedadeName).
Abaixo segue um exemplo reescrito com estrutura clara, nomes descritivos e lógica robusta — sem dependência de classes genéricas ou convenções obscuras:
<!-- Nível 2: Equipes -->
<HierarchicalDataTemplate DataType="{x:Type local:Team}" ItemsSource="{Binding Members}">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Source="/Assets/team.png" Width="16" Height="16" Margin="0,0,4,0"/>
<TextBlock Text="{Binding TeamTitle}" FontStyle="Italic" Foreground="#3498DB"/>
</StackPanel>
</HierarchicalDataTemplate>
<!-- Nível 3: Membros individuais -->
<DataTemplate DataType="{x:Type local:StaffMember}">
<StackPanel Orientation="Horizontal" Margin="2">
<Image Source="/Assets/person.png" Width="16" Height="16" Margin="0,0,4,0"/>
<TextBlock Text="{Binding FullName}" Foreground="#27AE60"/>
</StackPanel>
</DataTemplate>
</TreeView.Resources> </TreeView>
</div>As classes subjacentes devem implementar `INotifyPropertyChanged` e usar `ObservableCollection<T>` para garatnir atualizações automáticas da interface. Veja as versões simplificadas e modernizadas:
<div class="code-block">```
public class Department : INotifyPropertyChanged
{
private string _displayName;
private ObservableCollection<Team> _subDepartments = new();
public string DisplayName
{
get => _displayName;
set { _displayName = value; OnPropertyChanged(); }
}
public ObservableCollection<Team> SubDepartments => _subDepartments;
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string TeamTitle
{
get => _teamTitle;
set { _teamTitle = value; OnPropertyChanged(); }
}
public ObservableCollection<StaffMember> Members => _members;
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
</div><div class="code-block">```
public class StaffMember : INotifyPropertyChanged
{
private string _fullName;
public string FullName
{
get => _fullName;
set { _fullName = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
OrganizationalTree.ItemsSource = root;
</div>Para estilização avançada, é possível substituir o `ControlTemplate` padrão do `TreeViewItem`, personalizendo ícones de expansão, linhas de conexão e estados visuais (selecionado, desabilitado, expandido). O uso de `ToggleButton` customizado com `ControlTemplate` permite total controle sobre a aparência do botão de expansão — inclusive com gradientes, animações e comportamento condicional baseado em `IsChecked`.