Как я могу избежать мерцания в полноэкранном приложении WPF?

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

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

EDIT: я пробовал скрывать/показывать что-то в некоторых диалогах, и это, похоже, не имеет значения. Может быть, потому, что основное приложение для киоска отличается тяжелым стилем?

Ответы

Ответ 1

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

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

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

Используйте одно окно, содержащее TabControl, чтобы вы не могли видеть вкладки. Переключайте вкладки в коде, когда вы обычно показываете или скрываете окна. Вы можете просто выполнить поиск и замену "Окно" в существующем коде с помощью "Страница", а затем заменить вызовы "Показать()" на свой пользовательский "Показать()", который выполняет следующие действия:

  • Проверить ранее созданный TabItem для этой страницы (используя словарь)
  • Если TabItem не найден, оберните страницу внутри нового TabItem и добавьте ее в TabControl
  • Переключите 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

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

Ответ 3

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

Просто посмотрите Frame и классы страницы, которые вы можете легко проектировать с помощью VS или Blend.

Ответ 4

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

Ответ 5

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

Кори

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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