c# - Task 和 FrameworkElement 参数的词法绑定(bind)

标签 c# wpf asynchronous

我从以下语句中得到了我对任务内部可用的外部 lambda 参数的期望,但是 FrameworkElement 转换对象的某些属性(包括名称)抛出 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 在任务中的监视部分...

enter image description here

我猜这是线程问题?
错误的原因是什么?将我需要的值传递到任务中的最佳方法是什么?


解决方案

正如@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
);

关于我在下面的评论中提出的问题,根据建议的阅读,我想等待任务是一个好主意的原因是与线程生命周期有关。
我尝试了上面的异步版本,在任务中使用 Thread.Sleep 调用并将其与删除异步和等待的相同内容进行比较。它表现相同。包含上述代码的窗口是从主窗口启动的,如果我启动任务并关闭子窗口,让主窗口继续运行,MessageBox 仍会在 sleep 期后显示。如果我也关闭主机窗口,则在任务完成之前,进程终止并且 MessageBox 不显示。
我不知道这两种情况下是否存在任何内存泄漏,但我认为等待任务的做法是为了消除这种风险。

最佳答案

从后台线程(如运行任务的线程),您无法访问在应用程序的 UI 线程中创建的 DependencyObjects 的依赖属性。这是因为 DependencyObjects 是 DispatcherObjects,因此具有线程关联性。

带有按钮单击处理程序的简单示例:

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

关于c# - Task 和 FrameworkElement 参数的词法绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41510725/

相关文章:

android - AsyncHttpRequest POST 不触发回调(loopj 的 android-async-http)

c# - 使用 ??运算符并处理空值

c# - 当属性等于 Max 和 NHibernate 时选择对象

wpf - 重用数据模板

c# - 如何使窗口高度适应内容?

c# - 所有的 Web 请求都是并行执行和异步处理的吗?

c#:在完成之前经过一定时间后重新启动异步任务

c# - CoCreateInstance 返回 "Class not registered"

c# - 如何在接收器事件中心编辑 EvenData 消息?

.net - WPF 图形布局组件