Что вызывает игнорирование сенсорного ввода в моем приложении WPF после поворота планшета Samsung? - PullRequest
1 голос
/ 05 января 2012

У меня есть приложение WPF, которое я тестирую на планшете Samsung XE700T1A. У меня нет других планшетов для тестирования. После запуска приложения, когда я поворачиваю планшет в портретный режим, элементы пользовательского интерфейса перестают реагировать на прикосновения. Они продолжают игнорировать касание после повторного поворота в альбомную ориентацию, но затем, после третьего поворота назад в портретное положение, сенсор работает. Дальнейшие повороты, похоже, дают неопределенные результаты. Иногда касание работает, иногда вам нужно продолжать вращение, чтобы вернуть его. Я видел случайные изменения в этой исходной последовательности, но по большей части она выглядит очень последовательной.

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

MainWindow.xaml

<Window x:Class="RotationBug.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <TextBlock x:Name="DisplayText" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center"
                   Text="I'm happy." />

        <Button Grid.Row="1" 
                Height="22" 
                Width="80" 
                Margin="0,0,0,20" 
                Click="OnButtonPress"
                Content="Depress Me" />
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnButtonPress(object sender, RoutedEventArgs e)
    {
        DisplayText.Text = DateTime.UtcNow.Ticks.ToString();
    }
}

Это простое демонстрационное приложение также размещено здесь, на GitHub .

Кто-нибудь знает, что является причиной этой проблемы? Или как это решить? Кто-нибудь может воспроизвести эту проблему?

1 Ответ

2 голосов
/ 11 января 2012

Эта проблема, по-видимому, известна внутри корпорации Майкрософт, когда " Сенсорная система WPF не всегда правильно определяет изменения разрешения. "

Перефразируя комментарии, обходной путь заключается в том, чтобы подписаться на SystemEvents.DisplaySettingsChanged, а после небольшой задержки репостить WM_DISPLAYCHANGE в окне с заголовком «SystemResourceNotifyWindow» в текущем потоке.

Однако фактический код, размещенный там, не подходит ни для кого, кроме Microsoft. Код ниже будет работать для всех нас. Я разместил пример решения здесь, на GitHub .

class NativeRotationFix
{
    private readonly Window window;

    private const string MessageWindowTitle = "SystemResourceNotifyWindow";
    private const uint WM_DISPLAYCHANGE = 0x007E;
    private const int Delay = 500;

    private int width;
    private int height;
    private int depth;

    public delegate bool WNDENUMPROC(IntPtr hWnd, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern bool EnumThreadWindows(uint dwThreadId, WNDENUMPROC lpfn, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll")]
    public static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

    public NativeRotationFix(Window window)
    {
        this.window = window;
        SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged;
    }

    ~NativeRotationFix()
    {
        SystemEvents.DisplaySettingsChanged -= OnDisplaySettingsChanged;
    }

    private void OnDisplaySettingsChanged(object sender, EventArgs e)
    {
        new DispatcherTimer(TimeSpan.FromMilliseconds(Delay), DispatcherPriority.Normal, (s, a) =>
        {
            WindowInteropHelper interopHelper = new WindowInteropHelper(window);

            Screen screen = Screen.FromHandle(interopHelper.Handle);
            width = screen.Bounds.Width;
            height = screen.Bounds.Height;
            depth = screen.BitsPerPixel;

            uint threadId = GetCurrentThreadId();
            EnumThreadWindows(threadId, PostToNotifyWindow, IntPtr.Zero);

            (s as DispatcherTimer).Stop();

        }, Dispatcher.CurrentDispatcher);
    }

    private bool PostToNotifyWindow(IntPtr hwnd, IntPtr lparam)
    {
        StringBuilder buffer = new StringBuilder(MessageWindowTitle.Length + 1);

        if (GetWindowText(hwnd, buffer, buffer.Capacity) <= 0) return true;
        if (buffer.ToString() != MessageWindowTitle) return true;

        PostMessage(hwnd, WM_DISPLAYCHANGE, new IntPtr(depth), new IntPtr(MakeLong(width, height)));
        return false;
    }

    private static int MakeLong(int low, int high)
    {
        return (int)((ushort)low | (uint)high << 16);
    }
}
...