Как я уже упоминал в комментарии к вашему сообщению, могут возникнуть две проблемы, которые нам нужно решить.
- У вас много элементов, поэтому, когда вы расширяете узел - wpf начинает генерировать контейнеры элементов и помещать их в визуальное дерево. Это может быть раздражающим.
- С другой стороны, я предполагаю следующий сценарий - пользователь расширяет элемент узла, и вы отправляете запрос к базе данных / удаленному серверу, который может выполняться некоторое время.
WPF не предоставляет событие BeforeExpand, поэтому вам следует использовать данные просмотра, уведомления об изменении свойств, привязку данных и наблюдаемые коллекции. Я проиллюстрирую это в простом приложении.
Весь подход можно приблизительно описать так:
- Привязать свойство IsExpanded данных представления к свойству IsExpanded TreeViewItem
- Слушайте свойство IsExpanded где-то изменились уведомления (может быть в модели)
- Установить свойство IsLoading (в моем sapmle оно объявлено в данных вида)
- Запустить задачу, которая заполняет внутренние предметы
- Сбросить свойство IsLoading
- Поместите DataTrigger в xaml, который реагирует на свойство IsLoading.
Я разработал класс ViewData для своего элемента дерева с наблюдаемыми свойствами IsExpanded, IsLoaded, наблюдаемой коллекцией дочерних элементов и именем, которое будет отображаться:
public class TreeItem : ObservableObject
{
public TreeItem (string name)
{
this.Name = name;
}
public string Name
{
get;
private set;
}
private bool _isExpanded;
public bool IsExpanded
{
get
{
return this._isExpanded;
}
set
{
if (this._isExpanded != value)
{
this._isExpanded = value;
this.OnPropertyChanged("IsExpanded");
}
}
}
private bool _isLoading;
public bool IsLoading
{
get
{
return this._isLoading;
}
set
{
if (this._isLoading != value)
{
this._isLoading = value;
this.OnPropertyChanged("IsLoading");
}
}
}
private ObservableCollection<TreeItem> _innerItems = new ObservableCollection<TreeItem>();
public IEnumerable<TreeItem> InnerItems
{
get
{
return this._innerItems;
}
}
public void LoadInnerItems()
{
this.IsLoading = true;
var sc= SynchronizationContext.Current;
new Thread(new ThreadStart(() =>
{
Thread.Sleep(5000);
sc.Send(o =>
{
this._innerItems.Add(new TreeItem("1223"));
this._innerItems.Add(new TreeItem("2345"));
this._innerItems.Add(new TreeItem("666678"));
this.IsLoading = false;
}, null);
}))
.Start();
}
}
Следующий шаг заключается в разработке модели презентации. Как вы можете видеть, наша модель слушает событие IsExpanded PropertyChanged и когда оно равно истинному началу. LoadingInnerItems .:
public class SampleModel : ObservableObject
{
public SampleModel()
{
this._items.Add(new TreeItem("AAA"));
this._items.Add(new TreeItem("BBB"));
this._items.Add(new TreeItem("CCC"));
foreach (var i in this._items)
{
i.PropertyChanged += new PropertyChangedEventHandler(Item_PropertyChanged);
}
}
void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsExpanded")
{
var item = (TreeItem)sender;
if(item.IsExpanded)
{
item.LoadInnerItems();
}
}
}
private ObservableCollection<TreeItem> _items = new ObservableCollection<TreeItem>();
public IEnumerable<TreeItem> Items
{
get
{
return this._items;
}
}
}
XAML для нашего TreeView. Когда мы загружаем внутренние элементы - передний план нашего дерева выглядит красным:
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding InnerItems}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="Foreground"
Value="Aqua"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoading}"
Value="True">
<Setter Property="Foreground"
Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
И не забудьте установить модель как текстовый вид данных в коде:
public MainWindow()
{
InitializeComponent();
this.Model = new SampleModel();
}