Связывание двух свойств через мультисвязывание - PullRequest
1 голос
/ 17 ноября 2010

Я пытаюсь привязать несколько элементов управления WPF к дереву. Я создал оболочку, которая представляет методы в дереве как свойства. Я пытаюсь отфильтровать список с найденными совпадениями. Когда совпадений слишком много, я не хочу перечислять все совпадения, так как это занимает несколько секунд и совершенно не нужно. Я решил это декларативно, используя MultiBinding, который принимает и ленивые загрузки Matches, а затем NumMatches, который вычисляется намного быстрее. MultiBindingConverter сначала оценивает NumMatches, отправленные в функцию. Если это значение меньше определенного значения, только тогда оно пытается оценить свойство Matches.

Проблема в том, что когда изменяется Matches, изменяется и NumMatches. Таким образом, мой конвертер вызывается два раза, один раз, когда изменяется соответствие, и один, когда изменяется значение NumMatches. Это было бы хорошо, даже если, возможно, немного подвержено ошибкам, если бы WPF не (по-видимому) не кэшировал данные, потому что, хотя оба свойства изменились в то время, свежее значение не извлекается, а используется кэшированное. Преобразователь будет вызываться с несовпадающими аргументами, например, count будет 100 (безопасно для перечисления), но перечисление вызовет доступ к миллиону или двум значениям, поскольку новый список соответствий не выбирается, пока не будет вызван PropertyChanged for Matches (они обновляются в том же свойстве оболочки, помните) Как мне решить эту проблему? Я хочу решить это декларативно.

Мои идеи следующие:

1) Найдите лучший способ решения подкачки, не используя MultiBinding

2) Отключить кэширование

Какой из них наиболее правдоподобен? Насколько я здесь нарушил водопровод WPF?

Вот некоторые из моих XAML:

        <ListBox SelectionChanged="ListBox_SelectionChanged"  Height="250">
        <ListBox.ItemsSource>
            <MultiBinding>
                <MultiBinding.Converter>
                    <mine:PagingMultiValue> </mine:PagingMultiValue>
                </MultiBinding.Converter>
                <Binding Path="Matches"></Binding>
                <Binding Path="NumMatches"></Binding>
            </MultiBinding>
        </ListBox.ItemsSource>
    </ListBox>

UPDATE

Вот некоторые из моих TrieWrapper:

public string Text
        {
            get { return text; }
            set
            {
                if (string.Equals(text, value))
                    return;
                text = value;
                OnMatchesChanged();
                OnNumMatchesChanged();
                OnTextChanged();

            }
        }

Несмотря на то, что у меня есть контроль над источником моей структуры данных Trie, я бы предпочел не изменять его, выставляя текущие совпадения через свойство. Вот почему у меня есть этот "взломать" вместо этого. Когда вызывается OnMatchesChanged (), он обновляется старым значением NumMatches. Затем непосредственно после этого он обновляется с правильным значением для обоих. Если я изменяю порядок обновлений, единственное, что меняется, - это когда происходит неправильное перечисление, либо когда я добавляю в строку текстового поиска, либо удаляю из нее.

Спасибо!

Ответы [ 4 ]

0 голосов
/ 25 ноября 2010

Если у вас нет веских оснований сделать это по-другому, вам следует использовать тот факт, что ListBox имеет встроенную виртуализацию пользовательского интерфейса, и выполнить что-то в соответствии с положениями следующей статьи, чтобы построить виртуализацию данных.

http://blogs.microsoft.co.il/blogs/tomershamam/archive/2009/10/01/ui-virtualization-vs-data-virtualization-part-2.aspx

(ссылка на виртуализацию пользовательского интерфейса из MSDN)

http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx

Ваше текущее решение - это решение для виртуализации данных (оценивается лениво), но оно не выполненотаким образом, что WPF может легко взаимодействовать с ним.Используя пользовательскую реализацию ICollectionView (для удобства статья выводит ListCollectionView), вам не нужно ничего делать с ListBox, за исключением того, что для ItemsSource задан объект CollectionViewSource, связанный с вашим набором данных.Это делает XAML полностью читабельным (по сравнению с использованием нескольких привязок и т. Д.) И повышает производительность, так как он не будет использовать ваш конвертер для определения того, какие элементы должны отображаться, WPF будет использовать свои собственные методы для определения того, что должно быть на экране или нет,

0 голосов
/ 22 ноября 2010

Я понимаю, что вы хотите сделать это декларативно, но это кажется немного сложным для вашей ситуации.

Можете ли вы

  1. Использовать свойство обтекания, скажем, MatchesUpdated, и связать его с ItemsSource?

  2. Уведомить свойство MatchesUpdated об изменении NumMatches или MatchesChanged?

  3. Предоставить параметр преобразователя для привязки: {Binding Path = 'TheViewModelItSelf', RelativeSource = '{RelativeSource blahblahblah}'}

  4. Используйте конвертер, внутри которого он преобразует параметр ConverterParameter в ViewModelObject, затем оценивает свойство NumMatches и, следовательно, свойство Matches.

0 голосов
/ 24 ноября 2010

Мне кажется, что ваша логика неверна, так как NumMatches должен представлять количество совпадений, а это не всегда так (когда возникает событие). Исходя из этого, я предполагаю, что у вас есть что-то вроде этого в коде:

private Trie<int> _Matches
public Trie<int> Matches
{
    get { return Matches; }
    set
    {
        _Matches = value;
        NotifyPropertyChanged("Matches");
        UpdateNumMatches();
    }
}

private int _NumMatches
public int NumMatches
{
    get { return NumMatches; }
    set
    {
        _NumMatches = value;
        NotifyPropertyChanged("NumMatches");
    }
}

private void UpdateNumMatches()
{
    NumMatches = 5;  //Whatever calculates the number of matches
}

Если вы переключите UpdateNumMatches(); так, чтобы он находился выше вызова PropertyChanged, вам следует решить проблему. Э.Г.

private Trie<int> _Matches
public Trie<int> Matches
{
    get { return Matches; }
    set
    {
        _Matches = value;
        UpdateNumMatches();
        NotifyPropertyChanged("Matches");
    }
}

В общем, помните, что вызов события вызывает внешний код, который, вероятно, будет работать с вашим объектом, поэтому перед тем, как сделать это, вам необходимо убедиться, что ваш объект находится в действительном состоянии.

0 голосов
/ 19 ноября 2010

Во-первых, вы можете использовать конвертер PagingMultiValue только для внутренней привязки, если хотите

<Binding Path="NumMatches" Converter="mine:PagingMultiValue"></Binding>

Во-вторых, вам нужно реализовать INotifyPropertyChanged в классе-оболочке, к которому вы привязываете, чтобы обновленные значения обновлялись в пользовательском интерфейсе

вот пример

public class Wrapper : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int numMatches;
    public int NumMatches
    {
        get { return numMatches; }
        set
        {
            numMatches = value;
            if (PropertyChanged != null)
                PropertyChanged(this, 
                    new PropertyChangedEventArgs("NumMatches"));
        }
    }
}

Надеюсь, что это поможет, или даст вам направление для дальнейшего расследования

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...