WPF保存选项卡控制状态 [英] WPF preserve Tab control state

查看:411
本文介绍了WPF保存选项卡控制状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看了帖子@的如何停止WPF自定义的Tabcontrol卸载选项卡上的变化可视树,但我无法得到它的工作,我必须有错过了什么。请帮助。谢谢



我使用类TabControlEx从样本项目@的 http://www.pluralsight-training.net/community/blogs/eburke/存档/ 2009/04/30 /保存最WPF的标签控制 - 从摧毁其通children.aspx

 <窗​​口x:类=MyMainwindow
的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns:X =HTTP ://schemas.microsoft.com/winfx/2006/xaml
的xmlns:sharedC =CLR的命名空间:myShared.Converter;装配= myShared
的xmlns:mainTab =CLR的命名空间:MyWPFLib .View标题={结合标题}
HEIGHT =600WIDTH =850TOP =223左=164ResizeMode =CanResize闭幕=的windowClosing
WindowStyle =工具窗口>
<网格和GT;
< mainTab:TabControlEx IsSynchronizedWithCurrentItem =真
Grid.Row =0
保证金=10 0 5 5
X:NAME =MainTabRegion
TabStripPlacement =评出的
的SelectedIndex ={结合Tabs.SelectedIndex}
的ItemsSource ={结合Tabs.TabItems}
>
< TabControl.ItemContainerStyle>
<风格的TargetType ={X:类型TabItem的}>
< setter属性=标题>
< Setter.Value>
<绑定路径=头文件/>
< /Setter.Value>
< /二传手>
< setter属性=能见度
值={结合可见性,模式=单向,转换器= {StaticResource的boolToVis}}/>
< /样式和GT;
< /TabControl.ItemContainerStyle>
< / mainTab:TabControlEx>
< /网格和GT;
< /窗GT;
$ B为maintab $ B数据源是Tabs.TabItems

公众的ObservableCollection< ITabControl>的TabItems
{
得到
{
返回_items;
}
}

每个TabItem的是建立与



 <数据类型的DataTemplate ={X:类型VM:Item1ViewModel}> 
<大众:视图1 />
< / DataTemplate中>


解决方案

我使用的代码从该链接所有的时间不一个问题,虽然我注意到,因为当我第一次拿到了代码的网站已经改变。你可能要检查的我有什么,什么网站有任何代码的差异



下面是我得到了他们改变了网站之前的代码:

  //扩展的TabControl这样你就不会得到
//卸载的性能损失和重装的VisualTree时节省显示的项目切换标签

//从http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-获得从毁灭其通children.aspx
//,并提出了一些修改,因此做拖/放操作

[TemplatePart(NAME =PART_ItemsHolder时重用TabItem的的ContentPresenter,类型= typeof运算(面板))]
公共类TabControlEx:System.Windows.Controls.TabControl
{
//保存所有项目,但只标记当前选项卡的项目为可见
私人面板_itemsHolder = NULL;

// Temporaily持有删除项目的情况下,这是一个拖/放操作
私有对象_deletedObject = NULL;

公共TabControlEx()
:基地()
{
//这是必要的,这样我们得到的初步数据绑定所选项目
this.ItemContainerGenerator .StatusChanged + = ItemContainerGenerator_StatusChanged;
}

///<总结>
///如果容器完成后,产生所选项目
///< /总结>
///< PARAM NAME =发件人>< /参数>
///< PARAM NAME =E>< /参数>
无效ItemContainerGenerator_StatusChanged(对象发件人,EventArgs e)如
{
(this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
this.ItemContainerGenerator.StatusChanged - = ItemContainerGenerator_StatusChanged;
UpdateSelectedItem();
}
}

///<总结>
///得到ItemsHolder和产生任何儿童
///< /总结>
公共覆盖无效OnApplyTemplate()
{
base.OnApplyTemplate();
_itemsHolder = GetTemplateChild(PART_ItemsHolder)作为面板;
UpdateSelectedItem();
}

///<总结>
///当项目改变我们删除任何生成的面板的孩子和添加任何新的必要的
///< /总结>
///< PARAM NAME =E>< /参数>
保护覆盖无效OnItemsChanged(NotifyCollectionChangedEventArgs E)
{
base.OnItemsChanged(E);

如果(_itemsHolder == NULL)
{
的回报;
}

开关(e.Action)
{
情况下NotifyCollectionChangedAction.Reset:
_itemsHolder.Children.Clear();

如果(base.Items.Count大于0)
{
base.SelectedItem = base.Items [0];
UpdateSelectedItem();
}

中断;

情况下NotifyCollectionChangedAction.Add:
情况下NotifyCollectionChangedAction.Remove:

//搜索造成的拖/放操作
如果最近删除的项目( e.NewItems = NULL&放大器;!&安培;!_deletedObject = NULL)
{
的foreach(VAR在e.NewItems项)
{
如果(_deletedObject ==项目)
{
//如果新的项是作为最近删除的一个(即一个拖/放事件)
//然后取消删除并重新使用ContentPresenter所以它不具有相同的为
//重绘。我们确实需要演示(使用标签)
ContentPresenter CP = FindChildContentPresenter(_deletedObject)虽然链接到新的项目;
如果(CP!= NULL)
{
INT指数= _itemsHolder.Children.IndexOf(CP);

(_itemsHolder.Children [指数]的ContentPresenter).TAG =
(产品TabItem的)?项目:(this.ItemContainerGenerator.ContainerFromItem(项目));
}
_deletedObject = NULL;
}
}
}

如果(e.OldItems!= NULL)
{
的foreach(VAR在e.OldItems项)
{

_deletedObject =项目;

//我们希望在稍后的优先级的情况下,运行此此
//是一个拖/放操作,使我们可以重新使用模板
this.Dispatcher。的BeginInvoke(DispatcherPriority.DataBind,
新动作(委托()
{
如果(_deletedObject = NULL)
{
ContentPresenter CP = FindChildContentPresenter(_deletedObject); $! b $ b如果(CP!= NULL)
{
this._itemsHolder.Children.Remove(CP);
}
}
}
) );
}
}

UpdateSelectedItem();
中断;

情况下NotifyCollectionChangedAction.Replace:
抛出新NotImplementedException(替换尚未实现);
}
}

///<总结>
///更新的ItemsHolder
LT的可视子///&; /总结>
///< PARAM NAME =E>< /参数>
保护覆盖无效OnSelectionChanged(SelectionChangedEventArgs E)
{
base.OnSelectionChanged(E);
UpdateSelectedItem();
}

///<总结>
///产生所选项目
///℃的ContentPresenter; /总结>
无效UpdateSelectedItem()
{
如果(_itemsHolder == NULL)
{
的回报;
}

//生成如有必要,
TabItem的项目= GetSelectedTabItem()一个ContentPresenter;
如果(项目!= NULL)
{
CreateChildContentPresenter(项目);
}

//显示右子
的foreach(ContentPresenter孩子_itemsHolder.Children)
{
child.Visibility =((child.Tag作为TabItem的).IsSelected)? Visibility.Visible:Visibility.Collapsed;
}
}

///<总结>
///创建子ContentPresenter为给定的项目(可以是数据或TabItem的)
///< /总结>
///< PARAM NAME =项>< /参数>
///<&回报GT;< /回报>
ContentPresenter CreateChildContentPresenter(对象的项目)
{
如果(项目== NULL)
{
返回NULL;
}

ContentPresenter CP = FindChildContentPresenter(项目);

如果(CP!= NULL)
{
返回CP;
}

//实际儿童要添加。 cp.Tag是对TabItem的
CP =新ContentPresenter基准();
cp.Content =(产品TabItem的)? (项目作为TabItem的).Content:项目;
cp.ContentTemplate = this.SelectedContentTemplate;
cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
cp.ContentStringFormat = this.SelectedContentStringFormat;
cp.Visibility = Visibility.Collapsed;
cp.Tag =(产品TabItem的)?项目:(this.ItemContainerGenerator.ContainerFromItem(项目));
_itemsHolder.Children.Add(CP);
返回CP;
}

///<总结>
///查找给定对象的CP。数据可能是一个TabItem的或一块数据
的///< /总结>
///< PARAM NAME =数据>< /参数>
///<&回报GT;< /回报>
ContentPresenter FindChildContentPresenter(对象数据)
{
如果(数据TabItem的)
{
数据=(数据截至TabItem的).Content;
}

如果(数据== NULL)
{
返回NULL;
}

如果(_itemsHolder == NULL)
{
返回NULL;
}

的foreach(在_itemsHolder.Children ContentPresenter CP)
{
如果(cp.Content ==数据)
{
返回CP;
}
}

返回NULL;
}

///<总结>
///从TabControl的复制;希望它是在类,而不是私人
///<保护; /总结>
///<&回报GT;< /回报>
保护TabItem的GetSelectedTabItem()
{
对象将selectedItem = base.SelectedItem;
如果(selectedItem设置== NULL)
{
返回NULL;
}

如果(_deletedObject ==将selectedItem)
{

}

TabItem的项目= selectedItem设置为TabItem的;
如果(项目== NULL)
{
项= base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex)作为TabItem的;
}
回报率的项目;
}
}



我也做了一些改变原来的代码,因为我拖着/下探标签和删除项目并重新添加它时,不希望重新创建的标签,但老实说它已经这么久,我忘了什么部分是我的,什么是不。



我做了一个快速测试,仔细检查了,是它让你的非绑定值,如选择或扩大值。



修改



在响应低于您的意见,提出的DataTemplates在你的资源地方定义用于每个选项卡哪个用户控件。当的TabControl试图绘制每个选项卡的内容,它会使用你的数据类型

 中所定义任何的DataTemplate; Window.Resources> ; 
< D​​ataTemplate中的TargetType ={X:类型本地:TabAViewModel}>
<局部:TabAView />
< / DataTemplate中>
< D​​ataTemplate中的TargetType ={X:类型本地:TabBViewModel}>
<局部:TabBView />
< / DataTemplate中>
< /Window.Resources>

<的TabControl的ItemsSource ={结合的TabItems}>
< TabControl.ItemContainerStyle>
<风格的TargetType ={X:类型TabItem的}>
< setter属性=标题VALUE ={结合头}/>
< /样式和GT;
< /TabControl.ItemContainerStyle>
< / TabControl的>



视图模型......

 公共MyViewModelConstructor()
{
TabItems.Add(新TabAViewModel {头=选项卡中});
TabItems.Add(新TabBViewModel {头=标签B});
}



编辑#2



文字XAML,因为你要求

 <局部:TabControlEx> 
< TabItem的标题=Test1的>
<数据网格的ItemsSource ={结合测试}>
< D​​ataGrid.Columns>
< D​​ataGridTextColumn绑定={结合TestValue}标题=测试/>
< /DataGrid.Columns>
< / DataGrid的>
< / TabItem的>
< TabItem的标题=Test2的>
<数据网格的ItemsSource ={结合测试}>
< D​​ataGrid.Columns>
< D​​ataGridTextColumn绑定={结合TestValue}标题=测试/>
< /DataGrid.Columns>
< / DataGrid的>
< / TabItem的>
< /地方:TabControlEx>


I have read the posts @ How to stop Wpf Tabcontrol to unload Visual tree on Tab change, but I could not get it work, I must have missed something. Pls help. thanks

I am using class TabControlEx from sample project @ http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its-children.aspx

<Window x:Class="MyMainwindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:sharedC="clr-namespace:myShared.Converter;assembly=myShared"
    xmlns:mainTab="clr-namespace:MyWPFLib.View" Title="{Binding Title}"        
    Height="600" Width="850" Top="223" Left="164" ResizeMode="CanResize" Closing="WindowClosing"
    WindowStyle="ToolWindow">
<Grid>
 <mainTab:TabControlEx IsSynchronizedWithCurrentItem="True"
                        Grid.Row="0"  
                        Margin="10 0 5 5"
                        x:Name="MainTabRegion"  
                        TabStripPlacement="Top" 
                        SelectedIndex="{Binding Tabs.SelectedIndex}" 
                        ItemsSource="{Binding Tabs.TabItems}"                         
                        >
                        <TabControl.ItemContainerStyle>
                            <Style TargetType="{x:Type TabItem}">
                                <Setter Property="Header">
                                    <Setter.Value>
                                        <Binding Path="Header"/>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Visibility" 
                                Value="{Binding IsVisible, Mode=OneWay, Converter={StaticResource boolToVis}}"/>
                            </Style>
                        </TabControl.ItemContainerStyle>
                    </mainTab:TabControlEx>
</Grid>
</Window>

Datasource for the maintab is Tabs.TabItems

    public ObservableCollection<ITabControl> TabItems
    {
        get
        {
            return _items;
        }
    }

each TabItem is built with

<DataTemplate DataType="{x:Type vm:Item1ViewModel}">
    <vw:View1 />
</DataTemplate>

解决方案

I use the code from that link all the time without a problem, although I notice that the website has changed since when I first got the code. You may want to check for any code differences between what I have and what the website has.

Here's the code I got before they changed the site:

// Extended TabControl which saves the displayed item so you don't get the performance hit of 
// unloading and reloading the VisualTree when switching tabs

// Obtained from http://www.pluralsight-training.net/community/blogs/eburke/archive/2009/04/30/keeping-the-wpf-tab-control-from-destroying-its-children.aspx
// and made a some modifications so it reuses a TabItem's ContentPresenter when doing drag/drop operations

[TemplatePart(Name = "PART_ItemsHolder", Type = typeof(Panel))]
public class TabControlEx : System.Windows.Controls.TabControl
{
    // Holds all items, but only marks the current tab's item as visible
    private Panel _itemsHolder = null;

    // Temporaily holds deleted item in case this was a drag/drop operation
    private object _deletedObject = null;

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

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

    /// <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();

                if (base.Items.Count > 0)
                {
                    base.SelectedItem = base.Items[0];
                    UpdateSelectedItem();
                }

                break;

            case NotifyCollectionChangedAction.Add:
            case NotifyCollectionChangedAction.Remove:

                // Search for recently deleted items caused by a Drag/Drop operation
                if (e.NewItems != null && _deletedObject != null)
                {
                    foreach (var item in e.NewItems)
                    {
                        if (_deletedObject == item)
                        {
                            // If the new item is the same as the recently deleted one (i.e. a drag/drop event)
                            // then cancel the deletion and reuse the ContentPresenter so it doesn't have to be 
                            // redrawn. We do need to link the presenter to the new item though (using the Tag)
                            ContentPresenter cp = FindChildContentPresenter(_deletedObject);
                            if (cp != null)
                            {
                                int index = _itemsHolder.Children.IndexOf(cp);

                                (_itemsHolder.Children[index] as ContentPresenter).Tag =
                                    (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));
                            }
                            _deletedObject = null;
                        }
                    }
                }

                if (e.OldItems != null)
                {
                    foreach (var item in e.OldItems)
                    {

                        _deletedObject = item;

                        // We want to run this at a slightly later priority in case this
                        // is a drag/drop operation so that we can reuse the template
                        this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
                            new Action(delegate()
                        {
                            if (_deletedObject != null)
                            {
                                ContentPresenter cp = FindChildContentPresenter(_deletedObject);
                                if (cp != null)
                                {
                                    this._itemsHolder.Children.Remove(cp);
                                }
                            }
                        }
                        ));
                    }
                }

                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>
    /// generate a ContentPresenter for the selected item
    /// </summary>
    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>
    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>
    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;
    }

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

        if (_deletedObject == selectedItem)
        { 

        }

        TabItem item = selectedItem as TabItem;
        if (item == null)
        {
            item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;
        }
        return item;
    }
}

I did make some changes to the original code because I was dragging/dropping tabs and didn't want to re-create the tabs when removing an item and re-adding it, but honestly it's been so long that I forget what part is mine and what isn't.

I did a quick test to double-check, and yes it does keep your non-bound values such as selected or expanded values.

EDIT

In response to your comments below, put DataTemplates in your Resources somewhere that define which UserControl to use for each Tab. When the TabControl tries to draw each Tab's Content, it will use whatever DataTemplate you defined for that data type

<Window.Resources>
    <DataTemplate TargetType="{x:Type local:TabAViewModel}">
        <local:TabAView />
    </DataTemplate>
    <DataTemplate TargetType="{x:Type local:TabBViewModel}">
        <local:TabBView />
    </DataTemplate>
</Window.Resources>

<TabControl ItemsSource="{Binding TabItems}">
    <TabControl.ItemContainerStyle> 
        <Style TargetType="{x:Type TabItem}"> 
            <Setter Property="Header" Value="{Binding Header}" />
        </Style> 
    </TabControl.ItemContainerStyle>
</TabControl>

ViewModel...

public MyViewModelConstructor()
{
    TabItems.Add(New TabAViewModel { Header = "Tab A" });
    TabItems.Add(New TabBViewModel { Header = "Tab B" });
}

EDIT #2

Text XAML, as you requested

<local:TabControlEx>
    <TabItem Header="Test1">
        <DataGrid ItemsSource="{Binding Test}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding TestValue}" Header="Test" />
            </DataGrid.Columns>
        </DataGrid>
    </TabItem>
    <TabItem Header="Test2">
        <DataGrid ItemsSource="{Binding Test}">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding TestValue}" Header="Test" />
            </DataGrid.Columns>
        </DataGrid>
    </TabItem>
</local:TabControlEx>

这篇关于WPF保存选项卡控制状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆