دع ارتفاع WPF Tabcontrol يفترض ارتفاع أكبر عنصر؟
-
21-08-2019 - |
سؤال
هل هناك أي طريقة تجعل التحكم في علامة التبويب يأخذ حجم أكبر عنصر في علامة التبويب (حسنًا، في الواقع، محتوى عنصر التبويب)؟
نظرًا لعدم تخصيص حجم معين لعنصر التحكم في علامة التبويب، فيجب ضبط حجمه تلقائيًا:يقوم بذلك بشكل صحيح، ولكن عندما تقوم بالتبديل بين علامات التبويب، فإنه يتغير حجمه تلقائيًا إلى ارتفاع (وعرض) محتويات علامة التبويب المحددة حاليًا.
لا أريد أن يتم تغيير الحجم، ودع عنصر التحكم في علامة التبويب يتولى ارتفاع العنصر الأكبر، مع الاستمرار في ضبط حجمه تلقائيًا.
أي أدلة؟لقد حاولت ربط البيانات بـ Height
خاصية كل عنصر يستخدم كمحتوى لاستخدام الارتباط المتعدد، مع الارتباطات على كليهما ActualHeight
و ال Items
خصائص Tabcontrolولكن للأسف، ActualHeight
من عناصر المحتوى دائمًا 0.
<TabItem Header="Core" >
<Grid Margin="5">
<Grid.Height>
<MultiBinding Converter="{Converters1:AllTabContentEqualHeightConverter}">
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
<Binding Path="Items" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
</MultiBinding>
</Grid.Height>
...
هل يمكن هذا؟
المحلول 3
في الواقع، كان حل هذا الأمر أسهل على ما اعتقدت.منذ أن كان لدي قالب تحكم لـ TabControl
على أية حال، قمت بتعيين ارتفاع ContentPresenter
تقديم محتوى علامة التبويب المحددة.أفعل ذلك باستخدام محول يرتبط بعناصر ملف TabControl
, ، يقيسها إذا لزم الأمر (باستخدام Measure
) والشيكات DesiredSize
للحجم الذي أحتاجه.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var items = value as ItemCollection;
if (items == null)
return null;
double max = 0;
foreach (TabItem item in items)
{
var content = item.Content as FrameworkElement;
if (content == null) continue;
if (!content.IsMeasureValid)
content.Measure(new Size(int.MaxValue, int.MaxValue));
var height = content.DesiredSize.Height;
if (max < height)
max = height;
}
return max;
}
هذا يعمل بشكل جيد، مع بعض التحذيرات:
- يجب أن يكون كل محتوى علامة تبويب
FrameworkElement
- لا يتغير حجم المحتويات بمجرد تحميلها (لأن المحول يتم استدعاؤه فقط عندما تتغير خاصية العناصر، أي مرة واحدة فقط).
نصائح أخرى
ونعم يمكن القيام به: إعادة استخدام الشبكة، rowdefinitions مقابل كل-tabitem
مثال:
<TabControl Grid.IsSharedSizeScope="True">
<TabItem Header="Tab 1">
<Grid >
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="xxx"/>
</Grid.RowDefinitions>
</Grid>
</TabItem>
<TabItem Header="Tab 2">
<Grid >
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="xxx"/>
</Grid.RowDefinitions>
</Grid>
</TabItem>
</TabControl>
المشكلة هي أن TabControl
يقوم بتفريغ وإعادة تحميل محتواه أثناء تبديل علامات التبويب.ولذلك فهو يعرف فقط حجم المحتوى الموجود في علامة التبويب النشطة حاليًا.يجب أن تكون قادرًا على تغيير TabControl بحيث يكون كذلك أبداً يدمر أطفاله، وهم موجودون دائمًا (لكن ربما مخفيين).
هذا بلوق وظيفة بواسطة إريك بيرك يجب أن تبدأ.مما يمكنني قوله من خلال تصفح منشوره، ستحتاج إلى تغييره بحيث:
- يتم تحميل جميع الأطفال عند
TabControl
محمل. - يتم إخفاء الأطفال بدلاً من الانهيار عندما يكونون غير نشطين
وانها على الارجح ليس بالطريقة المناسبة WPF، ولكن، إذا كان لديك بالفعل كل عناصر المحتوى، هل يمكن ربما حلقة من خلالهم على الحمل وتعيين ذروة TabControl
برمجيا.
وهذا عمل بالنسبة لي بالتزامن مع نهج Grid.IsSharedSizeScope
هو مبين أعلاه.
ملاحظة يستخدم هذا SetCurrentValue
بدلا من مجرد إعداد الخاصية SelectedIndex
- وبهذه الطريقة نحافظ على الارتباطات المحتملة الحالية:
private void TabControl_OnLoaded(object sender, RoutedEventArgs e)
{
//NOTE: loop through tab items to force measurement and size the tab control to the largest tab
TabControl tabControl = (TabControl)sender;
// backup selection
int indexItemLast = tabControl.SelectedIndex;
int itemCount = tabControl.Items.Count;
for (
int indexItem = (itemCount - 1);
indexItem >= 0;
indexItem--)
{
tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItem);
tabControl.UpdateLayout();
}
// restore selection
tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItemLast);
}