2010-08-30 5 views
16

क्या कोई टैब डब्लूपीएफ टैब नियंत्रण में बदलता है जब टैब अनलोड/रीलोड को रोकने का कोई तरीका है? या यदि यह संभव नहीं है, तो टैब सामग्री को कैशिंग करने के लिए एक अनुशंसित विधि है, इसलिए उन्हें प्रत्येक टैब परिवर्तन के साथ पुन: उत्पन्न करने की आवश्यकता नहीं है?डब्ल्यूपीएफ टैबकंट्रोल - टैब चेंज पर अनलोड को रोकना?

उदाहरण के लिए, एक टैब का यूआई पूरी तरह से अनुकूलन योग्य और डेटाबेस में संग्रहीत है। जब उपयोगकर्ता काम करने के लिए ऑब्जेक्ट का चयन करता है, तो अनुकूलित लेआउट में आइटम उस ऑब्जेक्ट के डेटा के साथ पॉप्युलेट हो जाते हैं। उपयोगकर्ता प्रारंभिक लोड पर या डेटा पुनर्प्राप्त करते समय मामूली देरी की अपेक्षा करते हैं, लेकिन जब टैब के बीच आगे और पीछे नहीं बदलते हैं, और टैब बदलते समय देरी बहुत ध्यान देने योग्य होती है।

+0

मुझे नहीं लगता कि जब भी चयनित TabItems रीलोड उतारे जा रहे हैं/टैब नियंत्रण ch में आइटम anges। मुझे यकीन नहीं है, लेकिन हो सकता है कि आपके TabControl का चयन चेंज तर्क बदलना आवश्यक है ताकि यह हर बार डेटाबेस को फिर से क्वेरी न करे? – ASanch

+2

DataTemplates की भरी हुई/उतार घटनाओं आपके आवेदन, जब भी चयनित टैब में परिवर्तन, यह डेटाबेस से संबंध से चलाता है वस्तु की पुन: प्राप्त करने में हर बार जब मैं टैब बदलने (मैं MVVM डिजाइन पैटर्न का उपयोग कर रहा) चलाने मिल – Rachel

+0

तो, डेटा? – ASanch

उत्तर

16

मैं एक समाधान मिला: https://web.archive.org/web/20120429044747/http://eric.burke.name/dotnetmania/2009/04/26/22.09.28

संपादित करें: यह सही किया लिंक है: http://web.archive.org/web/20110825185059/http://eric.burke.name/dotnetmania/2009/04/26/22.09.28

यह मूल रूप से टैब की ContentPresenter और भार संग्रहीत करता कि जब टैब स्विचिंग इसे फिर से निकालने के बजाय। यह तब भी देरी कर रहा था जब टैब को ड्रैग/ड्रॉप करना था क्योंकि यह एक निकालना/जोड़ ऑपरेशन था, हालांकि कुछ संशोधनों के साथ मुझे यह भी पता चला कि (दूर कोड को कम प्रेषक प्राथमिकता पर हटाएं, फिर कोड जोड़ें, इसलिए जोड़ें ऑपरेशन को निकालने का ऑपरेशन रद्द करने का मौका था और एक नया ड्राइंग करने के बजाए पुराने सामग्री प्रदाता का उपयोग करने का मौका था)

संपादित करें: उपरोक्त लिंक अब काम नहीं करता है, इसलिए मैं यहां कोड की एक प्रति पेस्ट कर दूंगा। ड्रैगिंग/ड्रॉप करने की अनुमति देने के लिए इसे थोड़ा सा संशोधित किया गया है, लेकिन इसे अभी भी वैसे ही काम करना चाहिए।

// 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://eric.burke.name/dotnetmania/2009/04/26/22.09.28 
// 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; 
    } 
} 
+1

मुझे लगता है कि स्टैक ओवरफ्लो लिंक के मार्कडाउन को गलत तरीके से पार्स कर रहा है। यह काम करता है अगर आप पूरे यूआरएल की प्रतिलिपि/पेस्ट करते हैं (या \ [\] मार्कडाउन का उपयोग न करें)। http://web.archive.org/web/20110825185059/http://eric.burke.name/dotnetmania/2009/04/26/22.09.28 – skst

+1

मैं, अपने समाधान का उपयोग करने के कोशिश कर रहा हूँ के रूप में मुझे लगता है कि सही समस्या का समाधान लगता है। हालांकि, मुझे नहीं पता कि नौकरी को कैसे खत्म किया जाए ... परिवर्तनीय '_itemsHolder' हमेशा 'शून्य' है, उदाहरण के लिए। नियंत्रण मेरे एक्सएएमएल में है और सब ठीक से प्रदर्शित होता है, लेकिन क्या एक्सएएमएल में एक विशेष संदर्भ है कि मुझे अंतर को पुल करने की ज़रूरत है? – DonBoitnott

+0

@ डॉनबॉटनॉट किस बिंदु पर यह शून्य है? आपने कहा कि यह सही तरीके से काम करता है, इसलिए इसे किसी बिंदु पर आबादी मिलनी चाहिए। – Rachel

2

बस इस में जोड़ने के लिए, मैं एक ऐसी ही समस्या थी और उस के पीछे कोड में एक टैब आइटम की सामग्री का प्रतिनिधित्व एक उपयोगकर्ता नियंत्रण कैशिंग द्वारा इसे हल करने में कामयाब रहे।

मेरी प्रोजेक्ट में मेरे पास एक टैब नियंत्रण है जो संग्रह (एमवीवीएम) से जुड़ा हुआ है। हालांकि पहला टैब एक सिंहावलोकन है जो सूची दृश्य में अन्य सभी टैब का सारांश दिखाता है। मेरी समस्या यह थी कि जब भी कोई उपयोगकर्ता किसी आइटम टैब से ओवरव्यू टैब पर अपना चयन चलाता है, तो ओवरव्यू सभी सारांश डेटा के साथ फिर से खींचा जाता है, जो संग्रह में आइटम्स की संख्या के आधार पर 10-15 सेकंड ले सकता है। (ध्यान दें कि डीबी या किसी भी चीज़ से वास्तविक डेटा का कोई पुनः लोडिंग नहीं है, यह पूरी तरह से सारांश दृश्य का चित्रण है जो समय ले रहा था)।

जो मैं चाहता था वह सारांश दृश्य की लोडिंग के लिए केवल तब होता है जब डेटा संदर्भ पहले लोड हो जाता है और टैब के बीच किसी भी बाद स्विचिंग तत्काल हो।

समाधान:

वर्ग शामिल: MainWindow.xaml - मुख्य पृष्ठ टैब नियंत्रण से युक्त। MainWindow.xaml.cs - उपरोक्त के लिए कोड पीछे। MainWindowViewModel.cs - उपरोक्त दृश्य के लिए मॉडल देखें, संग्रह शामिल है। Overview.xaml - उपयोगकर्ता नियंत्रण जो ओवरव्यू टैब आइटम सामग्री खींचता है। अवलोकन ViewModel.cs - उपरोक्त दृश्य के लिए मॉडल देखें।

कदम:

  1. 'MainWindow में DataTemplate बदलें।XAML 'है कि नाम के एक खाली उपयोगकर्ता नियंत्रण के साथ अवलोकन टैब आइटम ड्रॉ' OverviewPlaceholder MainWindowViewModel.cs अवलोकन 'में'

  2. करने के लिए एक स्थिर संदर्भ जोड़ें '' के भीतर सार्वजनिक 'OverviewViewModel'

  3. के संदर्भ बनाओ ' 'MainWindow.xaml.cs'

  4. उपयोगकर्ता नियंत्रण से भरी हुई घटना के लिए एक ईवेंट हैंडलर जोड़ें 'OverviewPlaceholder', इस विधि के भीतर 'अवलोकन' करने के लिए केवल अगर यह रिक्त है, इस संदर्भ के DataContext सेट स्थिर संदर्भ का दृष्टांत वर्तमान DataContext के भीतर 'OverviewViewModel' संदर्भ (कि 'MainWindowViewModel' है) करने के लिए और जगह धारक की सामग्री सेट 'अवलोकन' के लिए स्थिर संदर्भ होने के लिए।

क्योंकि हर बार भरी हुई है (अर्थात उपयोगकर्ता सिंहावलोकन टैब पर क्लिक करता है) अब अवलोकन पृष्ठ केवल एक बार तैयार की है, यह पृष्ठ पर वापस पहले ही प्रदान की, स्थिर उपयोगकर्ता नियंत्रण रखता है।

0

टैब परिवर्तन पर टैब रीलोड से बचने के लिए मेरे पास वास्तव में एक सरल समाधान है, सामग्री सामग्री के बजाय टैब इटैम में सामग्री का उपयोग करें।

जैसे (MVVM शैली में)

 <TabItem Header="Tab1" Content="{Binding Tab1ViewModel}" /> 

की जगह द्वारा

 <TabItem Header="Tab1"> 
      <ContentPresenter Content="{Binding Tab1ViewModel}" /> 
     </TabItem> 
+0

यह उत्तर क्यों उठाया गया है? 'सामग्री प्रस्तुतकर्ता' को इसकी सामग्री सहित टैब स्विचिंग पर अनलोड किया जाएगा। यह कुछ भी नहीं करता है। – Sinatr