如何避免WPF全屏应用中的闪烁?

时间:2010-03-02 15:37:06

标签: .net wpf flicker fullscreen

我有一个WPF应用程序,它是一个全屏自助服务终端应用程序。它实际上是一个非常复杂的应用程序,但这里有一些显示基本概念的代码。基本上,每当用户从一个屏幕进入下一个屏幕时,就会出现一些严重的闪烁,从而打开新窗口。在严重的情况下,桌面会在新屏幕显示之前显示几秒钟。在这个示例代码中没有发生这种情况,因为它非常简单,但添加了一些按钮和样式,你会看到它。

App.xaml.cs:

public partial class App : Application {
    Manager mManager;
    public App() {
        mManager = new Manager();
        Window1 screen1 = new Window1(mManager);
        mManager.Screen1 = screen1;
        try {
            this.Run(screen1);
        } catch (Exception e) {
            System.Console.WriteLine(e.ToString());                
        } finally {
            Application.Current.Shutdown();
        }
    }
}

Window1.xaml.cs:

public partial class Window1 : Window {
    Manager Manager{get; set;}
    public Window1(Manager inManager) {
        InitializeComponent();
        Manager = inManager;
    }

    private void OnChangeScreen(object sender, RoutedEventArgs e) {
        Manager.OpenScreen2();
    }
}

Window2.xaml.cs:

public partial class Window2 : Window {
    Manager Manager{get; set;}
    public Window2(Manager inManager) {
        InitializeComponent();
        Manager = inManager;
    }

    private void OnChangeScreen(object sender, RoutedEventArgs e) {
        Manager.OpenScreen1();
    }
}

Manager.cs:

public class Manager {
    public Window1 Screen1{ get; set;}
    public Window2 Screen2{ get; set;}

    public Manager(){
        Screen1 = new Window1(this);
    }

    public void OpenScreen2() {
        Screen2 = new Window2(this);
        Screen2.Show();
        if (Screen1 != null) {
            Screen1.Hide();
        }
    }

    public void OpenScreen1() {
        Screen1 = new Window1(this);
        Screen1.Show();
        if (Screen2 != null) {
            Screen2.Hide();
        }
    }
}

Window1.xaml(基本上由window2.xaml模仿):

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" 
        WindowStyle="None"
        WindowState="Maximized"
        Width="1280"
        Height="1024"
        FontFamily="Global User Interface"
        ResizeMode="NoResize">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Button Name="ChangeScreenButton" Click="OnChangeScreen" Grid.Row="2" Grid.Column="2" Content="Toggle Screen 2"></Button>
    </Grid>
</Window>

交错显示两个窗口(即,在删除窗口2之前显示窗口1等)不会改变闪烁行为。在这个简单的应用程序中,可以隐藏未显示的其他屏幕,但在更复杂的应用程序中,有太多的状态信息可以正确,轻松地管理屏幕信息。

是否有一些神奇的代码字或技术可以避免闪烁,这种闪烁可以在这个简单的应用程序中运行,也可以扩展到更复杂的应用程序?我担心此时我会被迫重写整个用户界面以支持隐藏和显示,而这在我的时间框架内是不可行的。

编辑:我在某些对话框上尝试过隐藏/显示内容,这似乎并不重要。也许是因为主要的自助服务终端应用风格很重?

8 个答案:

答案 0 :(得分:15)

闪烁的根本原因是无论何时你.hide()一个窗口,PresentationSource被断开,导致Unloaded事件被触发到MILCore缓存的所有内容和所有内容要丢弃的WPF层。然后当你再次.Show()时,一切都会重建。

要防止闪烁,请确保始终将UI连接到PresentationSource。这可以通过以下几种方式完成:

带有伪装TabControl的单一窗口

使用包含TabControl样式的单个窗口,以便您无法看到标签。通常显示或隐藏窗口时,在代码中切换选项卡。您可以使用“Page”在现有代码中搜索并替换“Window”,然后将“Show()”调用替换为您执行以下操作的自定义“Show()”:

  1. 检查此页面的先前创建的TabItem(使用词典)
  2. 如果找不到TabItem,请将Page包装在新的TabItem中并将其添加到TabControl
  3. 将TabControl切换到新的TabItem
  4. 您将用于TabControl的ContentTemplate非常简单:

    <ContentTemplate TargetType="TabControl">
      <ContentPresenter x:Name="PART_SelectedContentHost"
                        ContentSource="SelectedContent" />
    </ContentTemplate>
    

    使用带导航的框架

    Frame与导航一起使用对于自助服务终端来说是一个非常好的解决方案,因为它实现了大量的页面切换和其他功能。但是,以这种方式更新现有应用程序可能比使用TabControl更多工作。在任何一种情况下,您都需要从Window转换为Page,但使用Frame时您还需要处理导航。

    具有不透明度的多个窗口

    您可以使用低不透明度使窗口几乎完全不可见,但WPF仍然会保留可视树。这将是一个微不足道的变化:只需将对Window.Show()Window.Hide()的所有调用替换为调用“MyHide()”和“MyShow()”来更新不透明度。请注意,您可以通过让这些例程触发非常短持续时间(例如0.2秒)的动画来进一步改善这一点,从而为不透明度设置动画。由于两个动画将同时设置,动画将顺利进行,效果会很好。

答案 1 :(得分:2)

我很好奇为什么你在Kiosk中为同一个应用程序使用多个窗口。您可以轻松地将所有控件放在同一个“窗口”上,只需更改面板上的可见性即可显示不同的“屏幕”。这肯定会阻止桌面显示,并允许你做淡化过渡或滑动动画等整洁的事情。

答案 2 :(得分:1)

WPF内置了导航功能。

只需查看Frame和您可以使用VS或Blend轻松设计的Page类。

答案 3 :(得分:0)

同意有关使用内置导航功能的评论,但如果您此时已锁定设计,或许可以考虑动画窗口的不透明度?不透明度的短100或200毫秒动画,从1 - > 0表示传出窗口,0表示&gt;传入窗口的1可能会解决问题。处理故事板上Completed事件中的传出窗口的实际清理。

答案 4 :(得分:0)

看看WPF如何使用DirectX和图形处理器来卸载屏幕元素的处理,计算机的DirectX和驱动程序是最新的吗?

科里

答案 5 :(得分:0)

如果构造函数中有任何初始化需要很长时间才能导致延迟和闪烁。您可以尝试使用异步方法或将该初始化放在后台线程上,以便它不会阻止显示窗口。

可能导致延迟的一个例子是数据库查询或网络上的数据请求。

快速实验将在慢窗口中禁用部分构造函数,以找出导致显示窗口延迟的原因。

答案 6 :(得分:0)

如前所述,使用Frames / Tab Controls可以避免过渡期间的闪烁

如果您不想更改应用程序并希望在Windows7或WindowsVista上删除闪烁(桌面之间闪烁),则可以将Windows的“视觉效果”设置优化为'Adjust for best performance'

答案 7 :(得分:0)

这是一个简单的替代方案,适用于我的自助式应用程序,黑色背景,灵感来自上述答案。在这里,我有一个“LanguageWindow”,可以从应用程序的任何地方打开,以更改当前语言。

在LanguageWindow.xaml中(检查WindowState = Minimized):

<Window x:Class="LanguageWindow"
    ...
    Title="LanguageWindow" Height="1024" Width="1280" WindowStyle="None" WindowState="Minimized" Background="Black">

在LanguageWindow.xaml.vb中:

Private Sub LanguageWindow_ContentRendered(sender As Object, e As EventArgs) Handles Me.ContentRendered
    Me.WindowState = WindowState.Maximized
End Sub

瞧!

(使用Visual Studio 2015,.NET Framework 4.6,WPF,VB.net完成)