2010-02-03 25 views
7

C# Aplicación XBap¿Cómo preparo los controles en un TabItem en WPF?

Tengo un TabControl con cuatro TabItems en él. Dos de estos TabItems simplemente contienen un DataGrid del WPFToolkit que extrae una datatable bastante pequeña (100 filas por 4 columnas) desde una base de datos SQL Server. Mi problema es que cuando cargo mi aplicación y hago clic en uno de los TabItems que contienen una cuadrícula de datos. Me parece una pausa de 2-3 segundos antes de enfocar esa pestaña. Esto solo ocurre la primera vez que se hace clic en la pestaña. Parece ser la representación de la cuadrícula de datos.

¿Cómo puedo hacer que esas pestañas se realicen antes de que se cargue la aplicación para que cuando un usuario hace clic en una pestaña, no haya esa pausa inicial de 2-3 segundos antes de que aparezca la pestaña?

Gracias

+0

Lo que sería aún mejor si las pestañas pre render en un hilo separado * después * la aplicación ha cargado –

+0

He intentado utilizar un hilo que hizo la llamada a la base de datos y la actualización de los artículos para el Sin embargo, todavía recibo esa pausa inicial la primera vez que se hace clic en la pestaña. –

+0

¿Estás haciendo una llamada Async DB o una de sincronización? ¿También cómo está uniendo la cuadrícula de datos a la tabla? si está usando un lector de datos, puede intentar hacer un rendimiento de retorno – Wegged

Respuesta

0

El problema no está recibiendo los datos (se puede cargar previamente que en un hilo separado), pero en realidad la construcción de los elementos visuales en la cuadrícula de datos.

Si pasa con Snoop se puede ver que hay una gran cantidad de elementos visuales, si no necesita toda la funcionalidad de cuadrícula de datos que podría ir con una representación más simple (ListView/ItemsControl/Usuario)

6

Nosotros use el WPF TabControl estándar y el problema es que destruye el VisualTree cada vez que cambia el SelectedItem.

Lo que terminamos haciendo fue crear un TabControl especial (lo llamé TabControlEx) que mantiene todos los ítems prestados pero elige simplemente mostrar/ocultar los ContentPresenters para los Ítems.

Este es el código correspondiente

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 
    } 
} 

donde usted la plantilla que algo como esto (es posible que necesite para expandirlo para Left/TabStripLocation derecha)

<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> 

que se podría utilizar como esto

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

Funciona muy bien y lo hemos estado utilizando con gran efecto para un mucho tiempo

0

Cargar el contenido Tab de forma dinámica en la selección, para la interfaz de usuario que sea sensible, utilice código similar a continuación:

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

El repsonse interfaz de usuario será muy rápido, la interfaz de usuario de empezar a cargar muy rápido y Don 't ver pausa para cualquier retraso

Cuestiones relacionadas