#c# #wpf #mvvm #treeview
#c# #wpf #mvvm #treeview
Вопрос:
У меня есть ObservableCollection, который я заполняю во время выполнения, который привязан к TreeView. Когда коллекция обновляется, корневой объект коллекции появляется в TreeView без какого-либо способа его расширения (см. Первое изображение).
Я предположил, что это означает, что возникла проблема с привязкой, однако удаление TreeView.Тег ItemCotainerStyle выводит стрелку, и все работает так, как задумано (см. Второе изображение). Это поведение работает и в обратном направлении, если тега style нет в представлении, и я добавляю его после обновления коллекции, тогда появятся стрелки.
Теги стиля не нужны для работы какой-либо функции в моем проекте, они просто остались от примера, с которым я работал.
<TreeView ItemsSource="{Binding CompileMessages}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded"
Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight"
Value="Normal" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:CompileMessagesDto}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Path=State}"
Margin="3"
Foreground="White" />
<TextBlock Text="{Binding Path=Path}"
FontWeight="Normal"
Foreground="White"
FontSize="12"
Margin="3" />
<TextBlock Text="{Binding Path=Description}"
FontWeight="Normal"
FontSize="12"
Foreground="#ff8000"
Margin="3" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
public class CompileMessagesDto
{
public string State { get; set; } = "";
public string Path { get; set; } = "";
public string Description { get; set; } = "";
public string Parent { get; set; }
public ObservableCollection<CompileMessagesDto> Children { get; set; } = new ObservableCollection<CompileMessagesDto>();
}
Просмотр дерева перед изменением
Просмотр дерева после удаления тега стиля
При инициализации коллекции с помощью тестовых значений отображаются стрелки, только после изменения коллекции во время выполнения TreeView ведет себя так. Я использую MaterialDesignInXaml, если это вообще помогает.
Редактировать (как обновляется коллекция)
Это определенно не лучший способ сделать это, но я перебираю коллекцию сообщений компилятора и добавляю их в коллекцию. Я остаюсь с каждым объектом и ссылкой на их родителя.
foreach (CompilerResultMessage message in messages)
{
CompileMessagesDto compileMessage = new CompileMessagesDto { State = message.State.ToString(), Path = message.Path, Description = message.Description, Parent = parent };
MessageCrawl(message.Messages, message.Path);
CompileMessages.Add(compileMessage);
}
Затем я устанавливаю дочерние элементы каждого объекта и удаляю каждый объект из коллекции, который не является корневым объектом. Оставляя корень с деревом дочерних элементов в нем.
List<CompileMessagesDto> removeItems = new List<CompileMessagesDto>();
foreach (CompileMessagesDto message in CompileMessages)
{
message.Children = new ObservableCollection<CompileMessagesDto>(CompileMessages.Where(c => c.Parent == message.Path));
if (message.Parent != "Root") { removeItems.Add(message); }
}
foreach (CompileMessagesDto message in removeItems)
{
CompileMessages.Remove(message);
}
Комментарии:
1. Это потому, что ваши привязки в
Style
ошибках throw. Ваши модели данныхIsExpanded
не имеют ниIsSelected
свойства.2. @BionicCode Я отредактировал вопрос, чтобы показать, как обновляется коллекция. Даже когда стиль удаляется перед запуском решения, древовидный вид отображается некорректно. Если я добавлю стиль обратно (так же, как я удаляю его на изображениях), появятся стрелки.
Ответ №1:
Вы перезаписываете источник привязки CompileMessagesDto.Children
:
message.Children = new ObservableCollection<CompileMessagesDto>(...);
Поскольку CompileMessagesDto
не реализует INotifyProeprtyChanged
Binding
, не сможет распознать изменение свойства.
Общее правило привязки данных: источник привязки всегда должен реализовывать свои свойства, как DependencyProperty
если бы источник был подклассом DependencyObject
. В противном случае исходный объект должен быть реализован INotifyPropertyChanged
.
Если вы этого не сделаете, это приведет к потенциальным утечкам памяти.
Чтобы решить вашу проблему, либо позвольте CompileMessagesDto
реализовать INotifyPropertyChanged
и вызвать PropertyChanged
событие из CompileMessagesDto.Children
свойства (рекомендуемое решение).
В качестве альтернативы сохраните текущий ObservableCollection
экземпляр и используйте Add
и Remove
для его изменения.
// Using for-statement allows to get rid of the 'removedItems' collection and the second foreach-statement (improve performance)
for (int index = CompileMessages.Count - 1; index >= 0; index--)
{
CompileMessages
.Where(c => c.Parent == message.Path)
.ToList()
.ForEach(message.Children.Add);
if (message.Parent != "Root")
{
CompileMessages.Remove(message);
}
}
Также обратите внимание, что ваш Style
выдает ошибки. CompileMessagesDto
не имеет ни IsExpanded
свойства, ни IsSelected
свойства.
Комментарии:
1. Извините, что не прояснил это в исходном вопросе. CompileMessages реализует NotifyPropertyChanged, когда он определен.
2. Все дело в
CompileMessagesDto.Children
свойстве.CompileMessages
проблема здесь не в этом.3. Мой плохой, я понимаю. Есть идеи, почему пользовательский интерфейс, похоже, исправляется при изменении тегов в этом случае? Я буду реализовывать то, что вы предложили сейчас.
4. Не уверен, что вы имеете в виду, извините. Как я уже говорил ранее, когда вы применяете
Style
установщики, генерируются ошибки, которые не позволяют вашим привязкам элементов работать должным образом. При удаленииStyle
ошибки исчезают, и привязки могут выполняться правильно.5. Механизм привязки пытается настроить систему уведомлений об изменении резервного свойства (потому
CompileMessagesDto
что не реализуетINotifyPropertyChanged
). Ошибки привязки отменяют эту настройку. Эта резервная система уведомлений создает статические поля для хранения исходного объекта привязки. Статические поля никогда не будут собираться мусором. Таким образом, резервная система создает утечку памяти для каждой привязки. Кроме того, эта резервная система работает чрезвычайно медленно. Всегда используйтеDependencyProeprty
или реализуйтеINotifyPropertyChanged
, чтобы избежать резервной системы.