2010-02-03 9 views
7

Aplikacja C# XBapJak mogę wstępnie kontrolować elementy TabItem w WPF?

Mam tabControl z czterema elementami TabItems. Dwa z tych elementów TabItems zawierają po prostu dane DataGrid z pakietu WPFToolkit, który pobiera dość małe dane (100 wierszy po 4 kolumny) z bazy danych SQL Server. Mój problem polega na tym, że kiedy ładuję moją aplikację i klikam na jedną z TabItems zawierającą datagrid. Wydaje mi się, że trwa to 2-3 sekundy, zanim skupi się na tej karcie. Dzieje się tak tylko przy pierwszym kliknięciu karty. Wydaje się, że jest to renderowanie datagridu.

W jaki sposób ustawić te karty jako wstępne, gdy aplikacja zostanie załadowana, aby użytkownik kliknął kartę, początkowa przerwa nie trwała 2-3 sekundy, zanim pojawi się karta.

Dzięki

+0

Co jest jeszcze lepszego, jeśli zakładki są renderowane w osobnym wątku * po * aplikacja załadowała –

+0

Próbowałem użyć wątku, który wykonał wywołanie bazy danych i aktualizację źródła danych dla datagrid jednak nadal otrzymuję tę początkową pauzę za pierwszym kliknięciem karty. –

+0

Czy wywołujesz Async DB lub synchronizujesz je? Również w jaki sposób łączysz datagrid z tabelą? jeśli używasz datareader'a możesz próbować robić zwrot – Wegged

Odpowiedz

0

Problemem nie jest uzyskiwanie danych (można wczytywać, że w osobnym wątku), ale faktycznie budowa elementów wizualnych w DataGrid.

Jeśli zaznaczysz ze Snoop widać, że istnieje wiele elementów wizualnych, jeśli nie potrzebujemy wszystkie funkcje datagrid można przejść z prostszej reprezentacji (ListView/ItemsControl/niestandardowy)

6

Mamy użyj standardowego TabControla WPF, a problem polega na tym, że zmienia się VisualTree za każdym razem, gdy zmieniasz SelectedItem.

W efekcie stworzyliśmy specjalny TabControl (nazwałem go TabControlEx), który zachowuje wszystkie elementy renderowane, ale wybiera po prostu pokazać/ukryć ContentPresenters dla TabItems.

Oto odpowiedni kod

using System; 
using System.Collections.Specialized; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Controls.Primitives; 

namespace MVVM.Demo 
{ 
    /// <summary> 
    /// The standard WPF TabControl is quite bad in the fact that it only 
    /// even contains the current TabItem in the VisualTree, so if you 
    /// have complex views it takes a while to re-create the view each tab 
    /// selection change.Which makes the standard TabControl very sticky to 
    /// work with. This class along with its associated ControlTemplate 
    /// allow all TabItems to remain in the VisualTree without it being Sticky. 
    /// It does this by keeping all TabItem content in the VisualTree but 
    /// hides all inactive TabItem content, and only keeps the active TabItem 
    /// content shown. 
    /// </summary> 
    [TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))] 
    public class TabControlEx : TabControl 
    { 
     #region Data 
     private Panel itemsHolder = null; 
     #endregion 

     #region Ctor 
     public TabControlEx() 
      : base() 
     { 
      // this is necessary so that we get the initial databound selected item 
      this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
      this.Loaded += TabControlEx_Loaded; 
     } 
     #endregion 

     #region Public/Protected Methods 
     /// <summary> 
     /// get the ItemsHolder and generate any children 
     /// </summary> 
     public override void OnApplyTemplate() 
     { 
      base.OnApplyTemplate(); 
      itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel; 
      UpdateSelectedItem(); 
     } 

     /// <summary> 
     /// when the items change we remove any generated panel children and add any new ones as necessary 
     /// </summary> 
     /// <param name="e"></param> 
     protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) 
     { 
      base.OnItemsChanged(e); 

      if (itemsHolder == null) 
      { 
       return; 
      } 

      switch (e.Action) 
      { 
       case NotifyCollectionChangedAction.Reset: 
        itemsHolder.Children.Clear(); 
        break; 

       case NotifyCollectionChangedAction.Add: 
       case NotifyCollectionChangedAction.Remove: 
        if (e.OldItems != null) 
        { 
         foreach (var item in e.OldItems) 
         { 
          ContentPresenter cp = FindChildContentPresenter(item); 
          if (cp != null) 
          { 
           itemsHolder.Children.Remove(cp); 
          } 
         } 
        } 

        // don't do anything with new items because we don't want to 
        // create visuals that aren't being shown 

        UpdateSelectedItem(); 
        break; 

       case NotifyCollectionChangedAction.Replace: 
        throw new NotImplementedException("Replace not implemented yet"); 
      } 
     } 

     /// <summary> 
     /// update the visible child in the ItemsHolder 
     /// </summary> 
     /// <param name="e"></param> 
     protected override void OnSelectionChanged(SelectionChangedEventArgs e) 
     { 
      base.OnSelectionChanged(e); 
      UpdateSelectedItem(); 
     } 

     /// <summary> 
     /// copied from TabControl; wish it were protected in that class instead of private 
     /// </summary> 
     /// <returns></returns> 
     protected TabItem GetSelectedTabItem() 
     { 
      object selectedItem = base.SelectedItem; 
      if (selectedItem == null) 
      { 
       return null; 
      } 
      TabItem item = selectedItem as TabItem; 
      if (item == null) 
      { 
       item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem; 
      } 
      return item; 
     } 
     #endregion 

     #region Private Methods 

     /// <summary> 
     /// in some scenarios we need to update when loaded in case the 
     /// ApplyTemplate happens before the databind. 
     /// </summary> 
     /// <param name="sender"></param> 
     /// <param name="e"></param> 
     private void TabControlEx_Loaded(object sender, RoutedEventArgs e) 
     { 
      UpdateSelectedItem(); 
     } 

     /// <summary> 
     /// if containers are done, generate the selected item 
     /// </summary> 
     /// <param name="sender"></param> 
     /// <param name="e"></param> 
     private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) 
     { 
      if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
      { 
       this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; 
       UpdateSelectedItem(); 
      } 
     } 

     /// <summary> 
     /// generate a ContentPresenter for the selected item 
     /// </summary> 
     private void UpdateSelectedItem() 
     { 
      if (itemsHolder == null) 
      { 
       return; 
      } 

      // generate a ContentPresenter if necessary 
      TabItem item = GetSelectedTabItem(); 
      if (item != null) 
      { 
       CreateChildContentPresenter(item); 
      } 

      // show the right child 
      foreach (ContentPresenter child in itemsHolder.Children) 
      { 
       child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed; 
      } 
     } 

     /// <summary> 
     /// create the child ContentPresenter for the given item (could be data or a TabItem) 
     /// </summary> 
     /// <param name="item"></param> 
     /// <returns></returns> 
     private ContentPresenter CreateChildContentPresenter(object item) 
     { 
      if (item == null) 
      { 
       return null; 
      } 

      ContentPresenter cp = FindChildContentPresenter(item); 

      if (cp != null) 
      { 
       return cp; 
      } 

      // the actual child to be added. cp.Tag is a reference to the TabItem 
      cp = new ContentPresenter(); 
      cp.Content = (item is TabItem) ? (item as TabItem).Content : item; 
      cp.ContentTemplate = this.SelectedContentTemplate; 
      cp.ContentTemplateSelector = this.SelectedContentTemplateSelector; 
      cp.ContentStringFormat = this.SelectedContentStringFormat; 
      cp.Visibility = Visibility.Collapsed; 
      cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); 
      itemsHolder.Children.Add(cp); 
      return cp; 
     } 

     /// <summary> 
     /// Find the CP for the given object. data could be a TabItem or a piece of data 
     /// </summary> 
     /// <param name="data"></param> 
     /// <returns></returns> 
     private ContentPresenter FindChildContentPresenter(object data) 
     { 
      if (data is TabItem) 
      { 
       data = (data as TabItem).Content; 
      } 

      if (data == null) 
      { 
       return null; 
      } 

      if (itemsHolder == null) 
      { 
       return null; 
      } 

      foreach (ContentPresenter cp in itemsHolder.Children) 
      { 
       if (cp.Content == data) 
       { 
        return cp; 
       } 
      } 

      return null; 
     } 
     #endregion 
    } 
} 

Gdzie byś szablon to coś jak to (być może trzeba go rozwinąć do Left/Right TabStripLocation)

<ControlTemplate x:Key="MainTabControlTemplateEx" 
       TargetType="{x:Type controls:TabControlEx}"> 
    <Grid> 

     <Grid.RowDefinitions> 
      <RowDefinition x:Name="row0" Height="Auto"/> 
      <RowDefinition x:Name="row1" Height="4"/> 
      <RowDefinition x:Name="row2" Height="*"/> 
     </Grid.RowDefinitions> 

     <TabPanel x:Name="tabpanel" 
      Background="{StaticResource OutlookButtonHighlight}" 
      Margin="0" 
      Grid.Row="0" 
      IsItemsHost="True" /> 

     <Grid x:Name="divider" 
       Grid.Row="1" Background="Black" 
       HorizontalAlignment="Stretch"/> 

     <Grid x:Name="PART_ItemsHolder" 
       Grid.Row="2"/> 
    </Grid> 
    <!-- no content presenter --> 
    <ControlTemplate.Triggers> 
     <Trigger Property="TabStripPlacement" Value="Top"> 
      <Setter TargetName="tabpanel" Property="Grid.Row" Value="0"/> 
      <Setter TargetName="divider" Property="Grid.Row" Value="1"/> 
      <Setter TargetName="PART_ItemsHolder" Property="Grid.Row" Value="2" /> 
      <Setter TargetName="row0" Property="Height" Value="Auto" /> 
      <Setter TargetName="row1" Property="Height" Value="4" /> 
      <Setter TargetName="row2" Property="Height" Value="*" /> 
     </Trigger> 
     <Trigger Property="TabStripPlacement" Value="Bottom"> 
      <Setter TargetName="tabpanel" Property="Grid.Row" Value="2" /> 
      <Setter TargetName="divider" Property="Grid.Row" Value="1" /> 
      <Setter TargetName="PART_ItemsHolder" Property="Grid.Row" Value="0" /> 
      <Setter TargetName="row0" Property="Height" Value="*" /> 
      <Setter TargetName="row1" Property="Height" Value="4" /> 
      <Setter TargetName="row2" Property="Height" Value="Auto" /> 
     </Trigger> 
    </ControlTemplate.Triggers> 

</ControlTemplate> 

które można użyć takiego

<local:TabControlEx 
     IsSynchronizedWithCurrentItem="True" 
     ItemsSource="{Binding Path=Workspaces}" 
     Template="{StaticResource MainTabControlTemplateEx}"> 
</local:TabControlEx> 

Działa bardzo dobrze i używamy go do doskonałego efektu Dawno już

0

Załaduj zawartość Tab dynamicznie od wyboru, na interfejsie być czuły, należy użyć kodu podobny do poniżej:

private void tab_Selected(object sender, EventArgs e) 
{ 
    //Get the selected tab 
Action loadTab = delegate 
{ 
    LoadSelectedTab(tabItem); 
} 
Dispatcher.BeginInvoke(DispatcherPriority.Background, loadTab); 
} 
public void LoadSelectedTab(TabItem item) 
{ 
    item.Content = new EmployeeTab(); 
    ..... 
} 

repsonse UI będzie bardzo szybka, UI rozpocząć ładowanie bardzo szybki i don zobacz pauzę dla wszelkich opóźnień

Powiązane problemy