Search code examples
wpftemplatesxamlribbontitle

Center WPF RibbonWindow Title via XAML Code


I've found some info on StackOverflow regarding my problem, so I introduced the following XAML code to my window. Now everything is fine, while the WPF window hasn't quick launch icons or contextual tabs active.

Is there a way to center the application title completely via XAML Code.

<ribbon:Ribbon.TitleTemplate>
    <DataTemplate>
        <TextBlock TextAlignment="Center" HorizontalAlignment="Stretch"
            Width="{Binding ElementName=Window, Path=ActualWidth}">ApplicationTitle
            <TextBlock.Effect>
               <DropShadowEffect ShadowDepth="0" Color="MintCream " BlurRadius="10"/>   
            </TextBlock.Effect>
        </TextBlock>
    </DataTemplate>           
</ribbon:Ribbon.TitleTemplate>

Solution

  • Here's a very naïve way to do it. It comes about from inspecting the visual tree of a RibbonWindow and its concomitant Ribbon. I've been playing with this code for a couple of hours (and no longer) -- it's a bit rough around the edges and I'm not sure it's completely bug free. There's some optimizations to be made and it should be noted that I suck at WPF; there might be better way to do things.

    For what it's worth the code is below, but note first:

    • The references to the PART_Icon template are not directly related to your question, but it is related to the aesthetics of the window.

    • The references to IsWin8OrHigher and FindChild are in classes that I'll include at the end. My interest in Windows 8 is that the native ribbon library centres the title text, whereas earlier versions of Windows do not. I'm trying to emulate that here.

    • I have no idea how the RibbonWindow was shipped with Visual Studio 2012 in its current iteration. The rendering on Windows 8 looks pretty miserable. After all this, I'm tempted to overload TitleTemplate with a TextBlock to get rid of the default glow and leave it at that.

    • The RibbonWindow doesn't look very good maximized, customization or not.

    When I started writing this code, this was approximately what I was aiming for:

    enter image description here

    For comparison, this is how the RibbonWindow renders itself with no customisation:

    enter image description here

    This is how it renders with TitleTemplate defined to a TextBlock with TextAlignment="Center" but otherwise without any fancy text effects:

    enter image description here

    With the code below, we get this result:

    enter image description here

    MainWindow:

    public partial class MainWindow {
        public MainWindow() {
            InitializeComponent();
            if (Environment.OSVersion.IsWin8OrHigher()) {
                SizeChanged += (sender, args) => TitleHack();
                Activated += (sender, args) => TitleHack();
            }
        }
    
        public override void OnApplyTemplate() {
            base.OnApplyTemplate();
            if (!Environment.OSVersion.IsWin8OrHigher())
                return;
            var icon = GetTemplateChild("PART_Icon") as Image;
            if (icon == null)
                return;
            icon.Margin = new Thickness(icon.Margin.Left + 3, icon.Margin.Top + 2,
                                        icon.Margin.Right, icon.Margin.Bottom);
        }
    
        private void TitleHack() {
            var ribbonTitlePanel = MyRibbon.FindChild<FrameworkElement>("PART_TitlePanel");
            var qatTopHost = MyRibbon.FindChild<FrameworkElement>("QatTopHost");
            var titleHost = MyRibbon.FindChild<FrameworkElement>("PART_TitleHost");
            var tabGroup = MyRibbon.FindChild<FrameworkElement>("PART_ContextualTabGroupItemsControl");
    
            var qatTopHostLeft = qatTopHost.TransformToAncestor(ribbonTitlePanel).Transform(new Point(0, 0)).X;
            var tabGroupLeft = tabGroup.TransformToAncestor(ribbonTitlePanel).Transform(new Point(0, 0)).X;
    
            var width = ribbonTitlePanel.ActualWidth;
    
            if (tabGroup.Visibility == Visibility.Visible) {
                width -= tabGroup.ActualWidth;
                width -= tabGroupLeft - qatTopHostLeft;
            } else {
                width -= qatTopHost.ActualWidth;
            }
    
            if (ResizeMode != ResizeMode.NoResize && WindowStyle != WindowStyle.None)
                width -= 48; // For the min and max buttons
    
            titleHost.Width = width > 0 ? width : Double.NaN;
        }
    }
    

    OperatingSystemExtensionMethods.cs:

    public static class OperatingSystemExtensionMethods {
        private static readonly Version Windows8Version = new Version(6, 2);
    
        public static bool IsWin8OrHigher(this OperatingSystem that) {
            if (that.Platform != PlatformID.Win32NT)
                return false;
            return that.Version.CompareTo(Windows8Version) >= 0;
        }
    }
    

    DependencyObjectExtensionMethods.cs:

    public static class DependencyObjectExtensionMethods {
        public static T FindChild<T>(this DependencyObject that, string elementName)
            where T : FrameworkElement {
            var childrenCount = VisualTreeHelper.GetChildrenCount(that);
    
            for (var i = 0; i < childrenCount; i++) {
                var child = VisualTreeHelper.GetChild(that, i);
                var frameworkElement = child as FrameworkElement;
    
                if (frameworkElement != null && elementName == frameworkElement.Name)
                    return (T) frameworkElement;
    
                if ((frameworkElement = frameworkElement.FindChild<T>(elementName)) != null)
                    return (T) frameworkElement;
            }
            return null;
        }
    }