Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在WPF中预呈现TabItem上的控件?_C#_Wpf_Tabcontrol_Tabitem - Fatal编程技术网

C# 如何在WPF中预呈现TabItem上的控件?

C# 如何在WPF中预呈现TabItem上的控件?,c#,wpf,tabcontrol,tabitem,C#,Wpf,Tabcontrol,Tabitem,C#XBap应用程序 我有一个TabControl,其中有四个TabItems。其中两个选项卡项仅包含WPFToolkit中的DataGrid,它从SQL Server数据库中提取一个相当小的数据表(100行4列)。我的问题是,当我加载我的应用程序并单击其中一个包含datagrid的选项卡项时。在我看来,在它使标签成为焦点之前,有2-3秒的停顿。这仅在第一次单击选项卡时发生。这似乎是datagrid的呈现 如何在应用程序加载时使这些选项卡预渲染,以便用户单击选项卡时,在选项卡显示之前不会出现2-

C#XBap应用程序

我有一个TabControl,其中有四个TabItems。其中两个选项卡项仅包含WPFToolkit中的DataGrid,它从SQL Server数据库中提取一个相当小的数据表(100行4列)。我的问题是,当我加载我的应用程序并单击其中一个包含datagrid的选项卡项时。在我看来,在它使标签成为焦点之前,有2-3秒的停顿。这仅在第一次单击选项卡时发生。这似乎是datagrid的呈现

如何在应用程序加载时使这些选项卡预渲染,以便用户单击选项卡时,在选项卡显示之前不会出现2-3秒的初始暂停


感谢

问题不在于获取数据(您可以在单独的线程中预加载数据),而在于在datagrid中构建可视项


如果您使用Snoop进行检查,您可以看到有很多可视项,如果您不需要所有的datagrid功能,您可以使用更简单的表示法(ListView/ItemsControl/Custom)

我们使用标准的WPF选项卡控件,问题是每次更改SelectedItem时都会破坏VisualTree

我们最终做的是创建一个特殊的TabControl(我称之为TabControlEx),它保持所有项目的呈现,但选择简单地显示/隐藏TabItems的contentpresenter

这是相关代码

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
    }
}
使用系统;
使用System.Collections.Specialized;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Controls.Primitives;
名称空间MVVM.Demo
{
/// 
///标准的WPF TabControl非常糟糕,因为它只有
///甚至在VisualTree中包含当前选项卡项,因此如果
///具有复杂视图每个选项卡重新创建视图需要一段时间
///选择更改。这使得标准选项卡控件对
///使用.此类及其关联的ControlTemplate
///允许所有选项卡项保留在VisualTree中,而不使其粘滞。
///它通过在VisualTree中保留所有TabItem内容来实现这一点,但是
///隐藏所有非活动的TabItem内容,并仅保留活动的TabItem
///显示的内容。
/// 
[TemplatePart(Name=“PART_ItemsHolder”,Type=typeof(Panel))]
公共类TabControlEx:TabControl
{
#区域数据
私有面板itemsHolder=null;
#端区
#区域导体
公共TabControlEx()
:base()
{
//这是必要的,以便我们获得初始数据绑定的选定项
this.ItemContainerGenerator.StatusChanged+=ItemContainerGenerator\u StatusChanged;
this.Loaded+=TabControlEx\u Loaded;
}
#端区
#区域公共/受保护方法
/// 
///获取ItemsHolder并生成所有子项
/// 
应用程序模板()上的公共重写无效
{
base.OnApplyTemplate();
itemsHolder=GetTemplateChild(“PART_itemsHolder”)作为面板;
UpdateSelectedItem();
}
/// 
///当项目更改时,我们将删除所有生成的面板子项,并根据需要添加任何新的子项
/// 
/// 
已更改受保护的覆盖(NotifyCollectionChangedEventArgs e)
{
碱基(e);
if(itemsHolder==null)
{
返回;
}
开关(电动)
{
案例通知CollectionChangedAction.Reset:
itemsHolder.Children.Clear();
打破
案例NotifyCollectionChangedAction。添加:
案例NotifyCollectionChangedAction。删除:
如果(例如,OldItems!=null)
{
foreach(e.OldItems中的var项)
{
ContentPresenter cp=FindChildContentPresenter(项目);
如果(cp!=null)
{
itemsHolder.Children.Remove(cp);
}
}
}
//不要对新项目做任何事情,因为我们不想这样做
//创建未显示的视觉效果
UpdateSelectedItem();
打破
案例通知收集更改操作。替换:
抛出新的NotImplementedException(“替换尚未实现”);
}
}
/// 
///更新ItemsHolder中的可见子项
/// 
/// 
选择更改时受保护的覆盖无效(SelectionChangedEventArgs e)
{
基础。选举变更(e);
UpdateSelectedItem();
}
/// 
///从TabControl复制;希望它在该类中受到保护,而不是私有的
/// 
/// 
受保护的选项卡项GetSelectedTabItem()
{
object selectedItem=base.selectedItem;
如果(selectedItem==null)
{
返回null;
}
TabItem item=选择editem作为TabItem;
如果(项==null)
{
item=base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex)作为TabItem;
}
退货项目;
}
#端区
#区域私有方法
/// 
///在某些情况下,我们需要在加载时进行更新,以防
///ApplyTemplate发生在数据绑定之前。
/// 
/// 
/// 
已加载私有无效选项卡ControlEx_(对象发送方,路由目标)
{
UpdateSelectedItem();
}
/// 
///如果容器已完成,则生成所选项目
/// 
/// 
/// 
普里瓦特
<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>
<local:TabControlEx 
        IsSynchronizedWithCurrentItem="True" 
        ItemsSource="{Binding Path=Workspaces}" 
        Template="{StaticResource MainTabControlTemplateEx}">
</local:TabControlEx>
 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();
  .....
}
    /// <summary>
    /// There was a flaky issue when first tab was uninitialized
    /// </summary>
    /// <param name="oldValue"></param>
    /// <param name="newValue"></param>
    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);
        UpdateSelectedItem();
    }