Заполните сетку данных в фоновом режиме - PullRequest
0 голосов
/ 21 февраля 2012

Я пытаюсь заполнить devexpress GridControl в фоновом режиме (это не быстрый процесс).Я делаю это так:

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

Запись данных в Grid:

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

Когда я вызываю FillData, это вызывает зависание пользовательского интерфейса.Я не могу использовать обычную задачу, потому что сетка заполнена из пользовательского интерфейса, и у меня есть «Вызывающий поток не может получить доступ к этому объекту».

Как сделать такой процесс передачи данных в фоновом режиме, не замораживая пользовательский интерфейс?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Обычно опасно использовать Task.Factory.StartNew(...) при работе с кодом, связанным с потоком пользовательского интерфейса, потому что в первый раз он проверит TaskScheduler, обнаружит, что его нет, и будет использовать пул потоков TaskScheduler.Когда поток завершает вычисления, используя, скажем, функцию, скажем, Compute(3), он возвращается, но здесь он маршалирует обратно в поток пользовательского интерфейса, а затем обновит поток пользовательского интерфейса с результатами до сих пор.

Второй ив третий раз он вызывает Compute, поскольку SynchronizationContext был перенаправлен обратно в пользовательский интерфейс, затем он будет выполняться в потоке пользовательского интерфейса, таким образом блокирует ваш пользовательский интерфейс.

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

Для получения более подробной информации, пожалуйста, смотрите статью Стивена Клири в https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html и / или В чем разница между Task.Run () и Task.Factory.StartNew ()

Вот способ, который я нашел, чтобы загрузить данные для сетки данных в фоновом режиме, не блокируя ваш пользовательский интерфейс.

Сначала создайте объект блокировки и включите синхронизацию коллекции, затем фактически загрузите данные в фоновый поток с помощью 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}" 
                   ... >

Для получения подробной информации, пожалуйста, смотрите:

0 голосов
/ 21 февраля 2012

вызов вызова диспетчера помещает все обратно в поток пользовательского интерфейса, вам нужно разделить вашу работу на части, которые можно выполнять в фоновом режиме, и те, которые действительно должны быть выполненыв UI-потоке, как и при добавлении элемента, эти операции передаются только диспетчеру.

(DispatcherPriority.Background просто означает, что другие элементы с более высоким приоритетом будут выполняться первыми, это не имеет никакого отношения кфоновые потоки, каждый вызов диспетчера UI-потока приводит к тому, что эта операция рано или поздно выполняется в указанном UI-потоке)

...