5

I'm developing a C# WPF application that is intended to run on full Windows 10 tablets using exclusively touch. So far, the app works great, except for one of my dialog windows has buttons that don't like to be touched.

Dialog Window XAML:

<Window x:Class="Commencement_Organizer.ConfirmStudentWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Commencement_Organizer"
    mc:Ignorable="d"
    Title="Confirmation" Topmost="True" Height="372.677" Width="578.225" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" 
    WindowStyle="None" Background="White" AllowsTransparency="True" Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">

<Window.Triggers>
    <EventTrigger RoutedEvent="Window.Loaded">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation Duration="00:00:0.2" Storyboard.TargetProperty="Opacity" From="0" To="1" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</Window.Triggers>

<Grid Background="#FF171717">
    <Grid Margin="1" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Button x:Name="YesButton" Content="Yes" Margin="25" Grid.Row="2" Click="YesButton_Click"/>
        <Button x:Name="NoButton" Content="No" Margin="25" Grid.Row="2" Grid.Column="1" Click="NoButton_Click"/>
        <Label x:Name="label" Content="Confirm your name" Margin="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="24"/>
        <Label x:Name="nameLabel" Content="Label" HorizontalAlignment="Center" Margin="0" Grid.Row="1" VerticalAlignment="Center" Grid.ColumnSpan="2" FontSize="24" FontWeight="Bold"/>

    </Grid>
</Grid>

Implementation (C#):

ConfirmStudentWindow confirmWindow = new ConfirmStudentWindow(student);
        confirmWindow.Confirm += OnConfirm;

        // This window makes everything behind the dialog window a grey tint, which makes the dialog more prominent
        var darkwindow = new Window() 
        {
            Background = Brushes.Black,
            Opacity = 0.4,
            AllowsTransparency = true,
            WindowStyle = WindowStyle.None,
            WindowState = WindowState.Maximized,
            Topmost = true,
            Effect = new BlurEffect()
        };
        darkwindow.Show(); // Show grey background tint
        confirmWindow.ShowDialog(); // Stops main UI thread
        darkwindow.Close();

Everything about this works as intended, except when I try to use a touchscreen to use those buttons, they just stay highlighted, but don't actually click. Works perfect with a mouse.

Tested on 2 different Windows 10 Devices (Surface Pro 2 and Surface Book).

To put it in the form of a question:

Why does launching this window as a Dialog make it resistant to touch, but not if it's launched as a regular window?

Edit - New Question:

Is there any way to simulate a Dialog window other than launching a regular window that always stays on top and then put a tinted overlay behind it while giving the window an eventhandler that provides the result of the user input?

Clay07g
  • 1,105
  • 7
  • 23
  • Update: This bug only occurs when the window is displayed using "ShowDialog()". – Clay07g Sep 27 '16 at 01:36
  • Without investigating your code i remember that there has been a bug regarding touch support in .NET. Anyways it should be fixed with recent versions of .NET. See https://connect.microsoft.com/VisualStudio/feedback/details/903760/wpf-touch-services-are-badly-broken and may give the posted hotfix a try. – BudBrot May 20 '17 at 10:40
  • I will try that. WPF seems perfectly fine for touch unless it's a Dialog window. – Clay07g May 20 '17 at 15:53
  • 1
    Personally, I would avoid using Dialog or any modal windows. I would simply show it and have it overlay your main view when shown. Set the background to a color with nearly 0 opacity but not quite and you won't be able to click through it; but it will still look over laying the view like you've intended. There are other techniques also but this works. – Michael Puckett II May 21 '17 at 14:26
  • That's the solution I ended up implementing, but it feels very bloated compared to a Dialog, especially since it required an EventHandler to simulate the Dialog return. – Clay07g May 22 '17 at 00:26
  • I had problems with WPF ShowDialog in touch devices see muy question https://stackoverflow.com/questions/38642479/how-to-disable-wpf-tablet-support-in-surface-4-pro NET 4.7 promises these problems are solved for WPF and touch devices https://blogs.msdn.microsoft.com/dotnet/2017/04/05/announcing-the-net-framework-4-7/ – SERWare May 24 '17 at 14:46

2 Answers2

2

Comparing two identical .Show() methods, one being executed from a ViewModel and the other executed in code-behind, I found that the code-behind instance did not detect touches.

My solution was to invoke the method on the main UI thread from code-behind:

Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() =>
{
     window.Show()
}));

Beware that if you expect a result from the dialog then you will need to include it within the `Dispatched.BeginInvoke' method, otherwise it will not work correctly.

Ideally, grouping all relevant logics within would be the best approach.

Jack
  • 886
  • 7
  • 27
  • This is the best workaround I have found, so thank you! But I have found some conditions where it fails and it does make things looks strange specially if you have data going on. I have reported the bug to Microsoft so please upvote if you'd also want them to fix it: https://developercommunity.visualstudio.com/content/problem/890778/touch-bugissue-with-showdialog-and-textbox.html – Luishg Jan 20 '20 at 17:43
1

I tried your example and it is running without any problem at my end. I am using DELL notebook with touch display with W10 and .NET 4.5.2. One thing that I can suggest you is to try to hook up StylusUp (or StylusDown that depends on your logic) event on buttons in dialog. I did encounter similar problems in the past when only click event was managed.

This worked for me, confirm method was called and dialog closed.

ConfirmStudentWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Input;

namespace DialogTouchTest
{
    /// <summary>
    /// Interaction logic for ConfirmStudentWindow.xaml
    /// </summary>
    public partial class ConfirmStudentWindow : Window
    {
        public Action Confirm;

        public ConfirmStudentWindow()
        {
            InitializeComponent();
        }

        private void YesButton_Click(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
            Yes();
        }

        private void NoButton_Click(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
            No();
        }

        private void YesButton_StylusUp(object sender, StylusEventArgs e)
        {
            e.Handled = true;
            Yes();
        }

        private void NoButton_StylusUp(object sender, StylusEventArgs e)
        {
            e.Handled = true;
            No();
        }

        private void Yes()
        {
            DialogResult = true;
            Confirm();
        }

        private void No()
        {
            DialogResult = false;
        }
    }
}

ConfirmStudentWindow.xaml

<Window x:Class="DialogTouchTest.ConfirmStudentWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Confirmation" Topmost="True" Height="372.677" Width="578.225" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" 
    WindowStyle="None" Background="White" AllowsTransparency="True" Stylus.IsTapFeedbackEnabled="False" Stylus.IsTouchFeedbackEnabled="False">

    <Window.Triggers>
        <EventTrigger RoutedEvent="Window.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Duration="00:00:0.2" Storyboard.TargetProperty="Opacity" From="0" To="1" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Window.Triggers>

    <Grid Background="#FF171717">
        <Grid Margin="1" Background="White">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <Button x:Name="YesButton" Content="Yes" Margin="25" Grid.Row="2" StylusUp="YesButton_StylusUp" Click="YesButton_Click"/>
            <Button x:Name="NoButton" Content="No" Margin="25" Grid.Row="2" Grid.Column="1" StylusUp="NoButton_StylusUp" Click="NoButton_Click"/>
            <Label x:Name="label" Content="Confirm your name" Margin="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="24"/>
            <Label x:Name="nameLabel" Content="Label" HorizontalAlignment="Center" Margin="0" Grid.Row="1" VerticalAlignment="Center" Grid.ColumnSpan="2" FontSize="24" FontWeight="Bold"/>

        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;

namespace DialogTouchTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            ConfirmStudentWindow confirmWindow = new ConfirmStudentWindow();
            confirmWindow.Confirm += OnConfirm;

            // This window makes everything behind the dialog window a grey tint, which makes the dialog more prominent
            var darkwindow = new Window()
            {
                Background = Brushes.Black,
                Opacity = 0.4,
                AllowsTransparency = true,
                WindowStyle = WindowStyle.None,
                WindowState = WindowState.Maximized,
                Topmost = true,
                Effect = new BlurEffect()
            };
            darkwindow.Show(); // Show grey background tint
            confirmWindow.ShowDialog(); // Stops main UI thread
            darkwindow.Close();
        }

        private void OnConfirm()
        {

        }
    }
}
benderto
  • 896
  • 11
  • 39
  • I will do a test and let you know. It's possible all I needed to do was update my .NET Framework. Thank you for putting my code into an isolated example. +1 for that. I'll give you the bounty if I can replicate these results. – Clay07g May 24 '17 at 20:27
  • This program seems to work fine for me, as well. Funny side effect, though, is that touching above the buttons activates them. I'm sure I can figure it out, just not enough time right now. This answer does help a lot, though. Thank you. – Clay07g May 25 '17 at 22:32