重复按钮点击

时间:2017-01-31 11:54:21

标签: c# wpf visual-studio-2015

我有一个控件,允许用户在图像的特定部分执行一些重型图像处理,并且他们有箭头按钮在图像周围移动这个区域。

因为这个过程是非常重的(每次运行平均800毫秒)我使用了一个重复按钮,它将这变成了一个" Ghost"并且只在鼠标按下事件时执行该过程。

这非常有效并且解决了与此功能相关的大多数性能问题

无论其

某些用户拒绝学习这种持有和释放的方法,并坚持点击按钮移动它而不是持有和释放。

这意味着每次点击时都会调用重载方法,并且每次方法触发时它只会移动一个小的增量,因此当它尝试执行时,它们最终会挂起应用程序。这些800毫秒+过程中的100个

我的问题

如何以与保持和释放相同的方式处理此敲击行为?

我想过一个计时器但是无法弄清楚如何检测正常点击和最后一次点击之间的区别。

6 个答案:

答案 0 :(得分:2)

考虑监控鼠标活动并在短暂不活动后开始重型过程。

考虑在单独的线程上运行该进程 - 这可能意味着在内存中克隆(部分)映像。

考虑阻止进程多次同时运行(如果可能的话,即进程是异步的)。

答案 1 :(得分:2)

考虑以下选项之一:

禁用该按钮,直到该过程完成,导致800毫秒的延迟。用户很快就会学会使用hold down方法。这将涉及最少量的代码并将责任推给人类。它还可以确保您不会通过缓冲区中的点击或过度使用资源来阻止应用程序。

在按钮点击事件中放置一个计时器: '鬼区' 定时器启动(或复位为零)

然后,调用主要工作的代码位于计时器已过时的事件中,该事件将设置为您希望的任何暂停。 (即如果用户在一秒钟内没有再次点击) 然后停止计时器 执行代码

答案 2 :(得分:2)

快速,肮脏的解决方案:使用Timer

每次用户点击按钮,停止计时器,增加点击次数,启动计时器。如果计时器在用户再次点击之前经过,那么它应该做你的大工作方法。

这是否容易出现线程问题? 可能。我不是线程专家。如果线程专家可以对它发表评论,我会很高兴。

这是最好的解决方案吗? 几乎。但是它会让你感受一段时间(直到线程问题出现)。

page.onResourceRequested = function(requestData, request) {
    if ((/http:\/\/.+?\.css/gi).test(requestData['url']) || requestData.headers['Content-Type'] == 'text/css') {
        console.log('The url of the request is matching. Aborting: ' + requestData['url']);
        request.abort();
    }
};

最佳解决方案:此方法 plus 将此工作从GUI线程移开。然后你不必担心点击或点击并保持,你不会阻止 GUI线程。

如果您必须完成不更新UI的工作(例如重绘图像)然后将图像发送到UI,您可以创建一个新线程,然后您将遇到“从非UI线程访问UI元素”的错误,只需将UI代码放入Marshal即可。

private int _totalTaps = 0;
private const int _tapSequenceThreshold = 250; // Milliseconds
private Timer _tapTimer = new Timer(_tapSequenceThreshold);

private void InitializeTimer()
{
    _tapTimer.Elapsed += OnTapTimerElapsed;
}

private void OnTapTimerElapsed(object source, System.Timers.ElapsedEventArgs e)
{
    _tapTimer.Stop();

    // The `DoBigLogic` method should take the number of taps and
    // then do *something* based on that number, calculate how far
    // to move it, for example.
    DoBigLogic(_totalTaps);

    _totalTaps = 0;
}

// Call this each time the user taps the button
private void Tap()
{
    _tapTimer.Stop();
    _totalTaps++;
    _tapTimer.Start();
}

答案 3 :(得分:2)

您可以通过设置简单标志(最好是bool类型)来阻止程序执行800 ms功能。

您需要创建三个事件。一个鼠标向下,一个鼠标向上事件和另一个是按钮的鼠标移动事件。在声明时将标志设为false。单击该按钮时,在Mouse Down事件中将标志设为true。当您抬起鼠标单击时,即鼠标向上事件会使您的标记为假。

简单的代码说明。

bool click = false,run_process = true;

mouseDown_event()
{
    click = true;
}

mouseUp_event()
{
    click = false;
}

mouseMove_event()
{
    if(click == true && run_process == true)
    {
         click = false;
         run_process = false;
         //call your function
    }
}

答案 4 :(得分:1)

  

如何以与保持和释放相同的方式处理此敲击行为?

创建一个公式,根据最后一次抽头频率,当前操作和上次实际操作之间的时间计算权重值; 与我可能不知道的任何其他因素。使用正确的公式,它应该对使用系统的人来说具有代表性,而不是发送多次点击的人。

加权值应传递给另一个线程,该线程处理对屏幕的实际操作,并且可以处理暴风雪的点击或单击而不会错过任何节拍本身。

答案 5 :(得分:1)

您可以尝试使用反应式扩展which is available on nuget

using System;
using System.Windows.Controls;
using System.Windows.Input;
using System.Linq;
using System.Reactive.Linq;

namespace SmoothOutButtonTapping
{
    public static class Filters
    {
        //  First we need to combine the pressed events and the released
        //  events into a single unfiltered stream.
        private static IObservable<MouseButtonState> GetUnfilteredStream(Button button)
        {
            var pressedUnfiltered = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                x => button.PreviewMouseLeftButtonDown += x,
                x => button.PreviewMouseLeftButtonDown -= x);
            var releasedUnfiltered = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
                x => button.PreviewMouseLeftButtonUp += x,
                x => button.PreviewMouseLeftButtonUp -= x);

            return pressedUnfiltered
                .Merge(releasedUnfiltered)
                .Select(x => x.EventArgs.ButtonState);
        }

        //  Now we need to apply some filters to the stream of events.
        public static IObservable<MouseButtonState> FilterMouseStream(
            Button button, TimeSpan slidingTimeoutWindow)
        {
            var unfiltered = GetUnfilteredStream(button);

            //  Ironically, we have to separate the pressed and released events, even
            //  though we just combined them. 
            //  This is because we need to apply a filter to throttle the released events,
            //  but we don't need to apply any filters to the pressed events.
            var released = unfiltered

                //  Here we throttle the events so that we don't get a released event
                //  unless the button has been released for a bit.
                .Throttle(slidingTimeoutWindow)
                .Where(x => x == MouseButtonState.Released);

            var pressed = unfiltered
                .Where(x => x == MouseButtonState.Pressed);

            //  Now we combine the throttled stream of released events with the unmodified
            //  stream of pressed events.
            return released.Merge(pressed);
        }
    }
}

现在我们有一个流会在用户按下时立即响应,但不会触发已发布的事件,除非按钮释放的时间足够长。

以下是如何使用上述方法的示例。此示例仅在按钮处于“按下”状态时更改控件的颜色,但您可以轻松地执行任何操作。

using System;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Reactive.Linq;
using System.Threading;

namespace SmoothOutButtonTapping
{
    public partial class SmoothTapButtonControl : UserControl
    {
        public SmoothTapButtonControl()
        {
            InitializeComponent();

            _pressed = new SolidColorBrush(Colors.Lime);
            _released = Background;

            //  Don't forget to call ObserveOn() to ensure your UI controls
            //  only get accessed from the UI thread.
            Filters.FilterMouseStream(button, SlidingTimeoutWindow)
                .ObserveOn(SynchronizationContext.Current)
                .Subscribe(HandleClicked);
        }

        //  This property indicates how long the button must wait before 
        //  responding to being released.
        //  If the button is pressed again before this timeout window 
        //  expires, it resets.
        //  This is handled for us automatically by Reactive Extensions.
        public TimeSpan SlidingTimeoutWindow { get; set; } = TimeSpan.FromSeconds(.4);

        private void HandleClicked(MouseButtonState state)
        {
            if (state == MouseButtonState.Pressed)
                Background = _pressed;
            else
                Background = _released;
        }

        private Brush _pressed;
        private Brush _released;
    }
}

您可以在my github上找到上述示例的完整版本(包含项目文件和xaml)。