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