我正在编写一个WPF程序,我使用EventToCommand处理第三方控件中的事件并将它们连接到ICommands。我的视图模型方法都是异步的,我遇到的问题是事件不等待处理程序完成。问题在WPF中有点长,但我在下面的小控制台应用程序中总结了它。
我的一般问题是EventToCommand的实现是否允许我使用异步处理程序?是的,我确实谷歌没有发现任何东西。 mvvm-light文档here没有提到异步。
我愿意接受任何完成工作的工作。请注意下面的代码我使用名称" ButtonClick"这可能会让你问为什么我不会只是绑定到命令....这对于示例抱歉是一个糟糕的命名选择。在我的WPF应用程序中,我正在处理没有命令的事件。
class Program
{
public event EventHandler ButtonClickEvent;
static void Main(string[] args)
{
Console.WriteLine("Program");
var p = new Program();
}
public Program()
{
ButtonClickEvent += ButtonClickEventHandler;
Console.WriteLine("Start fire event");
ButtonClickEvent(this, new EventArgs()); // In WPF app this event is handled by EventToCommand, which executes an ICommand
Console.WriteLine("End fire event");
Console.Write("Press any key...");
Console.ReadKey();
// Output
// Start fire event
// Start GetStringAsync
// End fire event <-- not waiting
// End GetStringAsync
}
// In WPF app this method is wired up to a DelegateCommand
public async void ButtonClickEventHandler(object sender, EventArgs e)
{
await DownloadStuffAsync();
}
public async Task DownloadStuffAsync()
{
Console.WriteLine("Start GetStringAsync");
string page = await new HttpClient().GetStringAsync("http://www.ibm.com");
Console.WriteLine("End GetStringAsync");
}
}
答案 0 :(得分:2)
EventHandlers不支持异步方法。您需要一些Task
返回方法作为解决方法,并等待调用该事件的Task
。
您可以按照Stephen Cleary关于此主题的文章中的here解释使用Deferral。还有其他解决方案,例如覆盖SynchronizationContext
,但使用上面文章中显示的内容会更好。
答案 1 :(得分:1)
我遇到的问题是事件不等待处理程序完成。
我的一般问题是EventToCommand的实现是否允许我使用异步处理程序?
好吧,从第三方控件的角度来看问题。它提出的事件看起来像这样:
void ControlEvent(object sender, ControlEventArgs args);
当该代表返回时,已完成。事件处理程序签名返回void
,所有参数都是从控件传递到事件处理程序的数据。没有&#34;返回通信&#34;。代表没有办法告诉第三方控件&#34;我还没有完全完成;我只是回来释放线程,因为我是异步的;我有更多代码可以在以后执行,然后我
因此,简而言之,您想要做的事情可能需要对第三方控件进行非平凡的更改。有various hacks to try to force the asynchronous code to complete synchronously(正如我在最近的MSDN文章中所描述的那样),但它们都不能很好地工作。斯里兰姆所说的更好的解决方案是改变第三方控制以使用Deferrals;我在博客上描述了one possible implementation of Deferrals。但是,对于第三方控件来说,这将是一个非平凡的变化,他们可能不愿意这样做。
答案 2 :(得分:0)
我尝试使用Sriram在我在WPF的评论中提供的链接上的解决方案,它确实有效。
我的ViewModel
public class ViewModel
{
public ICommand DoSomethingCommand { get; set; }
public ViewModel()
{
DoSomethingCommand = new RelayCommand(DoSomething);
}
private async void DoSomething()
{
await DownloadStuffAsync();
Debug.WriteLine("Button Call Completed");
}
public async Task DownloadStuffAsync()
{
Debug.WriteLine("Start GetStringAsync");
await Task.Run(() =>
{
Debug.WriteLine("Thread sleeping");
Thread.Sleep(5000);
}).ConfigureAwait(false);
string page = await new HttpClient().GetStringAsync("http://www.ibm.com").ConfigureAwait(false);
Debug.WriteLine("End GetStringAsync");
}
}
我的观点
<StackPanel>
<Button Content="Do Something" Margin="0,2,2,2" Command="{Binding DoSomethingCommand}"/>
</StackPanel>
和调试窗口上的输出
答案 3 :(得分:0)
Sam事件只不过是一个触发器,可以通知按钮单击或鼠标移动等事情。如果你想开始做那件事。但在那之后处理你的异步需求。能否请您详细说明您的需求。这样我可以提供更多帮助吗?
由于 Ankit