Как сделать привязку данных WPF TreeView медленной и асинхронной? - PullRequest
3 голосов
/ 01 июня 2010

Я учусь использовать привязку данных в WPF для TreeView. Я процедурно создаю объект Binding, задаю свойства Source, Path и Converter, чтобы они указывали на мои собственные классы. Я могу даже пойти до установки IsAsync, и я вижу асинхронное обновление графического интерфейса при изучении дерева. Пока все хорошо!

Моя проблема в том, что WPF охотно оценивает части дерева, прежде чем они будут развернуты в графическом интерфейсе. Если оставить его достаточно долго, это приведет к оценке всего дерева (ну, на самом деле, в этом примере мое дерево бесконечно, но вы поняли идею). Я бы хотел, чтобы дерево оценивалось только по требованию, когда пользователь расширяет узлы. Возможно ли это с помощью существующего асинхронного связывания данных в WPF?

Помимо этого, я не выяснил, как ObjectDataProvider относится к этой задаче.


Мой код XAML содержит только один объект TreeView, а мой код C #:

public partial class Window1 : Window
{
    public Window1() {
        InitializeComponent();

        treeView.Items.Add( CreateItem(2) );
    }

    static TreeViewItem CreateItem(int number)
    {
        TreeViewItem item = new TreeViewItem();
        item.Header = number;

        Binding b = new Binding();
        b.Converter = new MyConverter();
        b.Source = new MyDataProvider(number);
        b.Path = new PropertyPath("Value");
        b.IsAsync = true;
        item.SetBinding(TreeView.ItemsSourceProperty, b);

        return item;
    }

    class MyDataProvider
    {
        readonly int m_value;

        public MyDataProvider(int value) {
            m_value = value;
        }

        public int[] Value {
            get {
                // Sleep to mimick a costly operation that should not hang the UI
                System.Threading.Thread.Sleep(2000);
                System.Diagnostics.Debug.Write(string.Format("Evaluated for {0}\n", m_value));
                return new int[] {
                    m_value * 2, 
                    m_value + 1,
                };
            }
        }
    }

    class MyConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            // Convert the double to an int.
            int[] values = (int[])value;
            IList<TreeViewItem> result = new List<TreeViewItem>();
            foreach (int i in values) {
                result.Add(CreateItem(i));
            }
            return result;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            throw new InvalidOperationException("Not implemented.");
        }
    }    
}

Примечание: Ранее мне удавалось выполнять ленивую оценку узлов дерева, добавляя обработчики событий WPF и непосредственно добавляя элементы при срабатывании обработчиков событий. Я пытаюсь отойти от этого и вместо этого использовать привязку данных (что, как я понимаю, больше соответствует духу «пути WPF»).

1 Ответ

3 голосов
/ 01 июня 2010

Универсальное решение (поскольку я не уверен, что ваш код не фиктивный)

  1. Создайте свою модель, содержащую родительские и дочерние элементы (в данном случае это int и список int)
  2. Создание ViewModel со свойством IsExpanded в дополнение к свойствам модели
  3. Свяжите свое свойство IsExpanded со свойством IsViewpanded TreeViewItem в представлении (xaml)
  4. В установщике свойства IsExpanded заполните список «Дети» с помощью диспетчера, приоритет которого - «Фон». Каждое добавление элемента в ваш список детей должно вызывать событие PropertyChanged.

Проверьте шаблон проектирования MVVM , если вы не знакомы с. Вот хорошее видео от Джейсона

...