0

У меня есть компонент TreeView, и в него подтягиваются пункты из древовидной структуры через ObservableCollection. При добавлении новых пунктов сами-то пункты добавляются, но у этих пунктов есть глубинные свойства, и их изменение ни к чему ни приводит.

введите сюда описание изображения

Я добавил 2 пункта с "латинским названием" newItem - это получилось, но добавление русского названия не достучалось до TreeView, с его точки зрения это верно: сами-то коллекции не поменялись. Что тут можно сделать? Напрашивающийся вариант: существует ли какой-то метод у ObservableCollection, который запускается при изменении коллекции, и который можно форсированно пульнуть, как типа коллекция поменялась? Тогда TreeView прочитает динамическое свойство (учитывающее русское название из глубинны) через Binding и всё встанет на свои места.

Схема кода примерно такова:

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

public class InfoItem : NotifyPropertyChanged, IEnumerable<InfoItem> // то, что поступает в пункты TreeView { ... private readonly ObservableCollection<InfoValue> infoValues = new(); // Так называемые глубинные свойства private readonly ObservableCollection<InfoItem> items = new(); // Дочерние пункты ...

public virtual string Declaration
{
    get
    {
        InfoValue v = FindValueDeep(&quot;Имя&quot;);
        return v == null || v.Size &lt;= 0 ? Caption : Caption + &quot;. &quot; + v.GetStrValue()[0];
        // Caption - свойство с одним геттером, вычисляется из name. Код длинный, поэтому не привожу

    }
}

public string Name
{
    get =&gt; name;
    set
    {
        if (!name.Equals(value))
        {
            string oldName = name;
            name = value;
            OnPropertyChanged();
        }
    }
}


}

public class InfoValue : IEnumerable { ... private byte[] value; // Бинарное содержание этого глубинного свойства ...

public string[] GetStrValue() // Через этот метод получаем русское название - для TreeView нужен только [0]-элемент
{
    ...
    // Здесь происходит преобразование из бинарки в текстовый массив
    // Свойства могут быть не только текстовые, но они все хранятся в бинарках
}

}

AXAML виджета:

<TreeView
    Name="SpaceItemTree"
    Items="{Binding SpaceTree}"
    Background="Aqua">
    <TreeView.ItemTemplate>
        <TreeDataTemplate ItemsSource="{Binding Items}">
            <TextBlock Text="{Binding Declaration}"/>
        </TreeDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

ViewModel, привязываемая к виджету:

public class TreeWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    public IReadOnlyList<InfoItem> SpaceTree => Program.classifier[2, 1, 1, 0, 5].Items; // Возвращает Items определённого пункта (класс InfoItem)
}
  • 1
    Нужна реализация INotifyPropertyChanged на тех самых свойствах в классе, примеры где-то тут. – aepot Oct 28 '22 at 15:39
  • но у этих пунктов есть глубинные свойства, и их изменение ни к чему ни приводит "...а класс с этими свойствами я не покажу, ну потому что зачем вам помогать отвечать на мой вопрос?" – aepot Oct 28 '22 at 15:40
  • @aepot я добавил в описание вопроса небольшую схему, код очень большой, дайте знать при необходимости, что ещё нужно показать – Pavel Sumarokov Oct 28 '22 at 16:37
  • Окей, понятнее не стало, а что из того что вы показали, вы хотите видеть в интерфейсе? их изменение ни к чему ни приводит - изменение чего, где оно? – aepot Oct 28 '22 at 16:58
  • @aepot, я хочу, чтобы когда в объекте InfoItem добавляется новое InfoValue в values или в конкретном InfoValue меняется содержание (value) - пункт в TreeView апдейтился так: $"{item.caption}. {Item.GetValue(0).GetStrValue()[0]}". Кроме простого caption я всë указал в коде – Pavel Sumarokov Oct 28 '22 at 17:08
  • Вы издеваетесь? А сейчас к чему у вас привязка данных? У вас на экране знаки вопроса, там наверное TextBlock, так? Он к чему-то привязан, так? К чему? Покажите разметку привязки, покажите это свойство в коде, на которое указывает эта привязка. У вас же проблема с обновлением, а не отображением, верно? Откройте в конце концов ссылку из вервого комментария, там все что вам нужно есть много раз. – aepot Oct 28 '22 at 17:17
  • @aepot попробовал вот эти 2 Ваших примера: https://ru.stackoverflow.com/questions/1091809 https://ru.stackoverflow.com/questions/1132061 Не получилось, апдейт не происходит. Дополнил код новой информацией, теперь-то я надеюсь достаточно инфы? – Pavel Sumarokov Oct 28 '22 at 19:53
  • Вызвать OnProperryChanged с именем этого свойства в сеттерах всех его моставляющих. Наличие сеттера у самого свойства не требуется. – aepot Oct 28 '22 at 20:25
  • @aepot поставил в байндинг свойство Name, имеющее сеттер - сработало. Дело в том, что свойство Declaration - не имеет сеттера, оно вычисляемое, причём оно зависит не только от собственных свойств InfoItem, но и от вложенных элементов значения List<InfoValues> поля infoValues. Сейчас разбираюсь с тем, как OnPropertyChange перекинуть из списка в материнскую модель – Pavel Sumarokov Oct 28 '22 at 20:25
  • Выглядит так, как-будто вы перемудрили. Вот еще пример нашел https://ru.stackoverflow.com/a/1270508/373567. Упрощайте структуру данных. – aepot Oct 28 '22 at 20:31

1 Answers1

1

Так как вопрос свелся к тому, как отслеживать get-only свойство, которое состоит из других свойств, приведу простой абстрактный пример.

Пусть будет человек, у него есть имя и фамилия, а так же полное имя из имени и фамилии.

public class NotifyPropertyChanged : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    =&gt; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

public class Person : NotifyPropertyChanged { private string _firstName; private string _lastName;

public string FirstName
{
    get =&gt; _firstName;
    set
    {
        _firstName = value;
        OnPropertyChanged();
        OnPropertyChanged(nameof(FullName));
    }
}

public string LastName
{
    get =&gt; _lastName;
    set
    {
        _lastName = value;
        OnPropertyChanged();
        OnPropertyChanged(nameof(FullName));
    }
}

public string FullName =&gt; $&quot;{FirstName} {LastName}&quot;;

}

Теперь при изменении имени или фамилии полное имя так же поменяется в интерфейсе.

<TextBlock Text="{Binding FullName}"/>
aepot
  • 49,560