4

I'm writing a WPF theme that will be used on a touch device. I also want this theme to provide normal behaviors when when used on a device with a mouse.

The issue I've run into is that the trigger for "IsMouseOver" is triggered and stays on when the control is touched. My research shows this is a known issue in that IsMouseOver gets set to true when a control is touched but not unset. Solutions I've seen for this problem are to remove the "IsMouseOver" trigger from the styling. I'm not thrill with this approach as mouse hover visual cues are lost by removing the IsMouseOver trigger.

<ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver" Value="true">
        <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
        <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource MouseOver.Border}"/>
     </Trigger>
</ControlTemplate.Triggers>

Also setting "Stylus.IsPressAndHoldEnabled" to false is not the solution to this problem.

<Setter Property="Stylus.IsPressAndHoldEnabled" Value="False"/>

Suggestions?

1 Answers1

1

I suggest that you look into adding a DataTrigger property that is able to detect if the user is on a touch device or not. Something like this simple bool or this.

Then, you can apply a MultiDataTrigger to only show the highlight if they are on a non-touch enabled device (i.e. mouse-driven). Here's a small reproducible sample.

MainWindow.xaml

<Grid>
    <StackPanel>
        <TextBlock Text="{Binding TouchStatus}" Margin="5" HorizontalAlignment="Center"/>
        <Button Margin="5" Content="Push" HorizontalAlignment="Center" Padding="5,15" BorderThickness="3">
            <Button.Style>
                <Style TargetType="Button">
                    <Setter Property="BorderBrush" Value="Blue"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="Button">
                                <Border x:Name="border" 
                                        BorderBrush="{TemplateBinding BorderBrush}" 
                                        BorderThickness="{TemplateBinding BorderThickness}" 
                                        Background="{TemplateBinding Background}" 
                                        Padding="{TemplateBinding Padding}" 
                                        HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
                                    <ContentPresenter/>
                                </Border>
                                <ControlTemplate.Triggers>
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self},Path=IsMouseOver}" Value="True"/>
                                            <Condition Binding="{Binding TouchEnabled}" Value="False"/>
                                        </MultiDataTrigger.Conditions>
                                        <Setter Property="BorderBrush" TargetName="border" Value="Yellow"/>
                                    </MultiDataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Button.Style>
        </Button>
    </StackPanel>
</Grid>

MainWindow.xaml.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        UpdateTouchStatus();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string touchStatus;
    private bool touchEnabled;
    public string TouchStatus { get => this.touchStatus; set { this.touchStatus = value; OnPropertyChanged(); } }
    public bool TouchEnabled { get => this.touchEnabled; set { this.touchEnabled = value; OnPropertyChanged(); } }

    private void UpdateTouchStatus()
    {
        if (!HasTouchInput())
        {
            this.TouchEnabled = true;
            this.TouchStatus = "This is a touch enabled device";
        }
        else
        {
            this.TouchEnabled = false;
            this.TouchStatus = "This is NOT a touch enabled device";
        }
    }

    public bool HasTouchInput()
    {
        foreach (TabletDevice tabletDevice in Tablet.TabletDevices)
        {
            //Only detect if it is a touch Screen not how many touches (i.e. Single touch or Multi-touch)
            if (tabletDevice.Type == TabletDeviceType.Touch)
                return true;
        }

        return false;
    }
}
Tam Bui
  • 2,940
  • 2
  • 18
  • 27
  • Better than outright loosing mouse over . Still not optimal. This requires a TouchEnabled property to be defined either in a subclassed control or the parent . – Eddie Wyatt Dec 15 '20 at 16:53
  • This is not a solution for the devices supporting touch and mouse, simultaneously. Such devices existed before the end of 2020. – Delbert Nov 11 '22 at 21:46