WPF: как показать вкладку «Пожалуйста, подождите» - PullRequest
1 голос
/ 01 марта 2011

В моем TabControl элементе управления оберткой я хочу предоставить следующую функцию:

void AddTab(Func<object> tabContentGenerator)

Функция должна добавить новый TabItem с содержимым «Пожалуйста, подождите», а затем вызвать функцию tabContentGenerator, чтобы получить объект для показа, и заменить содержимое этого TabItem возвращенным объектом.

Я пытался реализовать вызов tabContentGenerator в BackgrounWorker. Однако функция tabContentGenerator обычно создает UserControl для использования в качестве содержимого, и это вызывает исключение при вызове в BackgroundWorker. Есть ли у вас другая идея о том, как добиться требуемого поведения при наличии элемента вкладки «Пожалуйста, подождите», который впоследствии заменяется реальным контентом (который должен быть сгенерирован в потоке STA)?

Ответы [ 3 ]

1 голос
/ 01 марта 2011

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

Это похоже на какую-то платформу плагина, в которую вводится tabContentGenerator.Если это так, я бы использовал два действия, одно из которых выполняет долгую работу, а другое создает элементы управления.Ваш расширенный TabControl будет запускать первый в DoWork, а второй в WorkerCompleted.

Например (псевдокод):

public void AddTab(Action backgroundAction, Func<FrameworkElement> constructUiAction)
{
    var tab = ...

    var worker = new BackgroundWorker();

    worker.DoWork += (sender, e) => { backgroundAction(); };

    worker.RunWorkerCompleted += (sender, e) => 
        { 
            var ui = constructUiAction(); 
            if (ui != null) tab.Content = ui;
        };


    worker.RunWorkerAsync();
}

Другой вариант -действие возвращает FrameworkElementFactory, которое затем используется для создания экземпляра GUI в потоке пользовательского интерфейса через ControlTemplate.FrameworkElementFactory не является DispatcherObject и может быть создан в потоке без GUI.Сложнее создать пользовательский интерфейс на фабриках, но если клиент указывает шаблон элемента управления в ресурсе в XAML, он может получить FrameworkFactoryElement из его визуального дерева (например, ((ControlTemplate)FindResource("MyTemplate")).VisualTree).

public void AddTab(Func<FrameworkElementFactory> tabContentGenerator)
{
    var tab = ...

    var worker = new BackgroundWorker();

    FrameworkElementFactory uiFactory = null;

    worker.DoWork += (sender, e) => { uiFactory = tabContentGenerator(); }

    worker.RunWorkerCompleted += (sender, e) => 
        { 
            if (uiFactory != null)
            {
                var template = new ControlTemplate();
                template.VisualTree = uiFactory;
                template.Seal();
                tab.Content = template.LoadContent();
            };
        }

    worker.RunWorkerAsync();
}
1 голос
/ 01 марта 2011

Вы могли бы взглянуть на http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls Это показывает неопределенный индикатор выполнения.

0 голосов
/ 01 марта 2011

Простой способ сделать это - добавить свойство Waiting в модель представления, создать элемент пользовательского интерфейса для использования во время ожидания вкладки и наложить стиль на содержимое, например:

<Grid>
  <DockPanel>
     <DockPanel.Style>
        <Style TargetType="DockPanel">
           <Setter Property="Visibility" Value="Visible"/>
           <DataTrigger Binding="{Binding Waiting}" Value="True">
              <Setter Property="Visibility" Value="Collapsed"/>
           </DataTrigger>
        </Style>
        <! -- normal content goes here -->
  </DockPanel>
  <DockPanel>
     <DockPanel.Style>
        <Style TargetType="DockPanel">
           <Setter Property="Visibility" Value="Collapsed"/>
           <DataTrigger Binding="{Binding Waiting}" Value="True">
              <Setter Property="Visibility" Value="Visible"/>
           </DataTrigger>
        </Style>
        <! -- waiting content goes here -->
  </DockPanel>
</Grid>

Команда, запускающая BackgroundWorker, должна обновить Waiting до истины (и заставить объект подняться PropertyChanged) перед вызовом DoWork.Обработчик событий BackgroundWorker RunWorkerCompleted устанавливает Warning обратно в false.

Вы заметите, что я не использую какой-либо метод AddTab для создания своей вкладки.В большинстве случаев вы никогда не должны писать код, который напрямую создает объекты WPF.Гораздо более продуктивно делать это декларативно.Когда я изучал WPF, я часто говорил: «Мне нужно создать это в коде, потому что я не могу сделать X в XAML».Правильный ответ на этот вопрос - узнать, как сделать X в XAML, потому что вы почти наверняка можете, какой бы X ни был.

...