Как связать пользовательскую привязку представления списка, чтобы пользовательский интерфейс не зависал - PullRequest
1 голос
/ 12 июля 2011

Я новичок в WPF, хотя я и пытался читать столько, сколько я могу, я новичок, которому нужна мудрость, которую мудрец WPF здесь.

У меня есть список просмотра с пользовательским представлением, котороеотобразить тайлвью линейчатых диаграмм.Пользователь выбирает элемент в отдельном списке, это устанавливает источник данных, который будет использоваться в просмотре списка.Моя проблема в том, что пользовательский интерфейс зависает, пока listivew генерирует представление.Вот как выглядит макет:

((извините, я новичок на сайте, поэтому не могу публиковать изображения, но вот ссылка: ScreenShot ))

Источник данных состоит из класса, который содержит некоторые описательные свойства и список списка точек данных (x, y).Tileview берет список списка точек данных и генерирует одну линейную диаграмму для каждого элемента списка, используя список точек данных для рисования линии.

В настоящее время у меня есть источник данных со списком, содержащим 200 элементов, и каждый элемент имеет около200 точек данных (x, y).

Когда я выбираю элемент в левом списке, я использую событие SelectionChanged , чтобы установить свойство ItemsSource моего списка просмотрас новым набором данных.Проблема в том, что пользовательский интерфейс зависает до тех пор, пока не будут сгенерированы все графики.

Я пробовал разные подходы без успеха.Я попытался установить источник данных как асинхронный:

DataSeries="{Binding IsAsync=True, Path=Data}"

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

Я также попытался поместитьпривязка внутри потока выглядит следующим образом:

private void List_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    //Find the selected itemsource
    string runName = (string)ExperimentList.SelectedItem;
    ExperimentalRun selectedRun = _experimentRuns.Find(item => item.Name == runName);

    //bind datasource thru a new thread
    Thread newThread = new Thread(lstProductsNewSource);
    newThread.Start(selectedRun);
}

private void lstProductsNewSource(Object selectedRun)
{
    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate()
    {
        lstProducts.ItemsSource = ((ExperimentalRun)selectedRun).RunGraphData;
    });
} 

lstProducts - это имя списка, в котором есть представление тайлов диаграмм.Свойство RunGraphData указывает на список списка точек данных.

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

КакДополнительные сведения Я хотел бы указать, что диаграммы генерируются пользовательским контролем, и вот как выглядит xml представления плитки:

<local:TileView x:Key="ImageView">
    <local:TileView.ItemTemplate>
        <DataTemplate>
            <StackPanel Width="150" VerticalAlignment="Top">
                <local:graph DataSeries="{Binding IsAsync=True, Path=Data}"/>
                <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center"  Text="{Binding >IsAsync=True, Path=ContainerName}" ></TextBlock>
            </StackPanel>
        </DataTemplate>
    </local:TileView.ItemTemplate>
</local:TileView>

Графический пользовательский контроль - это типичный элемент управления диаграммой, который получает набор данных и связываетit.

Я обращаюсь за советом о том, как сделать так, чтобы мой пользовательский интерфейс реагировал, пока представление списка является обязательным.Вы можете мне помочь?

Редактировать:

Я забыл упомянуть, что пытался использовать виртуализацию в просмотре списка без каких-либо заметных различий:

<ListView Name="lstProducts" View="{StaticResource GridView}" >VirtualizingStackPanel.IsVirtualizing="True" >
</ListView>

Я ожидал, чтоон будет загружать текущее количество элементов в окне и что при прокрутке вниз он начнет загружать новые элементы, но даже когда я использую свойство IsVirtualizing , установленное в значение true, он, кажется, загружает все сразу, с той же задержкой идаже если я прокручиваю вниз, нет никаких дополнительных задержек, создающих впечатление, что все элементы уже присутствуют.

Редактировать 2: На этом сайте я нашел еще один вопрос с ответом, который предоставил тип решения: ссылка

Следуя рекомендациям, приведенным в указанной теме, яизменил мою предыдущую попытку создания потока: от попытки инкапсулировать все событие привязки данных в один фоновый поток до перекачки данных по одному графику за раз, используя преимущества свойств автообновления ObservableCollection.По сути, я создаю пустой источник данных и назначаю его, затем добавляю элементы в источник данных по одному, создавая поток с приоритетом фона каждый раз, когда добавляю точку.Это выглядит так:

private void ExperimentList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    //Find the selected itemsource
    string runName = (string)ExperimentList.SelectedItem;
    ExperimentalRun selectedRun = _experimentRuns.Find(item => item.Name == runName);

    //create and empty datasource to be filled one item at a time
    ExperimentalRun pumpRun = new ExperimentalRun(((ExperimentalRun)selectedRun).Name);
    lstProducts.ItemsSource = pumpRun.RunGraphData;

    //fill the emtpy source using a new thread per item added
    foreach (var item in selectedRun.RunGraphData)
    {
        PumpExperimentalRunData pump = new PumpExperimentalRunData(pumpRun, item);
        Thread newThread = new Thread(PumpAdd);
        newThread.Start(pump);
    }
}

private void PumpAdd(object PumpExperimentalRunData)
{
    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate()
    {
            ((PumpExperimentalRunData)PumpExperimentalRunData).PumpRun.RunGraphData.Add(((PumpExperimentalRunData)PumpExperimentalRunData).Item);
    });
}

Этот подход, кажется, работает, но я беспокоюсь, что он создает слишком много потоков (200) каждый раз, когда я меняю источник данных ... То, что я прочитал до сих пор, рекомендует использовать только несколько потоков, и это не подходит как немногие, я думаю. Я хотел бы сделать это по-другому, но у меня нет идей.

Кто-нибудь может увидеть ошибку моих путей или указать лучший подход?

1 Ответ

0 голосов
/ 12 июля 2011

Вы должны использовать VirtualizingStackPanel .

<ListView.ItemsPanel>
  <ItemsPanelTemplate>   
      <VirtualizingStackPanel/>                 
  </ItemsPanelTemplate>  
</ListView.ItemsPanel>
...