我正在尝试解决电容式触摸屏的灵敏度问题,如果用户手指太靠近屏幕表面,则会触发WPF按钮。
这个问题是许多用户最终用手指或手的一部分,而不是主触摸手指,靠近屏幕表面,这会导致触发不正确的按钮。
调整屏幕的灵敏度似乎没有什么区别,我想我可以尝试修改按钮按下事件,只有按下按钮超过一定时间才会触发点击。
任何人都可以解释我如何创建一个可调节按下的自定义按钮。触发Clicked事件之前的时间。
如果可能的话,或许您可以通过这样的自定义按钮包含一个非常简单的C#/ WPF应用程序。
修改
好的,所以我使用下面的代码创建了一个子类Button,根据@ kidshaw的回答,但我认为我必须缺少一些东西,因为除了默认的Click事件之外没有任何东西被调用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace AppName
{
public class TouchButton : Button
{
DoubleAnimationUsingKeyFrames _animation;
public static readonly DependencyProperty DelayElapsedProperty =
DependencyProperty.Register("DelayElapsed", typeof(double), typeof(TouchButton), new PropertyMetadata(0d));
public static readonly DependencyProperty DelayMillisecondsProperty =
DependencyProperty.Register("DelayMilliseconds", typeof(int), typeof(TouchButton), new PropertyMetadata(100));
public double DelayElapsed
{
get { return (double)this.GetValue(DelayElapsedProperty); }
set { this.SetValue(DelayElapsedProperty, value); }
}
public int DelayMilliseconds
{
get { return (int)this.GetValue(DelayMillisecondsProperty); }
set { this.SetValue(DelayMillisecondsProperty, value); }
}
private void BeginDelay()
{
this._animation = new DoubleAnimationUsingKeyFrames() { FillBehavior = FillBehavior.Stop };
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(this.DelayMilliseconds)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.Completed += (o, e) =>
{
this.DelayElapsed = 0d;
//this.Command.Execute(this.CommandParameter); // Replace with whatever action you want to perform
Console.Beep();
this.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
};
this.BeginAnimation(DelayElapsedProperty, this._animation);
}
private void CancelDelay()
{
// Cancel animation
this.BeginAnimation(DelayElapsedProperty, null);
}
private void TouchButton_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
this.BeginDelay();
}
private void TouchButton_TouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
this.CancelDelay();
}
}
}
如何调用TouchButton_TouchDown方法?我不得不以某种方式将它分配给TouchDown甚至处理程序吗?
好的,我添加了一个构造函数并设置了TouchDown / Up事件处理程序,以便可以正常工作,但CancelDelay()不会阻止事件被触发。它似乎工作正常,并在用户抬起手指时被调用,但不会阻止事件被触发。
答案 0 :(得分:0)
答案 1 :(得分:0)
你几乎可以肯定地想出一个解决方案来做到这一点。我会看两种方法:
选项#1可能是最容易理解的。生成单击事件的处理位于OnMouseLeftButtonDown和OnMouseLeftButtonUp处理程序的ButtonBase中。如果您实现(覆盖)这两个处理程序的自己版本,您应该能够相当容易地引入一个计时器,该计时器仅在用户按下(并按下)按钮后一定时间到期后才调用OnClick以生成单击事件。
PS:如果您还没有,我强烈建议您获取.NET Reflector的副本。它将允许您轻松查看WPF按钮实现的代码。我很快用它来看看WPF按钮的实现,以便了解它是如何工作的,以便回答这个问题。
答案 2 :(得分:0)
为了完整起见,这里是我根据@ kidshaw原始答案使用的完整解决方案。可能会拯救别人一段时间摆弄各个部分。
请注意,我收到VS Designer错误,抱怨没有在apps命名空间中找到自定义类。奇怪的是,这似乎不会发生在早期版本的VS上,所以也许它是VS中的一个错误。
<强> TouchButton.cs 强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace TouchButtonApp
{
public class TouchButton : Button
{
DoubleAnimationUsingKeyFrames _animation;
bool _isCancelled = false;
public static readonly DependencyProperty DelayElapsedProperty =
DependencyProperty.Register("DelayElapsed", typeof(double), typeof(TouchButton), new PropertyMetadata(0d));
public static readonly DependencyProperty DelayMillisecondsProperty =
DependencyProperty.Register("DelayMilliseconds", typeof(int), typeof(TouchButton), new PropertyMetadata(Properties.Settings.Default.ButtonTouchDelay));
public static readonly DependencyProperty IsTouchedProperty =
DependencyProperty.Register("IsTouched", typeof(bool), typeof(TouchButton), new PropertyMetadata(false));
// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TouchButton));
public TouchButton()
{
this.TouchDown +=TouchButton_TouchDown;
this.TouchUp +=TouchButton_TouchUp;
}
// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
// This method raises the Tap event
void RaiseTapEvent()
{
if (!_isCancelled)
{
//Console.Beep();
this.IsTouched = true;
Console.WriteLine("RaiseTapEvent");
RoutedEventArgs newEventArgs = new RoutedEventArgs(TouchButton.TapEvent);
RaiseEvent(newEventArgs);
}
}
public bool IsTouched
{
get { return (bool)this.GetValue(IsTouchedProperty); }
set { this.SetValue(IsTouchedProperty, value); }
}
public double DelayElapsed
{
get { return (double)this.GetValue(DelayElapsedProperty); }
set { this.SetValue(DelayElapsedProperty, value); }
}
public int DelayMilliseconds
{
get { return (int)this.GetValue(DelayMillisecondsProperty); }
set { this.SetValue(DelayMillisecondsProperty, value); }
}
//Start the animation and raise the event unless its cancelled
private void BeginDelay()
{
_isCancelled = false;
Console.WriteLine("BeginDelay ");
this._animation = new DoubleAnimationUsingKeyFrames() { FillBehavior = FillBehavior.Stop };
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.KeyFrames.Add(new EasingDoubleKeyFrame(1, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(this.DelayMilliseconds)), new CubicEase() { EasingMode = EasingMode.EaseIn }));
this._animation.Completed += (o, e) =>
{
this.DelayElapsed = 0d;
//this.Command.Execute(this.CommandParameter); // Replace with whatever action you want to perform
RaiseTapEvent();
this.IsTouched = false;
};
this.BeginAnimation(DelayElapsedProperty, this._animation);
}
private void CancelDelay()
{
// Cancel animation
_isCancelled = true;
Console.WriteLine("CancelDelay ");
this.BeginAnimation(DelayElapsedProperty, null);
}
private void TouchButton_TouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
this.BeginDelay();
}
private void TouchButton_TouchUp(object sender, System.Windows.Input.TouchEventArgs e)
{
this.CancelDelay();
}
}
}
在 App.xaml
中触发IsTouched事件时的自定义动画<Style x:Key="characterKeyT" TargetType="{x:Type local:TouchButton}">
<Setter Property="Focusable" Value="False" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Margin" Value="6,4,8,4"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TouchButton}">
<Grid x:Name="grid">
<Border x:Name="border" CornerRadius="0">
<Border.Background>
<SolidColorBrush x:Name="BackgroundBrush" Color="{Binding Source={StaticResource settingsProvider}, Path=Default.ThemeColorPaleGray2}"/>
</Border.Background>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="Black"
TextElement.FontSize="24"></ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Resources>
<Storyboard x:Key="FadeTimeLine" BeginTime="00:00:00.000" Duration="00:00:02.10">
<ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color"
To="#FF22B0E6"
Duration="00:00:00.10"/>
<ColorAnimation Storyboard.TargetName="BackgroundBrush" Storyboard.TargetProperty="Color"
To="#FFECE8E8"
Duration="00:00:02.00"/>
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsTouched" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource FadeTimeLine}"/>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource ThemeSolidColorBrushPaleGray}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" TargetName="border" Value="{StaticResource ThemeSolidColorBrushPaleGray2}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
XAML用法
<UserControl x:Class="TouchButtonApp.Keyboard1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TouchButtonApp"
mc:Ignorable="d"
d:DesignHeight="352" d:DesignWidth="1024">
<Grid>
<Grid Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
<ColumnDefinition Width="93*"/>
</Grid.ColumnDefinitions>
<local:TouchButton x:Name="qButton" Tap="Button_Click" Content="Q" Grid.Row="1" Style="{DynamicResource characterKeyT}" />
<local:TouchButton x:Name="wButton" Tap="Button_Click" Content="W" Grid.Column="1" Grid.Row="1" Style="{DynamicResource characterKeyT}" />
...