我根据Task中可用的外部lambda的参数获得了我对以下语句的期望但是,FrameworkElement强制转换对象的一些属性(包括Name)抛出了System.InvalidOperationException 。外部lambda中没有这样的错误。
Targets.AddHandler(CommandManager.ExecutedEvent,
(ExecutedRoutedEventHandler) ((s,e) =>
Task.Run(() =>
MessageBox.Show(string.Format("Targets {0} {1}",
((FrameworkElement)s).Name, e.RoutedEvent.Name))))
, true);
以下是((FrameworkElement)s).Name
任务内部的观察部分......
我想这是一个线程问题? 错误的原因是什么?将我需要的值传递给任务的最佳方法是什么?
正如@Mercer所解释的,我需要处理DependencyObject
的线程亲和性。如答案中所述,它继承自DispatcherObject
,所以 - 仅仅为了问题的完整性 - 如果有必要,我可以充分利用它来获取属性的实时值。
Targets.AddHandler(CommandManager.ExecutedEvent,
(ExecutedRoutedEventHandler) (async (s, e) =>
await Task.Run(() =>
{
var source = s as FrameworkElement;
string elementName = "null";
source.Dispatcher.Invoke(() =>
elementName = source.Name
);
MessageBox.Show(string.Format("Targets {0} {1}",
elementName, e.RoutedEvent.Name));
})
)
, true
);
关于我在下面的评论中的问题,基于建议的阅读,我想等待一个任务是一个好主意的原因是与线程生命周期有关。
我尝试了上面的异步版本,在Task中调用了Thread.Sleep,并将其与async和await删除相同。它的表现相同。包含上述代码的窗口从主窗口启动,如果我启动任务并关闭子窗口,使主窗口保持运行,则MessageBox仍会在休眠期后显示。如果我也关闭了主机窗口,在任务完成之前,进程终止,MessageBox没有显示。
我不知道在任何一种情况下是否有任何内存泄漏,但我认为等待任务的做法是消除这种风险。
答案 0 :(得分:1)
从后台线程(如Task运行的线程)中,您无法访问在应用程序的UI线程中创建的DependencyObjects的依赖项属性。这是因为DependencyObjects是DispatcherObjects,因此具有线程关联性。
使用Button Click处理程序的简单示例:
<Button Content="Click" Click="OnButtonClick"/>
以下Click处理程序方法抛出InvalidOperationException:
private async void OnButtonClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
await Task.Run(() => MessageBox.Show((string)button.Content));
}
虽然这个不会:
private async void OnButtonClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var content = (string)button.Content;
await Task.Run(() => MessageBox.Show(content));
}
我想在你的场景中也可以不等待任务:
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var content = (string)button.Content;
Task.Run(() => MessageBox.Show(content));
}