我正在尝试重构一些WPF代码并发现一个奇怪的代码序列。你能帮我理解一下它的含义吗?
我在自定义VirtualizingPanel中。在MeasureOverride内部,DispatcherTimer用于推迟虚拟化所有不再显示的项目的调用(请参阅here为什么可以这样做)。
使用DispatcherTimer来推迟对UI线程的一些调用对我来说似乎是一个奇怪的选择。这是我不理解的第一个事情,但考虑到其余的代码,开发人员可能已经选择了它,因为他熟悉这个构造。我想将其更改为一个简单的Dispatcher.BeginInvoke调用。
无论如何,第二个奇怪的是应用了锁定。我假设这样做是为了安全访问在MeasureOverride和CleanUpItems锁中的代码之间共享的实例字段。但这对我来说没有意义,因为无论如何一切都在UI线程上执行,对吧?
如果这是真的,这个代码实际上可能会引入死锁,以防调度程序在锁内停止一次执行并继续执行需要锁的调度程序队列上的另一个任务,对吧?或者WPF是否保证调度程序队列中的任务不会因队列中的其他任务而中断?
如果锁定的原因是其他原因,所有这些想法可能都无关紧要。因此,如果您知道其他原因,请与我分享。最后,我想摆脱这个锁,没有任何负面后果。
PS:ScrollCanvas是在XAML中创建的。
public class ScrollCanvas : VirtualizingPanel, IScrollInfo
{
private readonly DispatcherTimer _idleTimer;
public ScrollCanvas()
{
_idleTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(5), DispatcherPriority.Normal, CleanUpItems, Dispatcher.CurrentDispatcher);
_idleTimer.Stop();
}
protected override Size MeasureOverride(Size availableSize)
{
lock (_idleTimer)
{
//Finding the items to realize
//Generating the children from the item
//realizing the children
//Measuring the children
//Some further updates to other instance fields defined in ScrollCanvas
//I left this out cause this sequence is more that 100 lines of code
if (!_idleTimer.IsEnabled)
{
_idleTimer.Start();
}
}
private void CleanUpItems(int minDesiredGenerated, int maxDesiredGenerated)
{
//This method uses same properties and resources as code in other lock that I left out for clarity
var children = InternalChildren;
var generator = ItemContainerGenerator;
for (var i = children.Count - 1; i >= 0; i--)
{
var childGeneratorPos = new GeneratorPosition(i, 0);
var itemIndex = generator.IndexFromGeneratorPosition(childGeneratorPos);
if (itemIndex < minDesiredGenerated || itemIndex > maxDesiredGenerated)
{
generator.Remove(childGeneratorPos, 1);
RemoveInternalChildRange(i, 1);
}
}
}
private void CleanUpItems(object sender, EventArgs eventArgs)
{
_idleTimer.Stop();
lock (_idleTimer)
{
CleanUpItems(_protectedInterval.First, _protectedInterval.Last);
}
}
}
<Style TargetType="local:PageViewer">
<Setter Property="Background"
Value="Transparent"/>
<Setter Property="VirtualizingStackPanel.IsVirtualizing"
Value="True" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<local:ScrollCanvas Background="{Binding Background, RelativeSource={RelativeSource AncestorType=local:PageViewer}}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>