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

mmr*_*mmr 11 .net wpf fullscreen flicker

我有一个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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:我在一些对话框上尝试了隐藏/显示的东西,它似乎并不重要.也许是因为主要的自助服务终端应用风格很重?

Ray*_*rns 15

闪烁的根本原因在于,无论何时你.hide()一个窗口都PresentationSource被断开,导致Unloaded事件被触发到所有内容,并且所有被缓存在MILCoreWPF层中的东西都要被丢弃.然后当你.Show()再次使用它时,一切都会被重建.

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

带有伪装TabControl的单一窗口

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

  1. 检查以前为此页面创建的TabItem(使用词典)
  2. 如果找不到TabItem,请将Page包装在新的TabItem中并将其添加到TabControl中
  3. 将TabControl切换到新的TabItem

您将用于TabControl的ContentTemplate非常简单:

<ContentTemplate TargetType="TabControl">
  <ContentPresenter x:Name="PART_SelectedContentHost"
                    ContentSource="SelectedContent" />
</ContentTemplate>
Run Code Online (Sandbox Code Playgroud)

使用带导航的框架

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

具有不透明度的多个窗口

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