如何避免 WPF 全屏应用程序中出现闪烁?

发布于 2024-08-23 03:18:58 字数 3420 浏览 11 评论 0原文

我有一个 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>

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

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

编辑:我已经尝试过在某些对话框中隐藏/显示东西,但这似乎并不重要。也许是因为主要的信息亭应用程序风格很重?

I have a WPF application that is a fullscreen kiosk app. It's actually a pretty complicated app at this point, but here's some code that shows the basic idea. Essentially, whenever the user goes from one screen to the next, there's some serious flicker going on bringing up the new window. In severe cases, the desktop is displayed for a few seconds before the new screen shows up. That doesn't happen in this sample code, because it's so simple, but add a few more buttons and styles and you'll see it.

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 (essentially mimicked by 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>

Interleaving the displays of the two windows (ie, showing window 1 before deleting window 2, etc) does not change the flickering behavior. In this simple app, it would be possible to just hide the other screens that aren't shown, but in the more complicated app, there's just too much state information to manage screen information properly and easily.

Is there some magic codeword or technique to avoid flicker that would work in this simple app that also scales to the more complex app? I'm worried that I'll be forced to rewrite the entire UI at this point to support hiding and showing, and that's just not feasible in my timeframe.

EDIT: I've tried the hide/show thing on some dialogs, and it just doesn't seem to matter. Maybe it's because the main kiosk app is style heavy?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

吾家有女初长成 2024-08-30 03:18:58

闪烁的根本原因是,每当您 .Hide() 窗口时,其 PresentationSource 就会断开连接,从而导致 Unloaded 事件在所有内容以及缓存在 中的所有内容上触发。要丢弃的 WPF >MILCore 层。然后,当您稍后再次 .Show() 时,一切都会重建。

为了防止闪烁,请确保您的 UI 始终连接到PresentationSource。这可以通过多种方式完成:

带有伪装的 TabControl 的单个窗口

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

  1. 检查之前为此页面创建的 TabItem (使用字典)
  2. 如果没有找到 TabItem,则将页面包装在新的 TabItem 中并将其添加到 TabControl
  3. 将 TabControl 切换到新的 TabItem

用于 TabControl 的 ContentTemplate 非常简单:

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

使用带有导航的框架< /strong>

Frame 与导航结合使用对于信息亭来说是一个非常好的解决方案,因为它实现了许多页面切换和其他功能。然而,以这种方式更新现有应用程序可能比使用 TabControl 需要更多工作。无论哪种情况,您都需要从 Window 转换为 Page,但对于 Frame,您还需要处理导航。

具有不透明度的多个窗口

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

The underlying cause of the flicker is that whenever you .Hide() a window its PresentationSource is disconnected, causing Unloaded events to be fired on everything and everything cached in the MILCore layer of WPF to be discarded. Then when you .Show() it again later, everything is rebuilt.

To prevent flicker, make sure you keep your UI connected to a PresentationSource at all times. This can be done in several ways:

Single window with a disguised TabControl

Use a single window containing a TabControl styled so you can't see the tabs. Switch tabs in code when you would normally show or hide windows. You can simply search-and-replace "Window" in your existing code with "Page", then replace "Show()" calls to your custom "Show()" which does the following:

  1. Check for previously created TabItem for this Page (using a Dictionary)
  2. If no TabItem found, wrap the Page inside a new TabItem and add it to the TabControl
  3. Switch the TabControl to the new TabItem

The ContentTemplate you would use for your TabControl is extremely simple:

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

Using a Frame with Navigation

Using Frame with Navigation is a very good solution for a kiosk because it implements a lot of the page switching and other functionality. However it may be more work to update an existing application this way than to use a TabControl. In either case you need to convert from Window to Page, but with Frame you also need to deal with navigation.

Multiple windows with opacity

You can make a window almost completely invisible using a low opacity and yet WPF will still keep the visual tree around. This would be a trivial change: Just replace all calls to Window.Show() and Window.Hide() with calls to "MyHide()" and "MyShow()" which updates the opacity. Note that you can improve this further by having these routines trigger animations of very short duration (eg 0.2 second) that animate the opacity. Since both animations will be set at the same time the animation will proceed smoothly and it will be a neat effect.

蹲墙角沉默 2024-08-30 03:18:58

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

I'm curious as to why you are using multiple windows for the same application in a Kiosk. You could easily put all of the controls on the same "Window" and simply change visibility on Panels to display different "screens". This would certainly prevent the desktop from ever being shown, and would allow you to do neat things like fade transitions or sliding animations, etc.

非要怀念 2024-08-30 03:18:58

WPF 具有内置的导航功能。

只需查看 框架 和页面您可以使用 VS 或 Blend 轻松设计这些类。

WPF has built in navigation functionality.

Just look at the Frame and the Page classes which you can easily design using VS or Blend.

叫嚣ゝ 2024-08-30 03:18:58

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

科里

Seeing as how WPF uses DirectX and the Graphics Processor to offload processing of screen elements, are the computer's DirectX and drivers up to date?

Cory

肩上的翅膀 2024-08-30 03:18:58

这是一个简单的替代方案,适用于我的黑色背景的类似信息亭的应用程序,灵感来自上述答案。这里我有一个“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 完成)

Here is an easy alternative that works for me in my kiosk-like application with a black background, inspired from above answers. Here I have a "LanguageWindow" that can be opened from anywhere in the application to change the current language.

In LanguageWindow.xaml (check the WindowState=Minimized):

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

In LanguageWindow.xaml.vb:

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

Voilà!

(done with Visual Studio 2015, .NET Framework 4.6, WPF, VB.net)

幸福还没到 2024-08-30 03:18:58

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

Agreed with the comments about using the built-in navigation functionality, but if you're locked in to your design at this point, perhaps consider animating the opacity of your windows? A short 100 or 200 ms animation of Opacity from 1 -> 0 for the outgoing window and 0 -> 1 for the incoming window might resolve the issue. Handle the actual cleanup of the outgoing window in the Completed event on the storyboard.

べ繥欢鉨o。 2024-08-30 03:18:58

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

会导致延迟的示例是数据库查询或通过网络请求数据。

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

If you have any initialization in the constructor that takes a long time that could cause a delay and flicker. You could try using an asynchronous method or put that initialization on a background thread so that it does not block the showing of the window.

An example of something which would cause a delay would be a database query or a request for data over a network.

A quick experiment would be to disable parts of the constructor in a slow Window to find out what is causing delay in showing the Window.

趁微风不噪 2024-08-30 03:18:58

正如前面所回答的,使用框架/选项卡控件避免转换过程中的闪烁

如果您不想更改应用程序并希望消除 Windows7 或 WindowsVista 上的闪烁(桌面之间的闪烁),您可以将 Windows“视觉效果”设置优化为“调整以获得最佳性能”

As answered earlier using Frames/Tab Controls avoid flicker during transitions

If you don't want to change your application and want to remove that flicker (flashing of desktop in between) on Windows7 or WindowsVista you can optimise your windows 'Visual Effects' setting to be 'Adjust for best performance'

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文