[wpf] كيفية الحفاظ على حالة التحكم داخل عناصر علامة التبويب في TabControl



2 Answers

حصلت على حل مع هذه النصيحة WPF TabControl: Turning Off Tab Virtualization في http://www.codeproject.com/Articles/460989/WPF-TabControl-Turning-Off-Tab-Virtualization هذا فئة من TabContent مع خاصية IsCached.

Question

أنا وافد جديد إلى WPF ، في محاولة لبناء مشروع يتبع توصيات مقال جوش سميث الممتاز الذي يصف نموذج عرض نموذج ViewModel .

باستخدام نموذج لشفرة جوش كقاعدة ، قمت بإنشاء تطبيق بسيط يحتوي على عدد من "مساحات العمل" ، كل ممثلة بعلامة تبويب في TabControl. في التطبيق الخاص بي ، تعد مساحة العمل عبارة عن محرر مستندات يتيح إمكانية معالجة مستند هرمي عبر عنصر تحكم TreeView.

على الرغم من أنني نجحت في فتح مساحات عمل متعددة وعرض محتوياتها في عنصر التحكم TreeView المنمنم ، أجد أن TreeView "ينسى" حالته عند التبديل بين علامات التبويب. على سبيل المثال ، إذا تم توسيع TreeView في Tab1 جزئيًا ، فسيتم عرضه على أنه تم تصغيره بالكامل بعد التبديل إلى Tab2 والعودة إلى Tab1. يبدو أن هذا السلوك ينطبق على كافة جوانب حالة التحكم لكافة عناصر التحكم.

بعد بعض التجارب ، أدركت أنه يمكنني الاحتفاظ بالحالة ضمن TabItem من خلال ربط كل خاصية حالة تحكم بشكل صريح بخاصية مخصصة على ViewModel الأساسي. ومع ذلك ، يبدو هذا مثل الكثير من العمل الإضافي ، عندما أريد ببساطة أن تتذكر جميع عناصر التحكم الخاصة بي حالتها عند التبديل بين مساحات العمل.

أفترض أنني أفتقد شيئًا بسيطًا ، لكنني لست متأكدًا من أين تبحث عن الإجابة. أي دليل سيكون محل تقدير.

شكرا ، تيم

تحديث:

كما هو مطلوب ، سأحاول نشر بعض التعليمات البرمجية التي توضح هذه المشكلة. ومع ذلك ، نظرًا لأن البيانات التي تستند إليها TreeView معقدة ، فسوف أنشر مثالًا مبسطًا يعرض نفس symtoms. هنا هو XAML من النافذة الرئيسية:

<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Docs}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <ContentPresenter Content="{Binding Path=Name}" />
        </DataTemplate>
    </TabControl.ItemTemplate>

    <TabControl.ContentTemplate>
        <DataTemplate>
            <view:DocumentView />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

ربط XAML أعلاه بشكل صحيح إلى ObservableCollection DocumentViewModel ، حيث يتم تقديم كل عضو عبر DocumentView.

من أجل بساطة هذا المثال ، قمت بإزالة TreeView (المذكورة أعلاه) من DocumentView واستبدلت مع TabControl التي تحتوي على 3 علامات تبويب ثابتة:

<TabControl>
    <TabItem Header="A" />
    <TabItem Header="B" />
    <TabItem Header="C" />
</TabControl>

في هذا السيناريو ، لا يوجد ربط بين DocumentView و DocumentViewModel. عند تشغيل التعليمات البرمجية ، يتعذر على TabControl الداخلية تذكر التحديد الخاص به عند تبديل TabControl الخارجي.

ومع ذلك ، إذا قمت بوضوح ربط خاصية SelectedIndex الخاصة TabControl الداخلية ...

<TabControl SelectedIndex="{Binding Path=SelectedDocumentIndex}">
    <TabItem Header="A" />
    <TabItem Header="B" />
    <TabItem Header="C" />
</TabControl>

... إلى خاصية وهمية المقابلة على DocumentViewModel ...

public int SelecteDocumentIndex { get; set; }

... علامة التبويب الداخلية قادرة على تذكر اختيارها.

أدرك أنني أستطيع حل مشكلتي بفعالية من خلال تطبيق هذه التقنية على كل خاصية مرئية لكل تحكم ، لكنني أتمنى أن يكون هناك حل أكثر أناقة.




لقد نشرت إجابة عن سؤال مماثل. في حالتي خلقت يدويا خلق TabItems مشكلة إنشاء عرض مرارا وتكرارا. تحقق here




باستخدام فكرة WAF ، جئت إلى هذا الحل البسيط الذي يبدو أنه يحل المشكلة.

يمكنني استخدام سلوك التفاعل ، ولكن يمكن القيام بذلك مع الخاصية المرفقة إذا لم يتم الرجوع إلى مكتبة Interactivity

/// <summary>
/// Wraps tab item contents in UserControl to prevent TabControl from re-using its content
/// </summary>
public class TabControlUcWrapperBehavior 
    : Behavior<UIElement>
{
    private TabControl AssociatedTabControl { get { return (TabControl) AssociatedObject; } }

    protected override void OnAttached()
    {
        ((INotifyCollectionChanged)AssociatedTabControl.Items).CollectionChanged += TabControlUcWrapperBehavior_CollectionChanged;
        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        ((INotifyCollectionChanged)AssociatedTabControl.Items).CollectionChanged -= TabControlUcWrapperBehavior_CollectionChanged;
        base.OnDetaching();
    }

    void TabControlUcWrapperBehavior_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action != NotifyCollectionChangedAction.Add) 
            return;

        foreach (var newItem in e.NewItems)
        {
            var ti = AssociatedTabControl.ItemContainerGenerator.ContainerFromItem(newItem) as TabItem;

            if (ti != null && !(ti.Content is UserControl)) 
                ti.Content = new UserControl { Content = ti.Content };
        }
    }
}

والاستخدام

<TabControl ItemsSource="...">
    <i:Interaction.Behaviors>
        <controls:TabControlUcWrapperBehavior/>
    </i:Interaction.Behaviors>
</TabControl>





Related