为什么当文本框有焦点时,WPF网格的可见性会延迟



在下面的XAML中,editPanel总是可见的。overlayGrid只有在按下F5键开始长时间操作时才可见。在漫长的过程中,editPanel的视觉效果是变灰的。

<Window.InputBindings>
    <KeyBinding Key="F5" Command="{Binding Path=RefreshCommand}"/>
</Window.InputBindings>
<Grid>
    <StackPanel Name="editPanel">
        <TextBox>set focus here to see the problem.</TextBox>
        <CheckBox>set focus here to remove the problem.</CheckBox>
        <TextBlock Text="{Binding Path=Worker.Message}"/>
    </StackPanel>
    <Grid Name="overlayGrid" 
          Visibility="{Binding Path=Worker.Visibility}"
          Background="Gray" Opacity="0.5">
        <TextBox Text="{Binding Path=Worker.Message}" FontWeight="Bold" 
                 HorizontalAlignment="Center" VerticalAlignment="Center" 
                 />
    </Grid>
</Grid>

overlayGrid完全按照预期显示,除非TextBox有焦点。如果TextBox具有焦点,则在您看到overlayGrid的快速闪烁之前进行长时间操作。就好像代码是:做长操作,显示overlayGrid,折叠overlayGrid。

执行长操作并更改overlayGrid可见性的ViewModel代码如下:

Sub Refresh()
    Me.Worker.Message = String.Format("Refresh started at {0}..", 
                                      Date.Now.ToString("hh:mm:ss.fff")
    )
    Me.Worker.Visibility = Visibility.Visible
    ' Give the UI a chance to update itself.
    System.Windows.Forms.Application.DoEvents()
    Console.WriteLine("Debug: " + Me.Worker.Message)
    ' Fake the long operation.
    System.Threading.Thread.Sleep(1000)
    Me.Worker.Message = String.Format("Refresh completed at {0}.", 
                                      Date.Now.ToString("hh:mm:ss.fff")
    )
    Me.Worker.Visibility = Visibility.Collapsed
    Console.WriteLine("Debug: " + Me.Worker.Message)
End Sub

为什么当文本框有焦点时,overlayGrid的实际可见性延迟?如何解决这个问题?

当然,即使在WinForms中也不鼓励使用System.Windows.Forms.Application.DoEvents()。你当然不应该在WPF中使用它,即使它工作(显然,它不起作用)。

你应该做的是在后台线程上运行长时间的操作,然后使用Dispatcher.Invoke()在UI上设置结果。比如:

Sub Refresh()
    Me.Worker.Message = String.Format("Refresh started at {0}..",
                                      Date.Now.ToString("hh:mm:ss.fff")
    )
    Me.Worker.Visibility = Visibility.Visible
    Console.WriteLine("Debug: " + Me.Worker.Message)
    Task.Factory.StartNew(
        Function()
            ' Fake the long operation.
            System.Threading.Thread.Sleep(10000)
            Dispatcher.Invoke(
                Function()
                    Me.Worker.Message = String.Format("Refresh completed at {0}.",
                                                      Date.Now.ToString("hh:mm:ss.fff")
                    )
                    Me.Worker.Visibility = Visibility.Collapsed
                    Console.WriteLine("Debug: " + Me.Worker.Message)
                End Function)
        End Function)
End Sub

最新更新