2014-09-04 44 views
0

我試圖解決電容式觸摸屏的靈敏度問題,如果用戶的手指通過太靠近屏幕表面,WPF按鈕將被觸發。解決WPF觸摸屏靈敏度的方法

這個問題是,許多用戶手指或手指的部分,除了他們的主要觸摸手指,靠近屏幕表面,並導致觸發不正確的按鈕。

調整屏幕的靈敏度似乎沒什麼區別,我想我可以嘗試修改按鈕按下的事件,只有按下按鈕超過一定的時間纔會觸發點擊。

任何人都可以解釋如何創建一個自定義按鈕,觸發點擊事件之前將有一個可調'按'時間。

如果可能的話,也許你會友好地包含一個非常簡單的C#/ WPF應用程序與這樣的自定義按鈕。

編輯

OK,所以我用下面的代碼,按@ 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

想不出你創建一個用戶控件一個按鈕,wh ich在按下時啓動一個定時器,並在100ms後手動觸發點擊事件? – 2014-09-04 05:46:48

+0

那麼問題是,Windows觸摸驅動程序似乎沒有區分輕輕觸摸或長時間觸摸。所以我需要按鈕才能觸發事件,如果長按按鈕(並且需要一些實驗來確定可用性的最佳持續時間)。我的猜測是印刷機的時間大約是200-300毫秒,而傳球的觸發器會少於200.我想我可以在Press上啓動定時器並在Release上停止,如果> X觸發事件。 – 2014-09-04 06:22:44

+0

多數民衆贊成我的意思;) – 2014-09-04 06:28:31

回答

0

時間延遲按鈕將是最好的選擇。

我在這個其他堆棧溢出答案中提供了一個例子。

它使用動畫來延遲觸發命令。

希望它有幫助。

Do wpf have touch and gold gesture

+0

謝謝,看起來它會通過確保按鈕時間縮短來完成這項工作。你能解釋這段代碼在哪裏,還是全部在一個類文件中?更好的是提供一個帶有單個屏幕的小樣本應用程序和一個這樣的按鈕。 – 2014-09-04 06:54:58

+0

在一個類文件中創建它。建立你的應用程序,它應該可以從工具箱拖入 – kidshaw 2014-09-04 06:57:20

+0

好吧,已經做到了,但編譯器不喜歡'this.GetValue'或'this.SetValue'調用或this._animation,this.Command等..有這些需要參考嗎? – 2014-09-04 07:18:48

0

你幾乎肯定拿出一個解決方案來做到這一點。有兩種方法我想看看:

  1. 創建一個從按鈕獲得的專業化。你可以重寫不同的處理程序來實現你自己的行爲。
  2. 創建訂閱預覽鼠標事件的附加依賴項屬性。預覽事件將允許您在標準按鈕處理生成點擊事件之前攔截向上/向下事件以注入自己的行爲。

選項#1可能是最容易得到您的頭。生成Click事件的處理在OnMouseLeftButtonDown和OnMouseLeftButtonUp處理程序的ButtonBase中。如果您實現(覆蓋)您自己的這兩個處理程序的版本,則應該能夠相當容易地引入一個計時器,該計時器在用戶按下(並按住)按鈕後一段時間過期後纔會調用OnClick來生成單擊事件。PS:如果你還沒有它,我強烈建議你得到.NET Reflector的副本。它將允許您輕鬆查看WPF按鈕實現的代碼。我很快就用它來查看WPF按鈕的實現,以瞭解它是如何工作的,以便回答這個問題。

0

爲了完整起見,我使用了基於@ kidshaw的原始答案的完整解決方案。可能會把別人的東西拉到一起。

請注意,我收到VS Designer錯誤,它抱怨沒有在應用程序命名空間中找到自定義類。奇怪的是,這似乎並沒有發生在VS的早期版本上,所以也許它是VS中的一個bug。

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(); 
     } 

    } 
} 

時IsTouched事件被觸發的App.xaml

自定義動畫
<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}" /> 
...