我正在尝试将一些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的旧值更新它。然后直接使用正确的值更新两者。如果我改变更新的顺序,唯一改变的是当发生错误的枚举时,无论是我添加到文本搜索字符串还是从中删除。
谢谢!
答案 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)
我知道你想要宣传,但对你的情况来说似乎有点复杂。
你能
吗?使用wrap属性,比如MatchesUpdated,并将其绑定到ItemsSource?
在更改的NumMatches或MatchesChanged上通知MatchesUpdated属性?
为绑定提供转换器参数,即{Binding Path ='TheViewModelItSelf',RelativeSource ='{RelativeSource blahblahblah}'}
使用转换器,在转换器中,它将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虚拟化的事实,并按照以下文章的内容进行操作,以构建数据虚拟化。
(来自MSDN的UI虚拟化链接)
http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel.aspx
您当前的解决方案是一种数据虚拟化解决方案(正在进行延迟评估),但并不是以WPF可以轻松与其进行交互的方式完成的。通过使用自定义ICollectionView实现(文章派生ListCollectionView以方便),除了将ItemsSource设置为绑定到数据集的CollectionViewSource对象之外,您不必对ListBox执行任何操作。这使得XAML完全可读(与使用多重绑定等相比)并提高了性能,因为它不会使用你的转换器来计算出需要渲染的元素,WPF将使用它自己的方法来确定屏幕上应该是什么。