Как избежать мерцания в полноэкранном приложении WPF? - PullRequest
11 голосов
/ 02 марта 2010

У меня есть приложение 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 и т. Д.) Не меняет поведение мерцания. В этом простом приложении можно было бы просто скрыть другие не показанные экраны, но в более сложном приложении слишком много информации о состоянии, чтобы правильно и легко управлять информацией на экране.

Существует ли какое-нибудь волшебное кодовое слово или методика, позволяющая избежать мерцания, которое будет работать в этом простом приложении, которое также масштабируется до более сложного приложения? Я боюсь, что на этом этапе меня заставят переписать весь пользовательский интерфейс, чтобы поддерживать скрытие и отображение, а это просто невозможно в мой период времени.

РЕДАКТИРОВАТЬ: Я пытался скрыть / показать вещь в некоторых диалогах, и это просто не имеет значения. Может быть, это из-за того, что главное приложение киоска - это стиль, тяжелый?

Ответы [ 8 ]

15 голосов
/ 02 марта 2010

Основная причина мерцания заключается в том, что всякий раз, когда вы .Hide () окно его PresentationSource отключается, вызывая события Unloaded для всего и все кэшированные в слое MILCore WPF отбрасываются , Затем, когда вы .Show() это снова позже, все перестраивается.

Чтобы предотвратить мерцание, убедитесь, что ваш интерфейс пользователя всегда подключен к PresentationSource. Это можно сделать несколькими способами:

Одно окно с замаскированным TabControl

Используйте одно окно, содержащее стиль TabControl, чтобы вы не могли видеть вкладки. Переключайте вкладки в коде, когда вы обычно показываете или скрываете окна. Вы можете просто найти и заменить «Window» в существующем коде на «Page», а затем заменить вызовы «Show ()» на пользовательский «Show ()», который выполняет следующие действия:

  1. Проверить ранее созданный TabItem для этой страницы (используя словарь)
  2. Если TabItem не найден, оберните страницу внутри нового TabItem и добавьте его в TabControl
  3. Переключите TabControl на новый TabItem

Шаблон ContentTemplate, который вы используете для TabControl, чрезвычайно прост:

<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 секунды), которые оживляют непрозрачность. Поскольку обе анимации будут установлены одновременно, анимация будет проходить плавно, и это будет аккуратный эффект.

2 голосов
/ 02 марта 2010

Мне любопытно, почему вы используете несколько окон для одного и того же приложения в киоске. Вы можете легко разместить все элементы управления в одном «окне» и просто изменить видимость на панелях, чтобы отображать разные «экраны». Это, безусловно, предотвратит отображение рабочего стола и позволит вам делать аккуратные вещи, такие как плавные переходы, скользящая анимация и т. Д.

1 голос
/ 02 марта 2010

WPF имеет встроенные функции навигации.

Достаточно взглянуть на Frame и классы Page, которые вы можете легко создать с помощью VS или Blend.

0 голосов
/ 01 февраля 2016

Вот простая альтернатива, которая работает для меня в моем приложении, похожем на киоск, с черным фоном, вдохновленным ответами сверху. Здесь у меня есть «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)

0 голосов
/ 17 июня 2011

Как уже говорилось ранее, используя Frames / Tab Controls, избегайте мерцания во время переходов

Если вы не хотите менять свое приложение и хотите удалить это мерцание (мигание рабочего стола между ними) в Windows7 или WindowsVista, вы можете оптимизировать настройку Windows «Визуальные эффекты», чтобы она составляла «Настроить для лучшей производительности». «

0 голосов
/ 02 марта 2010

Если у вас есть инициализация в конструкторе, которая занимает много времени, что может вызвать задержку и мерцание. Вы можете попробовать использовать асинхронный метод или поместить эту инициализацию в фоновый поток, чтобы он не блокировал отображение окна.

Примером того, что может вызвать задержку, может быть запрос к базе данных или запрос данных по сети.

Быстрый эксперимент состоял бы в том, чтобы отключить части конструктора в медленном окне, чтобы выяснить причину задержки при отображении окна.

0 голосов
/ 02 марта 2010

Видя, как WPF использует DirectX и графический процессор для разгрузки обработки элементов экрана, обновлены ли DirectX и драйверы компьютера?

Cory

0 голосов
/ 02 марта 2010

Согласны с комментариями по поводу использования встроенной функции навигации, но если вы на этом этапе заперты в своем дизайне, возможно, стоит подумать об анимации непрозрачности ваших окон? Короткая анимация непрозрачности 100 или 200 мс от 1 -> 0 для исходящего окна и 0 -> 1 для входящего окна может решить проблему. Обработайте фактическую очистку исходящего окна в событии Completed на раскадровке.

...