2015-06-30 2 views
0

Вот что я хочу добиться:Размещение наложения элемента относительно области содержимого TabControl в WPF

При определенных обстоятельствах должно быть наложение видна поверх всех других элементов в окне. Эта часть работает.

Основным контейнером в Windows является TabControl, а наложение имеет размер, достаточно большой для отображения под заголовками вкладок (где находится область содержимого).

Проблема начинается, когда заголовки вкладок охватывают две строки (или при изменении размера шрифта). Есть ли способ разместить наложение относительно области, где начнется часть содержимого TabControl?

ответ

1

Предлагаю вам взглянуть на вставку вашего наложения в TabControl ControlTemplate, так что он станет частью визуального дерева, в которое нарисован TabItem, и соответственно изменит размер.

Это чрезвычайно упрощенный пример для ясности:

<ControlTemplate TargetType="{x:Type TabControl}"> 
    <StackPanel> 
     <TabPanel /> 
     <Grid> 

      <ContentPresenter x:Name="ContentTop" 
         HorizontalAlignment="{TemplateBinding HorizontalAlignment}" 
         VerticalAlignment="{TemplateBinding VerticalAlignment}" 
         Margin="{TemplateBinding Padding}" 
         Cursor="{TemplateBinding Cursor}" /> 

      <YourOverlayControl x:Name="OverlayControl"></YourOverlayControl> 

     </Grid> 
    </StackPanel> 
</ControlTemplate> 

Главное в том, что элемент управления наложением представлена ​​на верхней части ContentPresenter (в пределах сеточного управления). ContentPresenter в этом шаблоне будет отображать ваш контент TabItem.

Если вы еще не определили пользовательский шаблон управления для TabControl, check out this article on MSDN для полнофункционального примера.

+0

Ваше решение работает, но для меня это немного «слишком много» для этой простой задачи. Тем более, что я не смог найти шаблон управления TabControl по умолчанию для .net 4.5. – Onur

+0

Проблема в том, что нет простого способа привязки к TabPanel внутри TabControl, чтобы убедиться, что это фактическая высота ... чего вы действительно хотите достичь? Другой подход может заключаться в создании пользовательской реализации TabControl, которая предоставляет высоту TabPanel. Я добавлю еще один ответ. – olitee

1

Другим подходом может быть создание пользовательской реализации TabControl, которая предоставляет новое свойство зависимостей: TabPanelActualHeight.

Предполагается, что вы используете шаблон управления по умолчанию, но также должны работать для любого шаблона, который включает TabPanel (TabPanel не является обязательной частью шаблона TabControl).

public class CustomTabControl : TabControl 
{ 
    private static readonly DependencyPropertyKey ReadOnlyPropPropertyKey 
          = DependencyProperty.RegisterReadOnly("ReadOnlyProp", typeof(double), typeof(CustomTabControl), 
           new FrameworkPropertyMetadata((double)0, 
            FrameworkPropertyMetadataOptions.None)); 

    public static readonly DependencyProperty TabPanelActualHeightProperty = ReadOnlyPropPropertyKey.DependencyProperty; 

    public double TabPanelActualHeight 
    { 
     get { return (double)GetValue(TabPanelActualHeightProperty); } 
     protected set { SetValue(ReadOnlyPropPropertyKey, value); } 
    } 

    private TabPanel _tabPanel = null; 

    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 

     _tabPanel = this.GetChildOfType<TabPanel>(); 

     if (_tabPanel != null) 
     { 
      TabPanelActualHeight = _tabPanel.ActualHeight; 
      _tabPanel.SizeChanged += TabPanelOnSizeChanged; 
     } 

    } 

    private void TabPanelOnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs) 
    { 
     TabPanelActualHeight = _tabPanel.ActualHeight; 
    } 
} 

Примечание: Этот пример зависит от метода GetChildOfType<T> расширения от this SO post.

Теперь вы можете просто привязать свойство TabPanelActualHeight настраиваемого TabControl, чтобы помочь вам смещать/позиционировать оверлей.

+0

Спасибо за этот ответ. Это вдохновило меня создать прикрепленное свойство, которое примерно так же, как и свойство зависимостей в производном классе. – Onur

+0

Ах, да, вложенное свойство - еще один способ пойти. Спасибо, что поделились своим решением. – olitee

0

Вдохновленный this answer Я создал прикрепленное свойство, которое позволяет мне извлечь высоту панели вкладок. Я использую это значение для создания поля, необходимого для моего наложения. Я предпочитаю эту версию, потому что

  • не должны создать новый стиль/изменить стиль по умолчанию
  • Не нужно создать новый элемент управления только для одного маленького аспекта

Вот код для прикрепленного свойства.

public static class TabPanelHeaderHeight 
{ 
    private const double InitialValue = -1.0; 

    private static readonly DependencyProperty HeaderHeightProperty = 
     DependencyProperty.RegisterAttached(name: "HeaderHeight", propertyType: typeof(double), ownerType: typeof(TabPanelHeaderHeight), 
     defaultMetadata: new FrameworkPropertyMetadata(defaultValue: InitialValue, flags: FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, propertyChangedCallback: OnHeaderHeightChanged)); 

    public static double GetHeaderHeight(UIElement element) 
    { 
     return (double)element.GetValue(HeaderHeightProperty); 
    } 
    public static void SetHeaderHeight(UIElement element, double value) 
    { 
     element.SetValue(HeaderHeightProperty,value); 
    } 

    private static void OnHeaderHeightChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     var target = obj as TabControl; 
     if (target == null) 
      return; 

     // we hijack the on value changed event to register our event handler to the value we're really interested in 
     // but we want to do this only once, so we check if the value is the (invalid) initial value and change the value afterwards to register the event listener only once. 
     if ((double)args.OldValue == InitialValue) 
     { 
      var tp = target.GetChildOfType<TabPanel>(); 

      tp.SizeChanged += (sender, eventArgs) => { TargetSizeChanged(target,tp); }; 
      TargetSizeChanged(target,tp); 
     } 
    } 

    private static void TargetSizeChanged(TabControl target, TabPanel tp) 
    { 
     SetHeaderHeight(target, tp.ActualHeight); 
    } 

    public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject 
    { 
     if (depObj == null) return null; 

     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 
     { 
      var child = VisualTreeHelper.GetChild(depObj, i); 

      var result = (child as T) ?? GetChildOfType<T>(child); 
      if (result != null) return result; 
     } 
     return null; 
    } 

} 

GetChildOfType помощник от this post

Он может быть использован в XAML как это:

<TabControl yourNs:TabPanelHeaderHeight.HeaderHeight="{Binding Path=HeaderHeight}" > 
... 

Либо использовать преобразователь значений, чтобы создать запас для наложения или сделать это в посмотреть модель.

Смежные вопросы