gpt4 book ai didi

c# - 混合使用 Task 和 Dispatcher 会停止任务

转载 作者:太空宇宙 更新时间:2023-11-03 13:23:10 26 4
gpt4 key购买 nike

解释

我正在 WPF 中创建自己的搜索控件。此控件是一个 UserControl,它包含一个带有搜索参数的区域(例如:搜索特定 ID、名称等)和一个显示结果的 GridView

在我的控件中,我有一个ICommand 类型的dependency property,我在其中绑定(bind)命令以执行我的搜索查询。

public static readonly DependencyProperty SearchCommandProperty =
DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl));

在某个窗口中使用我的控件:

 <customControls:SearchControl SearchCommand="{Binding SearchItemsCommand}"
SearchResult="{Binding SearchResult}" />
  • SearchItemsCommand 是我的 ViewModel 中的一个 Command,我可以在其中找到我的搜索查询。在此命令中,您可以找到我的查询以检索结果。
  • SearchResult 是我的 ICollection,它包含搜索查询的结果。

命令代码

View 模型

 private DelegateCommand searchItemsCommand;
public DelegateCommand SearchItemsCommand
{
get
{
if (this.searchItemsCommand== null)
this.searchItemsCommand= new DelegateCommand(this.SearchItemsCommandExecuted);

return this.searchItemsCommand;
}
}

private ICollection<VoucherOverviewModel> voucherResults;
private void SearchItemsCommandExecuted()
{
using (DbContext context = new DbContext())
{
var query = (from v in context.Vouchers
join vt in context.VoucherTransactions on new
{
voucherID = v.VoucherID,
type = VoucherTransactionType.Out
} equals new
{
voucherID = vt.VoucherID,
type = vt.Type
}
join vtype in context.VoucherTypes on v.VoucherTypeID equals vtype.VoucherTypeID
join c in context.Customers on vt.CustomerID equals c.CustomerID
join pos in context.PointOfSales on v.PointOfSaleID equals pos.PointOfSaleID
select new VoucherOverviewModel()
{
PointOfSaleID = v.PointOfSaleID,
PointOfSaleName = pos.Name,
VoucherID = v.VoucherID,
VoucherCode = v.Code,
VoucherTypeID = v.VoucherTypeID,
VoucherTypeDescription = vtype.Code,
CustomerID = c.CustomerID,
CustomerName = c.Name,
Value = vt.Value,
UsedValue = context.VoucherTransactions
.Where(x => x.VoucherID == v.VoucherID &&
x.Type == VoucherTransactionType.In)
.Sum(x => x.Value),
CreateDate = vt.Date,
ValidFrom = v.ValidFrom,
ValidUntil = v.ValidUntil,
ParentVoucherID = v.ParentVoucherID,
Comment = v.Comment,
});

foreach (ISearchParameter searchParameter in this.SearchParameters)
{
if (!searchParameter.Value.IsNullOrDefault())
{
switch ((FilterVoucherParameterKey)searchParameter.Key)
{
case FilterVoucherParameterKey.CustomerID:
query = query.Where(x => x.CustomerID == (int)searchParameter.Value);
break;
case FilterVoucherParameterKey.VoucherID:
query = query.Where(x => x.VoucherCode.Contains((string)searchParameter.Value));
break;
case FilterVoucherParameterKey.PointOfSale:
query = query.Where(x => x.PointOfSaleID == (byte)searchParameter.Value);
break;
case FilterVoucherParameterKey.Type:
query = query.Where(x => x.VoucherTypeID == (byte)searchParameter.Value);
break;
}
}
}

this.voucherResults = query.ToList();
}
}

自定义控件

    public static readonly DependencyProperty SearchCommandProperty =
DependencyProperty.Register("SearchCommand", typeof(ICommand), typeof(SearchControl));

public ICommand SearchCommand
{
get
{
return (ICommand)this.GetValue(SearchCommandProperty);
}
set
{
this.SetValue(SearchCommandProperty, value);
}
}

这是我的依赖属性,因此我可以将 SearchItemsCommand 绑定(bind)到我的自定义控件。然后我有另一个 ICommand 来执行绑定(bind)命令并在我的自定义控件中显示 loading 元素。此 LocalSearchCommand 将在您单击按钮时执行。

 private DelegateCommand localSearchCommand;
public DelegateCommand LocalSearchCommand
{
get
{
if (this.localSearchCommand == null)
this.localSearchCommand = new DelegateCommand(this.LocalSearchCommandExecuted);

return this.localSearchCommand;
}
}

private void LocalSearchCommandExecuted()
{
loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null)));
})
.ContinueWith(t =>
{
if (t.IsCompleted)
{
t.Dispose();
}
});
}

问题

我想在执行查询以与用户交互时显示一个加载元素。要显示此元素,我必须将其设置为 visible。现在的问题是,当我将其设置为可见并想要执行搜索命令时,我的整个 UI 都卡住了。从数据库中获取结果并在 GridView 中生成之后,并且只有在那时,它才会显示我的加载元素。我确实理解为什么会发生这种情况,并且我尝试使用 Task 来解决它。

loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
this.Dispatcher.Invoke((Action)(() => this.SearchCommand.Execute(null)));
})
.ContinueWith(t =>
{
if (t.IsCompleted)
{
t.Dispose();
}
});

我必须在我的 Task 中使用 Dispatcher 来执行 SearchCommand,因为它属于 UI 线程。但是因为使用了Dispatcher类,所以出现了和之前一样的问题。我的 loading element 仅在查询已执行时显示,因为 Dispatcher 会在 UI 线程上执行搜索命令。如果不使用 Dispatcher 类,它会给我以下错误:

The calling thread cannot access this object because a different thread owns it.

我在线上收到此错误:

return (ICommand)this.GetValue(SearchCommandProperty);

即使使用空的 SearchItemsCommandExecuted 方法,也会发生错误。

我已经尝试过的

  • 我尝试将 TaskTaskScheduler 设置为

    TaskScheduler.FromCurrentSynchronizationContext()

  • 我使用了很多 BeginInvokeInvoke 的组合。

  • 我尝试在 Task 中设置 loading elementVisibility

但以上都不起作用。

如何解决我的问题,以便在执行查询时显示加载元素。我是否漏掉了一些明显的东西?

提前致谢!

洛顿

最佳答案

问题是您正在使用 ThreadPool 线程创建一个新的 Task,但使用 Dispatcher.Invoke,它在 UI 线程上运行您的命令,因此为什么您用户界面卡住。

您需要将 SearchCommand 工作卸载到后台线程,然后在 UI 线程上使用 Continuation 更新您的 UI(不要尝试在 中更新您的 UI搜索命令):

然后,它显示我的加载元素。我确实理解为什么会发生这种情况,并且我尝试使用任务来解决它。

loadingElement.Visible = true;
Task.Factory.StartNew(() =>
{
return this.SearchCommand.Execute(null);
})
.ContinueWith(t =>
{
MyUIElement = t.Result; // Update your UI here.
}, TaskScheduler.FromCurrentSynchronizationContext());

关于c# - 混合使用 Task 和 Dispatcher 会停止任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23424316/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com