所选选项卡更改时WPF TabItem状态丢失

所选选项卡更改时WPF TabItem状态丢失,wpf,xaml,mvvm,textbox,focus,Wpf,Xaml,Mvvm,Textbox,Focus,我有一个带有多个选项卡的TabControl。每个选项卡中都有一个多行文本框 因此,在选项卡1的可见文本框中,插入点从第1行开始 我将插入点移动到第2行的末尾 我将所选选项卡更改为选项卡2,然后返回到选项卡1 选项卡1文本框中的插入点始终位于第一行的开头 两个选项卡都由列表中的viewmodels填充: <TabControl ItemsSource="{Binding TabItemViewModelList}"> <TabControl.Reso

我有一个带有多个选项卡的
TabControl
。每个选项卡中都有一个多行
文本框

因此,在选项卡1的可见文本框中,插入点从第1行开始

我将插入点移动到第2行的末尾

我将所选选项卡更改为选项卡2,然后返回到选项卡1

选项卡1
文本框中的插入点始终位于第一行的开头

两个选项卡都由列表中的viewmodels填充:

<TabControl ItemsSource="{Binding TabItemViewModelList}">
    <TabControl.Resources>
        <DataTemplate DataType="TabItemViewModel">
            <Grid>
                <TextBox Text="{Binding Text}" />
            </Grid>
        </DataTemplate>
    </TabControl.Resources>
<TabControl>

这是简化的,但不是很多

这是因为每次我更改所选项目时,它都会重新实例化模板,还是诸如此类?除了痛苦地维护ViewModel中的每一点UI状态之外,还有其他方法可以解决这个问题吗

更新
我是对的,这是虚拟化错误,设计者可能认为这是一个特性。当然,他疯了:选项卡控件不是列表框。在你还没有为虚拟化准备足够的物品之前,它就完全不可用了。

在你的案例中,每次文本被重新初始化时,carret位置都会被更新。您可以在文本框上放置一个行为,将其设置为焦点结束。。。请参阅以下内容:

答案似乎是伊万·克里夫亚科夫的,在这个问题中找到的:。这不是他问题的答案,但却是我问题的答案

在XAML中看起来像这样:

<TabControl
    xmlns:ikriv="clr-namespace:IKriv.Windows.Controls.Behaviors"
    ikriv:TabContent.IsCached="True"
    ItemsSource="{Binding DocumentList.Documents}"
    SelectedItem="{Binding DocumentList.ActiveDocument}"
    />

下面是Krivyakov解决方案的完整源代码,以防CodeProject被小行星击中:

// TabContent.cs, version 1.2
// The code in this file is Copyright (c) Ivan Krivyakov
// See http://www.ikriv.com/legal.php for more information
//
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;

/// <summary>
/// http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization
/// </summary>
namespace IKriv.Windows.Controls.Behaviors
{
    /// <summary>
    /// Attached properties for persistent tab control
    /// </summary>
    /// <remarks>By default WPF TabControl bound to an ItemsSource destroys visual state of invisible tabs. 
    /// Set ikriv:TabContent.IsCached="True" to preserve visual state of each tab.
    /// </remarks>
    public static class TabContent
    {
        public static bool GetIsCached(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsCachedProperty);
        }

        public static void SetIsCached(DependencyObject obj, bool value)
        {
            obj.SetValue(IsCachedProperty, value);
        }

        /// <summary>
        /// Controls whether tab content is cached or not
        /// </summary>
        /// <remarks>When TabContent.IsCached is true, visual state of each tab is preserved (cached), even when the tab is hidden</remarks>
        public static readonly DependencyProperty IsCachedProperty =
            DependencyProperty.RegisterAttached("IsCached", typeof(bool), typeof(TabContent), new UIPropertyMetadata(false, OnIsCachedChanged));


        public static DataTemplate GetTemplate(DependencyObject obj)
        {
            return (DataTemplate)obj.GetValue(TemplateProperty);
        }

        public static void SetTemplate(DependencyObject obj, DataTemplate value)
        {
            obj.SetValue(TemplateProperty, value);
        }

        /// <summary>
        /// Used instead of TabControl.ContentTemplate for cached tabs
        /// </summary>
        public static readonly DependencyProperty TemplateProperty =
            DependencyProperty.RegisterAttached("Template", typeof(DataTemplate), typeof(TabContent), new UIPropertyMetadata(null));


        public static DataTemplateSelector GetTemplateSelector(DependencyObject obj)
        {
            return (DataTemplateSelector)obj.GetValue(TemplateSelectorProperty);
        }

        public static void SetTemplateSelector(DependencyObject obj, DataTemplateSelector value)
        {
            obj.SetValue(TemplateSelectorProperty, value);
        }

        /// <summary>
        /// Used instead of TabControl.ContentTemplateSelector for cached tabs
        /// </summary>
        public static readonly DependencyProperty TemplateSelectorProperty =
            DependencyProperty.RegisterAttached("TemplateSelector", typeof(DataTemplateSelector), typeof(TabContent), new UIPropertyMetadata(null));

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static TabControl GetInternalTabControl(DependencyObject obj)
        {
            return (TabControl)obj.GetValue(InternalTabControlProperty);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void SetInternalTabControl(DependencyObject obj, TabControl value)
        {
            obj.SetValue(InternalTabControlProperty, value);
        }

        // Using a DependencyProperty as the backing store for InternalTabControl.  This enables animation, styling, binding, etc...
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static readonly DependencyProperty InternalTabControlProperty =
            DependencyProperty.RegisterAttached("InternalTabControl", typeof(TabControl), typeof(TabContent), new UIPropertyMetadata(null, OnInternalTabControlChanged));


        [EditorBrowsable(EditorBrowsableState.Never)]
        public static ContentControl GetInternalCachedContent(DependencyObject obj)
        {
            return (ContentControl)obj.GetValue(InternalCachedContentProperty);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void SetInternalCachedContent(DependencyObject obj, ContentControl value)
        {
            obj.SetValue(InternalCachedContentProperty, value);
        }

        // Using a DependencyProperty as the backing store for InternalCachedContent.  This enables animation, styling, binding, etc...
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static readonly DependencyProperty InternalCachedContentProperty =
            DependencyProperty.RegisterAttached("InternalCachedContent", typeof(ContentControl), typeof(TabContent), new UIPropertyMetadata(null));

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static object GetInternalContentManager(DependencyObject obj)
        {
            return (object)obj.GetValue(InternalContentManagerProperty);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public static void SetInternalContentManager(DependencyObject obj, object value)
        {
            obj.SetValue(InternalContentManagerProperty, value);
        }

        // Using a DependencyProperty as the backing store for InternalContentManager.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InternalContentManagerProperty =
            DependencyProperty.RegisterAttached("InternalContentManager", typeof(object), typeof(TabContent), new UIPropertyMetadata(null));

        private static void OnIsCachedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (obj == null) return;

            var tabControl = obj as TabControl;
            if (tabControl == null)
            {
                throw new InvalidOperationException("Cannot set TabContent.IsCached on object of type " + args.NewValue.GetType().Name +
                    ". Only objects of type TabControl can have TabContent.IsCached property.");
            }

            bool newValue = (bool)args.NewValue;

            if (!newValue)
            {
                if (args.OldValue != null && ((bool)args.OldValue))
                {
                    throw new NotImplementedException("Cannot change TabContent.IsCached from True to False. Turning tab caching off is not implemented");
                }

                return;
            }

            EnsureContentTemplateIsNull(tabControl);
            tabControl.ContentTemplate = CreateContentTemplate();
            EnsureContentTemplateIsNotModified(tabControl);
        }

        private static DataTemplate CreateContentTemplate()
        {
            const string xaml =
                "<DataTemplate><Border b:TabContent.InternalTabControl=\"{Binding RelativeSource={RelativeSource AncestorType=TabControl}}\" /></DataTemplate>";

            var context = new ParserContext();

            context.XamlTypeMapper = new XamlTypeMapper(new string[0]);
            context.XamlTypeMapper.AddMappingProcessingInstruction("b", typeof(TabContent).Namespace, typeof(TabContent).Assembly.FullName);

            context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            context.XmlnsDictionary.Add("b", "b");

            var template = (DataTemplate)XamlReader.Parse(xaml, context);
            return template;
        }

        private static void OnInternalTabControlChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (obj == null) return;
            var container = obj as Decorator;

            if (container == null)
            {
                var message = "Cannot set TabContent.InternalTabControl on object of type " + obj.GetType().Name +
                    ". Only controls that derive from Decorator, such as Border can have a TabContent.InternalTabControl.";
                throw new InvalidOperationException(message);
            }

            if (args.NewValue == null) return;
            if (!(args.NewValue is TabControl))
            {
                throw new InvalidOperationException("Value of TabContent.InternalTabControl cannot be of type " + args.NewValue.GetType().Name +", it must be of type TabControl");
            }

            var tabControl = (TabControl)args.NewValue;
            var contentManager = GetContentManager(tabControl, container);
            contentManager.UpdateSelectedTab();
        }

        private static ContentManager GetContentManager(TabControl tabControl, Decorator container)
        {
            var contentManager = (ContentManager)GetInternalContentManager(tabControl);
            if (contentManager != null)
            {
                /*
                 * Content manager already exists for the tab control. This means that tab content template is applied 
                 * again, and new instance of the Border control (container) has been created. The old container 
                 * referenced by the content manager is no longer visible and needs to be replaced
                 */
                contentManager.ReplaceContainer(container);
            }
            else
            {
                // create content manager for the first time
                contentManager = new ContentManager(tabControl, container);
                SetInternalContentManager(tabControl, contentManager);
            }

            return contentManager;
        }

        private static void EnsureContentTemplateIsNull(TabControl tabControl)
        {
            if (tabControl.ContentTemplate != null)
            {
                throw new InvalidOperationException("TabControl.ContentTemplate value is not null. If TabContent.IsCached is True, use TabContent.Template instead of ContentTemplate");
            }
        }

        private static void EnsureContentTemplateIsNotModified(TabControl tabControl)
        {
            var descriptor = DependencyPropertyDescriptor.FromProperty(TabControl.ContentTemplateProperty, typeof(TabControl));
            descriptor.AddValueChanged(tabControl, (sender, args) =>
                {
                    throw new InvalidOperationException("Cannot assign to TabControl.ContentTemplate when TabContent.IsCached is True. Use TabContent.Template instead");
                });
        }

        public class ContentManager
        {
            TabControl _tabControl;
            Decorator _border;

            public ContentManager(TabControl tabControl, Decorator border)
            {
                _tabControl = tabControl;
                _border = border;
                _tabControl.SelectionChanged += (sender, args) => { UpdateSelectedTab(); };
            }

            public void ReplaceContainer(Decorator newBorder)
            {
                if (Object.ReferenceEquals(_border, newBorder)) return;

                _border.Child = null; // detach any tab content that old border may hold
                _border = newBorder;
            }

            public void UpdateSelectedTab()
            {
                _border.Child = GetCurrentContent();
            }

            private ContentControl GetCurrentContent()
            {
                var item = _tabControl.SelectedItem;
                if (item == null) return null;

                var tabItem = _tabControl.ItemContainerGenerator.ContainerFromItem(item);
                if (tabItem == null) return null;

                var cachedContent = TabContent.GetInternalCachedContent(tabItem);
                if (cachedContent == null)
                {
                    cachedContent = new ContentControl 
                    { 
                        DataContext = item,
                        ContentTemplate = TabContent.GetTemplate(_tabControl), 
                        ContentTemplateSelector = TabContent.GetTemplateSelector(_tabControl)
                    };

                    cachedContent.SetBinding(ContentControl.ContentProperty, new Binding());
                    TabContent.SetInternalCachedContent(tabItem, cachedContent);
                }

                return cachedContent;
            }
        }
    }
}
//TabContent.cs,版本1.2
//此文件中的代码是版权所有(c)Ivan Krivyakov
//看http://www.ikriv.com/legal.php 更多信息
//
使用制度;
使用系统组件模型;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Markup;
/// 
/// http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization
/// 
命名空间IKriv.Windows.Controls.Behaviors
{
/// 
///永久选项卡控件的附加属性
/// 
///默认情况下,绑定到ItemsSource的WPF TabControl会破坏不可见选项卡的可视状态。
///设置ikriv:TabContent.IsCached=“True”以保留每个选项卡的可视状态。
/// 
公共静态类TabContent
{
公共静态bool GetIsCached(DependencyObject obj)
{
返回(bool)对象获取值(IsCachedProperty);
}
公共静态void SetIsCached(DependencyObject对象,布尔值)
{
对象设置值(IsCachedProperty,value);
}
/// 
///控制是否缓存选项卡内容
/// 
///当TabContent.IsCached为true时,即使选项卡处于隐藏状态,每个选项卡的可视状态也会被保留(缓存)
公共静态只读从属属性IsCachedProperty=
DependencyProperty.RegisterAttached(“IsCached”、typeof(bool)、typeof(TabContent)、新UIPropertyMetadata(false、OnIsCachedChanged));
公共静态数据模板GetTemplate(DependencyObject obj)
{
返回(DataTemplate)对象GetValue(TemplateProperty);
}
公共静态void SetTemplate(DependencyObject对象,DataTemplate值)
{
对象设置值(TemplateProperty,value);
}
/// 
///用于代替缓存选项卡的TabControl.ContentTemplate
/// 
公共静态只读DependencyProperty TemplateProperty=
DependencyProperty.RegisterAttached(“模板”、typeof(DataTemplate)、typeof(TabContent)、新UIPropertyMetadata(null));
公共静态DataTemplateSelector GetTemplateSelector(DependencyObject obj)
{
返回(DataTemplateSelector)对象获取值(TemplateSelectorProperty);
}
公共静态void SetTemplateSelector(DependencyObject对象,DataTemplateSelector值)
{
对象设置值(TemplateSelectorProperty,值);
}
/// 
///用于代替缓存选项卡的TabControl.ContentTemplateSelector
/// 
公共静态只读从属属性TemplateSelectorProperty=
RegisterAttached(“TemplateSelector”、typeof(DataTemplateSelector)、typeof(TabContent)、new-UIPropertyMetadata(null));
[EditorBrowsable(EditorBrowsableState.Never)]
公共静态TabControl GetInternalTabControl(DependencyObject obj)
{
返回(TabControl)对象GetValue(InternalTabControlProperty);
}
[EditorBrowsable(EditorBrowsableState.Never)]
公共静态无效SetInternalTabControl(DependencyObject对象,TabControl值)
{
对象设置值(InternalTabControlProperty,值);
}
//使用DependencyProperty作为InternalTabControl的后台存储。这将启用动画、样式、绑定等。。。
[EditorBrowsable(EditorBrowsableState.Never)]
公共静态只读DependencyProperty InternalTabControlProperty=
DependencyProperty.RegisterAttached(“InternalTabControl”、typeof(TabControl)、typeof(TabContent)、新UIPropertyMetadata(null,OnInternalTabControlChanged));
[EditorBrowsable(EditorBrowsableState.Never)]
公共静态内容控件GetInternalCachedContent(DependencyObject obj)
{
return(ContentControl)obj.GetValue(InternalCachedContentProperty);
}
[EditorBrowsable(EditorBrowsableState.Never)]
公共静态void SetInternalCachedContent(DependencyObject对象,ContentControl值)
{
对象设置值(InternalCachedContentProperty,值);
}
//使用DependencyProperty作为InternalCachedContent的后备存储。这将启用动画、样式、绑定等。。。
[EditorBrowsable(EditorBrowsableState.Never)]