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

j8ag8udp  于 2022-11-30  发布在  其他
关注(0)|答案(3)|浏览(208)

我尝试编写一个代码来读取JSON文件,并允许用户在JSON文件中逐个输入对象的所有参数。
我试着写一个类似于“等待按钮”的东西,但是我没有为按钮写一个“GetAwaiter”扩展,尽管我找到了关于如何做的信息。
https://learn.microsoft.com/en-us/dotnet/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
http://blog.roboblob.com/2014/10/23/awaiting-for-that-button-click/
下面是我点击按钮“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. 
}

下面是尝试编写一个带扩展的Button的代码。我为自定义控件添加了一个新类,并编写了扩展。

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

但是我不能写一个继承System.Windows.Forms.Button的扩展,我该怎么办?
更新:以下是我尝试过的。

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;
            }}}
sdnqo3pr

sdnqo3pr1#

虽然你可能想重新设计你做事的方式,一个快速的解决方案是在模态模式下使用一个对话框,在对话框关闭时,捕获输入的数据并继续循环。循环将一直阻塞,直到对话框关闭。

liwlm1x9

liwlm1x92#

您可以使用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;
}
khbbv19g

khbbv19g3#

首先,我必须坚持你的请求违背了基于事件的MVVM模式的原则。
您的逻辑应该在一个单独的类中,并公开一个OnNext方法,该方法应该通过ActionCommand从模型中调用
无论如何,为了(尽可能地)符合MVVM模式,您不希望在按钮上使用await,而希望在绑定到按钮的命令上使用更多。
因此,让我们构建一个可等待的命令:

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

然后你可以用它来创建你的逻辑(我把它放在模型里,这是一个不好的做法):

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

而你的看法:

<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>

可用的工作演示here

相关问题