通过多重绑定绑定两个属性

时间:2010-11-16 23:27:04

标签: .net wpf data-binding

我正在尝试将一些WPF控件绑定到trie。我创建了一个包装器,它将trie中的方法公开为属性。我试图用trie返回的匹配过滤我的列表框。当匹配太多时,我不想枚举所有匹配,因为这需要几秒钟,而且完全没必要。我已经通过使用MultiBinding以声明方式解决了这个问题,MultiBinding既可以使用延迟加载匹配,也可以使用NumMatches,它可以更快地计算。 MultiBindingConverter首先评估发送到函数中的NumMatches。如果这小于某个值,那么它才会尝试评估Matches属性。

问题是当匹配发生变化时,NumMatches也会发生变化。因此,我的转换器被调用两次,一次是在匹配改变时,一次是在NumMatches改变时。如果WPF没有(显然)缓存数据,这可能会很好,即使可能有点容易出错,因为即使这两个属性在当时都发生了变化,也不会获取新值,而是使用缓存值。将使用不匹配的参数调用转换器,count将是例如100(可以安全枚举),但枚举将导致访问一百或两个值,因为在调用匹配的PropertyChanged之前不会获取新的匹配列表< strong>(它们在相同的包装器属性中更新,请记住)我应该如何解决这个问题? 我想以声明方式解决它。

我的想法如下:

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>

更新

以下是我的一些TrieWrapper:

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

            }
        }

即使我控制了Trie数据结构的源代码,我也不希望通过属性公开当前匹配来改变它。这就是为什么我有这个“黑客”而不是。调用OnMatchesChanged()时,将使用NumMatches的旧值更新它。然后直接使用正确的值更新两者。如果我改变更新的顺序,唯一改变的是当发生错误的枚举时,无论是我添加到文本搜索字符串还是从中删除。

谢谢!

4 个答案:

答案 0 :(得分:0)

首先,如果您愿意,可以在内部绑定上使用PagingMultiValue转换器

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

其次,您需要在绑定的包装类上实现INotifyPropertyChanged,以便在UI中刷新更改的值

这是一个例子

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"));
        }
    }
}

希望有所帮助,或为您提供进一步调查的指导

答案 1 :(得分:0)

我知道你想要宣传,但对你的情况来说似乎有点复杂。

你能

吗?
  1. 使用wrap属性,比如MatchesUpdated,并将其绑定到ItemsSource?

  2. 在更改的NumMatches或MatchesChanged上通知MatchesUpdated属性?

  3. 为绑定提供转换器参数,即{Binding Path ='TheViewModelItSelf',RelativeSource ='{RelativeSource blahblahblah}'}

  4. 使用转换器,在转换器中,它将ConverterParameter转换为ViewModelObject,然后评估NumMatches属性,从而评估匹配属性。

答案 2 :(得分:0)

在我看来,你的逻辑是错误的,因为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调用之上,则应解决问题。 E.g。

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

通常记住,引发事件会调用可能对您的对象进行操作的外部代码,因此您需要确保对象处于有效状态。

答案 3 :(得分:0)

除非您有充分的理由以另一种方式执行此操作,否则您应该使用ListBox内置UI虚拟化的事实,并按照以下文章的内容进行操作,以构建数据虚拟化。

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

(来自MSDN的UI虚拟化链接)

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

您当前的解决方案是一种数据虚拟化解决方案(正在进行延迟评估),但并不是以WPF可以轻松与其进行交互的方式完成的。通过使用自定义ICollectionView实现(文章派生ListCollectionView以方便),除了将ItemsSource设置为绑定到数据集的CollectionViewSource对象之外,您不必对ListBox执行任何操作。这使得XAML完全可读(与使用多重绑定等相比)并提高了性能,因为它不会使用你的转换器来计算出需要渲染的元素,WPF将使用它自己的方法来确定屏幕上应该是什么。