在WPF中相对于TabControl内容区域放置overlay元素

时间:2015-06-30 14:09:56

标签: c# wpf overlay tabcontrol

这就是我想要实现的目标:

在某些情况下,窗口中的所有其他元素顶部都应显示叠加层。这部分正在运作。

Windows中的主要容器是TabControl,并且叠加层的边距足够大,可以显示在标题标题下方(内容区域所在的位置)。

当标签标题跨越两行时(或字体大小发生变化时),问题就开始了。有没有办法将叠加层相对于TabControl的内容部分开始的区域放置?

3 个答案:

答案 0 :(得分: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为完整功能示例。

答案 1 :(得分:1)

另一种方法可能是创建一个TabControl的自定义实现,该实现公开一个新的依赖项属性:TabPanelActualHeight。

这假设您使用的是默认控件模板,但也适用于包含TabPanel的任何模板(TabPanel不是TabControl模板的必需部分)。

image/png         png;

注意:此示例取决于this SO post中的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; } } 扩展方法。

您现在可以简单地绑定到自定义TabControl的TabPanelActualHeight属性,以帮助您偏移/定位叠加层。

答案 2 :(得分: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}" >
...

使用值转换器为叠加层创建边距或在视图模型中完成此操作。