简体   繁体   English

如何等待异步方法中的按钮单击?

[英]How can I await for a button click in an async method?

I try to write a code to read a JSON File and allows user to input all the parametes for the objects in the JSON File one by one.我尝试编写代码来读取 JSON 文件,并允许用户一一输入 JSON 文件中对象的所有参数。
I try to write something like an "awaitable Button", but I failed to write a "GetAwaiter" extension for the button, although I found informations about how to do it.我尝试写一些类似“等待按钮”的东西,但我没能为按钮写一个“GetAwaiter”扩展,尽管我找到了关于如何做的信息。

https://learn.microsoft.com/en-us/do.net/desktop/winforms/controls/how-to-inherit-from-existing-windows-forms-controls?view.netframeworkdesktop-4.8 https://learn.microsoft.com/en-us/do.net/desktop/winforms/controls/how-to-inherit-from-existing-windows-forms-controls?view.netframeworkdesktop-4.8

how can I combine await.WhenAny() with GetAwaiter extension method 我如何将 await.WhenAny() 与 GetAwaiter 扩展方法结合起来

http://blog.roboblob.com/2014/10/23/awaiting-for-that-button-click/ http://blog.roboblob.com/2014/10/23/awaiting-for-that-button-click/

So here is my code after clicking a button "loadJSON":所以这是我点击按钮“loadJSON”后的代码:

for (int i = 0; i<templist_net.Count; i++)
{
    GeneratorFunctions.GetNetworkParameterList(templist_net[i].Type, templist_net[i], treeViewPath.SelectedPath, SolutionFolder);
    cBoxPouItem.Text = templist_net[i].Type;

    ListViewParam2.ItemsSource = GeneratorFunctions.TempList;   // Parameter list binding
    temp = GeneratorFunctions.TempList;
    ListViewParam2.Visibility = Visibility.Visible;             // Set list 2 visible
    ListViewParam.Visibility = Visibility.Collapsed;            // Set list 1 invisible

    //something stop loop, and wait user to type parameters in Listview, and click Button, Then the loop move on. 
}

And Here is code trying to write a Button with extension.这是尝试编写带有扩展名的 Button 的代码。 I add a new class for custom control, and write the extension.我为自定义控件添加了一个新的class,并编写了扩展名。

public partial class CustomControl2 : System.Windows.Forms.Button
{
    static CustomControl2()
    {

    }
    public static TaskAwaiter GetAwaiter(this Button self)
    {
        ArgumentNullException.ThrowIfNull(self);
        TaskCompletionSource tcs = new();
        self.Click += OnClick;
        return tcs.Task.GetAwaiter();

        void OnClick(object sender, EventArgs args)
        {
            self.Click -= OnClick;

            tcs.SetResult();
        }
    }
}

But I can't write a extension, which inherit System.Windows.Forms.Button.但是我不能写一个继承System.Windows.Forms.Button的扩展。 What should I do?我应该怎么办?

UPDATE: here is what i tried.更新:这是我尝试过的。

 private async Task Btn_loadJsonAsync(object sender, RoutedEventArgs e) {
        // Initialize an open file dialog, whose filter has a extend name ".json"
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "(*.json)|*.json";
        TextBoxInformation.Text += "Opening project ...\n";
        if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        { 
            networks = GeneratorFunctions.ReadjsonNetwork(openFileDialog.FileName);
            for (int i = 0; i < networks.Count; i++)
            {
                if (temp != null)
                {
                    if (networks[i].Type == "Network")
                    {
                        templist_net.Add(networks[i]);
                        i = 1;
                    }
                    if (networks[i].Type == "Subsystem")
                    {
                        templist_sub.Add(networks[i]);
                        i = 1;
                    }
                    if (networks[i].Type == "Component: Data Point Based Control")
                    {
                        templist_com.Add(networks[i]);
                        i = 1;
                    }
                }
            }
            using (SemaphoreSlim semaphore = new SemaphoreSlim(0, 1))
            {
                void OnClick(object sender, RoutedEventArgs e) => semaphore.Release();
                btn.Click += OnClick;

                for (int i = 0; i < templist_net.Count; i++)
                {
                    //...

                    //wait here until [btn] is clicked...
                    await semaphore.WaitAsync();
                }

                btn.Click -= OnClick;
            }}}

Although you may want to redesign the way you are doing things, a quick an dirty solution would be to use a dialog box in modal mode and upon the dialog box closing, capture the data that got input and continue looping.尽管您可能想要重新设计您做事的方式,但一个快速但肮脏的解决方案是在模态模式下使用对话框,并在对话框关闭时捕获输入的数据并继续循环。 The loop will block until the dialog box is closed.循环将阻塞,直到对话框关闭。

You can wait for a button click asynchronously using a SemaphoreSlim , eg:您可以使用SemaphoreSlim异步等待按钮单击,例如:

using (SemaphoreSlim semaphore = new SemaphoreSlim(0, 1))
{
    void OnClick(object sender, RoutedEventArgs e) => semaphore.Release();
    btn.Click += OnClick;

    for (int i = 0; i < templist_net.Count; i++)
    {
        //...

        //wait here until [btn] is clicked...
        await semaphore.WaitAsync();
    }

    btn.Click -= OnClick;
}

First of all I must insist that your request goes against the principles of the MVVM pattern which is based on events.首先,我必须坚持认为您的要求违反了基于事件的 MVVM 模式的原则。
Your logic should be in a separate class and expose an OnNext method which should be called from the model through an ActionCommand您的逻辑应该在一个单独的 class 中,并公开一个OnNext方法,该方法应该通过ActionCommand从 model 调用

Anyway, to conform (as much as possible) to the MVVM pattern, you don't want to await on the button but more on the command bound to the button.无论如何,为了(尽可能地)符合 MVVM 模式,您不想await按钮,而是更多地等待绑定到按钮的命令。
So let's build an awaitable command:因此,让我们构建一个可等待的命令:

public class AwaitableCommand : ICommand
{
    private readonly object _lock = new();
    
    private TaskCompletionSource? _taskCompletionSource;

    /// <summary>
    /// null-event since it's never raised
    /// </summary>
    public event EventHandler? CanExecuteChanged
    {
        add { }
        remove { }
    }

    /// <summary>
    /// Always executable
    /// </summary>
    public bool CanExecute(object? parameter) => true;
    

    public void Execute(object? parameter)
    {
        lock (_lock)
        {
            if (_taskCompletionSource is null)
                return;

            _taskCompletionSource.SetResult();

            // reset the cycle
            _taskCompletionSource = null;
        }
    }

    public Task WaitAsync()
    {
        lock (_lock)
        {
            // start a new cycle if needed
            _taskCompletionSource ??= new TaskCompletionSource();
            return _taskCompletionSource.Task;
        }
    }
}

Then you can create your logic with it (I put it in the model, wich is a bad practice):然后你可以用它创建你的逻辑(我把它放在 model 中,这是一个不好的做法):

public class Model : NotifyPropertyChangedBase
{
    private int _count;

    public Model()
    {
        RunLogicAsync();
    }

    public int Count
    {
        get => _count;
        private set => Update(ref _count, value);
    }

    public AwaitableCommand OnNextCommand { get; } = new();
    
    /// <summary>
    /// I know, I know, we should avoid async void
    /// </summary>
    private async void RunLogicAsync()
    {
        try
        {
            for (;;)
            {
                await OnNextCommand.WaitAsync();
                Count++;
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

And your view:而你的观点:

<Window ...>
    <Window.DataContext>
        <viewModel:Model />
    </Window.DataContext>

    <Window.Resources>
        <system:String x:Key="StringFormat">You clicked it {0} times</system:String>
    </Window.Resources>

    <Grid>
        <Button Content="{Binding Count}"
                ContentStringFormat="{StaticResource StringFormat}"
                Command="{Binding OnNextCommand}"
                Padding="10 5"
                HorizontalAlignment="Center"
                VerticalAlignment="Center" />
    </Grid>
</Window>

Working demo available here .此处提供工作演示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我可以在异步方法中应用的方式和地点 - How and where i can apply await in my async method 了解异步 - 我可以等待同步方法吗? - Understanding async - can I await a synchronous method? 如何在此父方法中等待没有 async 修饰符的异步方法? - How can I await an async method without an async modifier in this parent method? 使用Xamarin MessagingCenter调用异步方法时,如何处理异步并等待? - How can I handle async and await when using Xamarin MessagingCenter calling an async method? 异步等待按钮单击始终同步运行 - Async await button click always running synchronously 是否可以在按钮单击期间等待异步任务? - Is it possible to await async tasks during a button click? 如何使用 async 和 await 实现异步 GUI 操作? - How can I realize async GUI operations with async and await? 如何异步/等待按钮单击,不仅仅是等待等待页面,而是等待返回前在页面上执行的操作 - How to Async/Await a button click, not just wait on the await the page, but wait an action performed on the page before returning 我可以替换一个 await 方法吗? 通过调用异步任务返回? - Can I replace an await method; return with a call to an async Task? 有没有办法我可以等待当前不异步的方法? - Is there a way that I can await a method that's currently not async?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM