Diederik Krols

The XAML Brewer

A Show-All-Or-Nothing Behavior for Windows Universal Apps

Universal apps need to have a responsive UI that adapts to hugely differing form factors. This article describes how to build a Border control that hides its content when it’s too large to be fully displayed on the screen. It is implemented as a behavior, since –unlike in WPF- all native WinRT and Windows Phone controls are sealed. Originally, I had built a HideOnTrimTextBlock: a TextBlock that displays nothing if the text becomes too wide to display. The calculation was triggered when the TextBlock’s size changed. I soon discovered two things:

  • the SizeChanged event for a TextBlock is often swallowed by its parent, so you have to walk up the visual tree to hook event handlers there, and
  • the code used only members of FrameworkElement, so it was applicable to more than just TextBlock controls.

So I turned the HideOnTrimTextBlock into a ShowAllOrNothingBorder.

To detect whether the content of an element is too wide to be displayed, we compare its DesiredSize (don’t forget to Measure first) with its rendered ActualWidth.

I implemented this test as an extension method:

public static class FrameworkElementExtensions
{
    /// <summary>
    /// Returns whether or not the content of the element is too wide to be displayed entirely.
    /// </summary>
    public static bool IsContentTooWide(this FrameworkElement element)
    {
        element.Measure(new Size(double.MaxValue, double.MaxValue));
        return element.DesiredSize.Width > (element.ActualWidth + 1);
    }
}

I only check the Width here, feel free to bring the Height into the equation.

The ShowAllOrNothing Universal Behavior implements IBehavior and applies to any Border. In the SizeChanged we verify whether the content is too wide or not, and adjust the Opacity of the border's Child (alternatively you could play on the Visibility). Here’s the whole behavior:

/// <summary>
/// A behavior that makes a Border's content disappear when it doesn't entirely fit the screen.
/// </summary>
public class ShowAllOrNothingBehavior : DependencyObject, IBehavior
{
    private Border border;

    public DependencyObject AssociatedObject
    {
        get { return this.border; }
    }

    public void Attach(DependencyObject associatedObject)
    {
        if (associatedObject is Border)
        {
            this.border = associatedObject as Border;
            this.border.SizeChanged += this.Border_SizeChanged;
        }
        else
        {
            throw new Exception("ShowAllOrNothingBehavior Behavior only applies to Border.");
        };
    }

    private void Border_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (this.border.IsContentTooWide())
        {
            this.border.Child.Opacity = 0;
        }
        else
        {
            this.border.Child.Opacity = 1;
        }
    }

    public void Detach()
    {
        if (this.border != null)
        {
            this.border.SizeChanged -= this.Border_SizeChanged;
        }
    }
}

Here’s how to attach the behavior in XAML:

<Page ...
      xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
      xmlns:behaviors="using:U2UC.WinUni.Behaviors"
      ...>

<Border>
    <interactivity:Interaction.Behaviors>
        <behaviors:ShowAllOrNothingBehavior />
    </interactivity:Interaction.Behaviors>
    <TextBlock Text="I'm sorry, Dave. I'm afraid I can't do that." />
</Border>

Here’s a screen shot of the attached sample app. It illustrates a number of text block responsiveness options, such as word wrapping, character trimming, word trimming, and shrinking. The ShowAllOrNothing behavior is attached to the last one:

app_1

The slider at the bottom of the screen determines the width of the text blocks’ parent. When you slide it to the left you’ll see the last text immediately disappear when it’s touched by the red line:

app_2

In some use cases it makes sense to entirely hide a control if it doesn’t fit the screen. In the following screen shot, I believe that none of the text blocks actually produce any useful output:

app_3

Since it’s a Universal app, it’s also supposed to work on the phone:

phone_1 phone_2

All the code is sitting in the Shared project of a Universal App solution. The Windows 8.1 and Windows Phone 8.1 apps just need to reference their own platform-specific Behaviors SDK:

UniversalBehaviorSolution

You may want to implement this behavior in a Portable Class Library instead. In that case I suggest you first read this article by Joost van Schaik.

For the sake of completeness, here’s how this code would look like in WPF. Since the control classes are not sealed, we can put everything in a Border subclass:

// Wpf Control.
namespace WpfApplication
{
    using System.Windows;
    using System.Windows.Controls;

    /// <summary>
    /// A Border that makes its content disappear when it doesn't entirely fit the screen.
    /// </summary>
    public class ShowAllOrNothingBorder : Border
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ShowAllOrNothingBorder"/> class.
        /// </summary>
        public ShowAllOrNothingBorder()
        {
            this.SizeChanged += ShowAllOrNothingBorder_SizeChanged;
        }

        /// <summary>
        /// Determines whether content is too wide.
        /// </summary>
        public bool IsContentTooWide()
        {
            this.Measure(new Size(double.MaxValue, double.MaxValue));
            return this.DesiredSize.Width > (this.ActualWidth + 1);
        }

        private void ShowAllOrNothingBorder_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (IsContentTooWide())
            {
                this.Child.Opacity = 0;
            }
            else
            {
                this.Child.Opacity = 1;
            }
        }
    }
}

Here’s the full source code, it was written in Visual Studio 2013 Update 2: U2UC.WinUni.Behavior.zip (132.2KB)

Enjoy!

XAML Brewer

Comments (7) -

  • Else Stanhope

    9/24/2014 3:29:22 AM | Reply

    Hello, i think that i saw you visited my website so i came to “return the favor”.I am attempting to find things to improve my web site!I suppose its ok to use a few of your ideas!!

  • vp-corp

    10/6/2014 5:55:03 PM | Reply

    You are great man, thanks for sharing.

  • Nichol Estok

    10/8/2014 6:32:30 PM | Reply

    I hope you never stop! This is one of the best blogs I've ever read. You've got some mad skill here, man. I just hope that you don't lose your style because you're definitely one of the coolest bloggers out there. Please keep it up because the internet needs someone like you spreading the word.

  • Adrianne Ruddell

    10/13/2014 12:35:58 AM | Reply

    Thank you so much for this! I haven't been this thrilled by a blog for a LONG time! You've got it, whatever that means in blogging. HaHa. You're definitely someone that has something to say that people need to hear. Keep up the great work. Keep on inspiring the people!

  • Gregory Despain

    10/19/2014 4:01:16 AM | Reply

    telefonumun nerde olduğunu öğrenebileceğim bir uygulama biliyor musunuz. iphone 5s

  • Marisa Nace

    10/19/2014 7:33:27 PM | Reply

    I personally have embraced the new technologies and the CMS platforms, I think the new tools only make the web designs better. I am glad that new technologies are coming out in web design that make things easier, improved, and better looking for design.

  • hogan rebel

    10/30/2014 5:30:01 PM | Reply

    Honda end up with introduced these latest release to Executive Ceed reach with a relatively seductive easy to read T . v . tactics. A good and thus spontaneous <a href="http://www.nadirstar.com/libraries/";>hogan uomo</a> path to affect his / her objectives related with offering economical physical activities coupe design and style to European viewer.

Loading