2016-09-23 8 views
0

私はTabControlとというタブアイテムのMVVMセットアップを持っています。TabItemがすぐに読み込まれない

は、私は、ファイルを開いて、TabItemにそのファイルで作られたモデルをロード:それはHeaderContentだため

var model = new ViewModel(data, filename); 
ViewModels.Tabs.Add(model); 

TabItemはDataTemplateをしています。
Contentは別のUserControlで定義されており、Headerはメインファイル自体にあります。

ヘッダーが表示されますが、ヘッダーをクリックしたときにのみ、タブアイテムのロードされたイベントと内容が表示されます。

私はそれがすぐに読み込まれると思ったのですが、なぜですか?

私は同時に2つのタブを追加します。

var model = new ViewModel(data, filename); 
ViewModels.Tabs.Add(model); 
ViewModels.Tabs.Add(model); 

その後、最初のタブのロードされたイベントは、火災を行い、それがコンテンツが表示されています。

希望の動作を達成するにはどうすればよいですか?

+0

これは、仮想化の事であるに。他の通常の問題は、TabItemが選択されていないときに視覚状態が失われることです。ソリューションは仮想化を取り除くことです。 'ObservableCollection'を' ItemsSource'にバインドすると、コレクションの各項目に 'TabItem'が追加されるように、実際にコードを書いてください。私が個人的に保証することのできないcodeprojectのバージョンがあります:http://stackoverflow.com/a/36209166/424129 –

+0

修正:私はそれを個人的に保証することができます。私は昨年も同じことをしたことを思い出しました。プロジェクトをチェックしただけで、彼のコードを使いました。それはうまく動作します。 –

+0

私は、複雑なものを参照してください。まあ、おそらく 'ViewModels.Tabs.Add(null);を追加するだけです。回避策としてViewModels.Tabs.Remove(null);を使用します。 – Gerard

答えて

1

これは仮想化によるものです。いずれのSelectorサブクラスと同様に、可視アイテムのみが実際に存在します。 TabControlでは、表示されるのは選択したアイテムだけです。私はそれがタブコントロールの最も一般的な使用のための理想的な設計選択ではなかったと思うが、ここにある。

私が見つけた最善の解決策は、入り込む別のプロパティを追加し、各アイテムの実際のTabItemItemsSourceに作成することです。 this woefully unappreciated answerから、私はthis CodeProject thing by Ivan Krivyakovを見つけました。私はそれを使用し、それは動作します。

<TabControl 
    xmlns:ikriv="clr-namespace:IKriv.Windows.Controls.Behaviors" 
    ikriv:TabContent.IsCached="True" 

これはC#コードの285行ですが、インターネット上のものはなくなります。ここでは、次のとおりです。

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

あなたはタブコントロールのSelectedItemプロパティにバインドする必要がありますし、あなたのviewmodelにあなたのコレクションのどんな要素を指していることが必要です欲しいものを達成するために。それは次のようになります。 XAML

<TabControl ItemsSource="{Binding Items}" SelectedItem="{Binding Item}"> 
</TabControl> 

のViewModel

public ViewModel() { 
     SelectedItem = Items.First(); 
    } 

public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<Item> { 
     new Item("test1", 5), 
     new Item("test2", 2) 
    }; 

public Item SelectedItem { get; set; } //don't forget to implement ChangeNotifications for it 
+0

私はすでにこれを行い、それは選択のために働く。しかし、私の場合は少なくとも、ヘッダーをクリックするまで、タブアイテムは読み込まれません。 – Gerard

+0

あなたが何を意味するのか分かりません。 SelectedItemへのバインディングが正しく設定されている場合は、そのヘッダをクリックする必要なしに、即座に選択したタブが表示されます。新しいWPFプロジェクトを作成してそこでテストしてみてください。たぶんあなたのコードにロードするのを防ぐ何かがあります... – 3615

+0

選択したタブは実際にはすぐに表示されますが、ヘッダをクリックする必要はなく、残りのタブコントロールの内容は空です。私はその内容を読み込むためにヘッダーをクリックしなければなりません。あなたのタブアイテムがデータグリッドのようなコンテンツを持っている場合、これらのデータグリッドがすぐに表示されるかどうか試してみてください。 – Gerard

関連する問題