【问题标题】:Why is the visibility of a WPF Grid delayed when a TextBox has focus?为什么当 TextBox 具有焦点时 WPF Grid 的可见性会延迟?
【发布时间】:2011-10-11 23:40:46
【问题描述】:

在以下 XAML 中,editPanel 始终可见。仅当通过按 F5 键启动长时间操作时,overlayGrid 才可见。编辑面板变灰的视觉效果会发生漫长的过程。

<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

为什么当 TextBox 有焦点时,overlayGrid 的实际可见性会延迟?我该如何解决这个问题?

【问题讨论】:

    标签: wpf vb.net grid visibility


    【解决方案1】:

    AFAIK,即使在 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
    

    【讨论】:

    • 您的解决方案确实有效。但是,在我的“真实”程序中,我有一个绑定到 ObservableCollections 的多个列表框,这些列表框在长时间操作期间会更新。当然,由于后台线程,这会引发异常。因此我尝试使用单线程。
    • @Tim,你不应该在 UI 线程上做任何长时间的操作,因为那会阻塞它。每次修改集合时都使用Dispatcher.Invoke(),或者缓存更改,然后在操作完成后一次性进行所有修改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    相关资源
    最近更新 更多