在后台填充数据网格

发布于 2025-01-07 03:41:36 字数 760 浏览 4 评论 0原文

我正在尝试在后台填充 devexpress GridControl (这不是一个快速的过程)。我这样做:

...
CreateGrid();
ShowMessageInsteadOfGridControl;
...
FillGrid(dataGrid, other UI params);
...

在网格中写入数据:

private void FillGrid(GridControl data, ...);
{
   Task.Factory.StartNew(() =>
                                 {
                                      Application.Current.Dispatcher.Invoke(new Action(() => FillData(gridControl,UIparamns)),
                                                        DispatcherPriority.Background);
                                  }).ContinueWith(c => HideUserMessage(UIparamns));
}

当我调用 FillData 时,它会导致 UI 冻结。我无法使用通常的任务,因为网格是从 UI 填充的,并且我有“调用线程无法访问此对象”。

如何在后台进行此类数据发布过程而不冻结 UI?

I'm trying to fill devexpress GridControl in background (it's not quick process). I do it like this:

...
CreateGrid();
ShowMessageInsteadOfGridControl;
...
FillGrid(dataGrid, other UI params);
...

Write data in Grid:

private void FillGrid(GridControl data, ...);
{
   Task.Factory.StartNew(() =>
                                 {
                                      Application.Current.Dispatcher.Invoke(new Action(() => FillData(gridControl,UIparamns)),
                                                        DispatcherPriority.Background);
                                  }).ContinueWith(c => HideUserMessage(UIparamns));
}

When I call FillData, it causes UI freezing. I can't use usual Task, because Grid filled from UI and I have "The calling thread cannot access this object".

How to make such dataposting process in background without freezing UI?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

離殇 2025-01-14 03:41:36

调度程序调用将一切放回到 UI 线程上,您需要将操作分成可以在后台完成的部分和真正需要完成的部分UI 线程,就像添加一个项目一样,只将这些操作传递给调度程序。

DispatcherPriority.Background 只是意味着其他具有更高优先级的项目将首先执行,这与后台线程无关,每次调用 UI 线程的调度程序都会导致该操作被执行迟早在所述 UI 线程上执行)

The dispatcher invoke call puts everything back on the UI thread, you need to split your operation into parts which can be done in the background and those which really need to be done on the UI-thread, like adding an item, only pass those operations to the dispatcher.

(DispatcherPriority.Background just means that other items, with higher priority, will be executed first, this has nothing to do with background threads, every call to the dispatcher of the UI-thread leads to that operation being executed on said UI-thread sooner or later)

无声静候 2025-01-14 03:41:36

通常,在处理与 UI 线程相关的代码时使用 Task.Factory.StartNew(...) 是危险的,因为它第一次会检查 TaskScheduler,发现没有,就会使用线程池TaskScheduler。当线程使用名为 Compute(3) 的函数完成计算时,它会返回,但在这里它会编组回 UI 线程,然后使用迄今为止的结果更新 UI 线程。

第二次和第三次调用 Compute 时,由于 SynchronizationContext 已被编组回 UI,因此它将在 UI 线程上运行,从而阻塞您的 UI。

private void Form1_Load(object sender, EventArgs e)
{
    Task.Factory.StartNew(A);
}

private static void A() { }

private void Form1_Load(object sender, EventArgs e)
{
    Compute(3);
}

private void Compute(int counter)
{
    // If we're done computing, just return.
    if (counter == 0)
        return;

    var ui = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() => A(counter))
        .ContinueWith(t =>
        {
            Text = t.Result.ToString(); // Update UI with results.

            // Continue working.
            Compute(counter - 1);
        }, ui);
}

private int A(int value)
{
    return value; // CPU-intensive work.
}

要避免此潜在问题,请改用 Task.Run(() => A());

有关更多详细信息,请参阅 Stephen Cleary 的文章,网址为 https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html 和/或 Task.Run() 之间有什么区别和 Task.Factory.StartNew()

这是我发现的一种在后台加载数据网格数据而不阻塞 UI 的方法。

首先,创建一个锁对象,并启用集合同步,然后使用 Task.Run() 在后台线程上实际加载数据:

    private readonly object _myDataLock = new object();

    private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
    private CollectionViewSource MyListCollectionView = new CollectionViewSource();

    public MyViewModelConstructor() : base()
    {
        // Other ctor code 
        // ...
        // assign the data source of the collection views
        MyListCollectionView.Source = MyList;               

        // Setup synchronization
        BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
    }

    private async void LoadMyList()
    {
                // load the list
                await Task.Run(async () =>
                    {
                        MyList.ReplaceAll(await MyRepository.LoadMyList());
                    }
                );      
    }

然后在存储库中您可以编写:

    public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
    {
        var results = await this.DataContext.TwentyFieldDataRecords
           .OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);

        return results;
    }   

然后您可以像这样绑定在关联视图中:

<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
                   ....
                   ItemsSource="{Binding MyListCollectionView.View}" 
                   ... >

有关详细信息,请参阅:

Generally it is dangerous to use Task.Factory.StartNew(...) when working with code related to the UI thread, because the first time it will examine the TaskScheduler, find that there isn't one, and it will use a thread pool TaskScheduler. When the thread is done computing using a function called, say, Compute(3), it returns but here it marshals back to the UI thread, then will update the UI thread with the results so far.

The second and third times it calls Compute, since the SynchronizationContext has been marshaled back to the UI, it will then run on a UI thread, thus blocking your UI.

private void Form1_Load(object sender, EventArgs e)
{
    Task.Factory.StartNew(A);
}

private static void A() { }

private void Form1_Load(object sender, EventArgs e)
{
    Compute(3);
}

private void Compute(int counter)
{
    // If we're done computing, just return.
    if (counter == 0)
        return;

    var ui = TaskScheduler.FromCurrentSynchronizationContext();
    Task.Factory.StartNew(() => A(counter))
        .ContinueWith(t =>
        {
            Text = t.Result.ToString(); // Update UI with results.

            // Continue working.
            Compute(counter - 1);
        }, ui);
}

private int A(int value)
{
    return value; // CPU-intensive work.
}

To avoid this potential problem, instead use Task.Run(() => A());

For more details, please see Stephen Cleary's article at https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html and/or What is the difference between Task.Run() and Task.Factory.StartNew()

Here is a way that I've found to load data for a datagrid in the background while not blocking your UI.

First, create a lock object, and enable collection synchronization, then actually load the data on a background thread using Task.Run():

    private readonly object _myDataLock = new object();

    private FastObservableCollection<My20FieldsDataRecord> MyList = new FastObservableCollection<My20FieldsDataRecord>();
    private CollectionViewSource MyListCollectionView = new CollectionViewSource();

    public MyViewModelConstructor() : base()
    {
        // Other ctor code 
        // ...
        // assign the data source of the collection views
        MyListCollectionView.Source = MyList;               

        // Setup synchronization
        BindingOperations.EnableCollectionSynchronization(MyList, _myDataLock);
    }

    private async void LoadMyList()
    {
                // load the list
                await Task.Run(async () =>
                    {
                        MyList.ReplaceAll(await MyRepository.LoadMyList());
                    }
                );      
    }

Then in your repository you could write:

    public virtual async Task<IList<My20FieldsDataRecord>> LoadMyList()
    {
        var results = await this.DataContext.TwentyFieldDataRecords
           .OrderByDescending(x => x.MyDate).ToListAsync().ConfigureAwait(false);

        return results;
    }   

Then you could bind in your associated view like this:

<controls:DataGrid Name="MyListDataGrid" Grid.Row="1"
                   ....
                   ItemsSource="{Binding MyListCollectionView.View}" 
                   ... >

For details, please see:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文