【问题标题】:How to prevent firing of event multiple times on rapid click of a button in Xamarin forms如何防止在 Xamarin 表单中快速单击按钮时多次触发事件
【发布时间】:2021-02-18 02:16:09
【问题描述】:

快速单击按钮时如何避免多次调用同一事件。

下面是代码: 我创建了一个自定义委托命令,如下所示

  • 查看模型
namespace TestProject.ViewModels
{
    public class TestViewModel 
    {

        public CustomDelegateCommand MenuButtonClickCommand { get; set; }


        public TestViewModel()
        {
          MenuButtonClickCommand = new CustomDelegateCommand (async () => await ShowMenuAction());
        }

        
        private async Task ShowMenuAction()
        {
            //await some stuff
        }
    }
}


  • CustomDelegateCommand.cs

public class CustomDelegateTimerCommand : DelegateCommand
{
        public CustomDelegateTimerCommand(Action executeMethod, Func<bool> validateMethod, Action onBusy = default(Action)) : base(executeMethod)
        {
            BackgroundTaskWaitHandle = new EventWaitHandle(true, EventResetMode.ManualReset);
            _validateMethod = validateMethod;
            _onBusy = onBusy;
        }

}

我面临的问题是,每当用户快速单击按钮时,菜单列表弹出窗口就会打开多次。 我的项目中有很多命令,我需要一个可以在全球范围内工作的解决方案。

我尝试使用 ObservesCanExecute() 解决如下问题,但我不喜欢为每个命令创建单独变量的想法,因为我的项目中有很多命令并且我不想要按钮当 CanExecute = false 时进入禁用状态。

  • 视图模型
MenuButtonClickCommand = new CustomDelegateCommand (async () => await ShowMenuAction().ObservesCanExecute(() => CanExecute );

private async Task ShowMenuAction()
        {
            CanExecute = false;

            //await some stuff

            CanExecute = true;
        }

非常感谢任何帮助!

【问题讨论】:

  • 你可以绑定按钮的IsEnabled,当用户点击它时设置为false,处理逻辑后设置为true。

标签: xamarin xamarin.forms mvvm command


【解决方案1】:

创建一个继承自 Xamarin.Forms.Button 的新类,延迟点击事件,然后将其添加到您的 xmal。

public class DelayedButton : Xamarin.Forms.Button
{
    public DelayedButton()
    {
        this.Clicked += DelayedButton_Clicked;
    }

    async private void DelayedButton_Clicked(object sender, EventArgs e)
    {
        this.IsEnabled = false;
        await Task.Delay(Delay);
        this.IsEnabled = true;
    }


    public int Delay { get; set; } = 500;
}

在 XAML 中:

<yourNameSpace:DelayedButton Delay="300" Text="DelayedButton" Command="{Binding ClickCommand}"/>

【讨论】:

    【解决方案2】:

    有两种解决方案。一个是你使用的时候,MVVM 另一个是你不使用的时候。 非 MVVM 解决方案是将方法的执行延迟一定时间,如下所示:

        public class SingleClickListener
    {
        private bool hasClicked;
        private Action<object, EventArgs> _setOnClick;
        public SingleClickListener(Action<object, EventArgs> setOnClick)
        {
            _setOnClick = setOnClick;
        }
        public void OnClick(object v, EventArgs e)
        {
            if (!hasClicked)
            {
                _setOnClick(v, e);
                hasClicked = true;
            }
            reset();
        }
        private void reset()
        {
            Android.OS.Handler mHandler = new Android.OS.Handler();
            mHandler.PostDelayed(new Action(() => { hasClicked = false; }), 500);
        }
    
    }
    

    然后当你订阅 onclick 事件时:

                    var buttonNa = new Button { Text = "Test Button" };
                buttonNa.Clicked += new SingleClickListener((sender, e) =>
                {
    
                    //DO something
                }).OnClick;
    

    Mvvm 解决方案稍微复杂一些,但没有那么老套。

                TestCommand = new Command(
                execute: async () =>
                {
                    IsEditing = true;
                    RefreshCanExecutes();
                    //Fire Method
             TestMethod();
                },
                canExecute: () =>
                {
                    return !IsEditing;
                });
    
            public void RefreshCanExecutes()
        {
            (TestCommand as Command).ChangeCanExecute();
        }
            public void TestMethod()
        {
            //DO something
            IsEditing = false;
            RefreshCanExecutes();
        }
    

    显然不要忘记将你的命令绑定到 xaml :) 第二种解决方案实际上也禁用了按钮,因此用户甚至无法点击它,但第一个解决方案只会忽略进一步的点击,直到时间延迟结束。

    【讨论】:

    • 谢谢,上述两种解决方案都可以,但由于我的项目中有很多命令,我想避免为每个命令创建单独的变量(IsEditing)。
    • 恐怕你做不到。除非您使用在 App.xaml.cs 中某处声明的全局变量,但是一旦您点击一个按钮,就会禁用所有按钮。
    猜你喜欢
    • 2011-02-22
    • 1970-01-01
    • 1970-01-01
    • 2014-05-04
    • 1970-01-01
    • 2011-09-23
    • 2014-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多