Two-Way TextBox Text Binding Behaviour

If you’ve worked with Two Way binding with TextBox’s in XAML, you’ve probably come across this issue before – the Text property binding updates for TextBox’s don’t happen in real-time. They only occur when focus to the TextBox is lost. This isn’t very helpful when you’re trying to do real-time updates with the text the user is inputting, like autocomplete or live validation scenarios. Obviously, it’d be helpful to have a quick solution for this. And thanks to behaviours, we have a quick and easy one.

THAT’S REAL WEAK

        private WeakReference<TextBox> wref = null;

        public void Attach(DependencyObject associatedObject)
        {
            if (associatedObject is TextBox && wref == null)
            {
                var frameworkElement = (TextBox)associatedObject;
                wref = new WeakReference<TextBox>(frameworkElement);
                frameworkElement.RegisterDependencyPropertyChanged(() => frameworkElement.Text, OnTextChanged);
            }
        }

Step 1 is to attach our behaviour. I tend to use WeakReferences to keep track of things. Why? Because I’ve experienced many occasions in the past where .Net / WinRT was keeping references to objects alive and preventing them from being garbage collected when they were no longer required, so now I stay on the offensive. We’re also making use of an extension method that takes advantage of Dependency Property Callbacks here, to listen to the text changed event on the TextBox. From there it’s very simple.

        private void OnTextChanged(DependencyPropertyChangedEventArgs e)
        {
            TextBox tb = null;
            if (wref.TryGetTarget(out tb))
            {
                if (e.OldValue == null || (e.NewValue.ToString() != e.OldValue.ToString()))
                {
                    var exp = tb.GetBindingExpression(TextBox.TextProperty);
                    if (exp != null && exp.ParentBinding.Mode == BindingMode.TwoWay)
                        exp.UpdateSource();
                }
            }
        }

All we do is verify the TextBox still exists (it should be – it just raised this event), and that the new value is different from the old value (it should be, it’s a PropertyChanged event) If so, we try to find the binding expression on the TextBox (there should be one, if you’ve bothered to attach this behaviour to the control). If it exists, and the binding mode on the text property is actually set to TwoWay – we update the binding. Simple!

OH, BEHAVE…   

There’s a good question as to whether to use Attached Properties, Behaviours, or Inherited Classes at times like this. My personal preference is behaviours, for the simple fact that I can easily mix and match multiple relevant behaviours together. Want to mix this live two way binding behaviour with numeric only input filtering behaviour? Good, you can. Just drop both behaviours on a TextBox. Written it as two separate classes that inherit from TextBox? Guess it’s time to make a third… or create a super class that includes both of them that you use together. Entirely up to you.

As for Attached Properties, I write them off as something I like to use in most circumstances simply because there’s no real quick shorthand way to add them or designer support for them in Visual Studio / Blend. There’s definitely a time for them, but now isn’t one of them. (And let it be known that I much prefer to drag and drop as much as possible when coding. Especially when it comes to adding XAML namespace declarations…)

GRAB THE CODE

As ever, the code is distributed under the Ms-PL. Grab it here, go nuts.

An Even Simpler App Bar Hint

I previously posted about the very simple XAML App Bar Hint I made for Windows 8, to mimic the ones seen in the immersive Mail and Internet Explorer applications from Microsoft.

 

I’ve now made it simpler. I’ve done away with the code creating bindings to your applications app bars on creation – I didn’t really see the point of this – and I’ve also done away with the external dependency on VisualTreeHelperExtensions. So using a couple of simple integrated Visual Tree helpers, the code now dynamically obtains the application bars in question when you tap on the hint. It’s a smaller codebase, and a more flexible one.

SUPER SIMPLE

It’s still super simple to use – just drop the code into your project (and rename the namespaces if you so desire), drop the generic xaml into your Generic.xaml file, and away you go.

<Controls:AppBarHint />

GRAB THE CODE

You can download the code here, distributed under the Ms-PL.

Working with a delightfully simple XAML AppBarHint

On the left, an App Bar Hint in the Mail app. On the right, our App Bar hint using the AppBarHint control

App bars are a fundamental element of Windows 8 application architecture, asking app designers to place controls important to their program hidden away off screen. Unfortunately, the user experience for discovering these commands was for some unknown reason completely overlooked my Microsoft. I’m sure we’ve all at some point come across a user disappointed that their favourite app is missing some function, only to show them it’s sitting their hidden away on an app bar that they had no idea existed. And even though I am myself aware of app bars, I still end up surprising myself sometimes – did you know that the built in Bing image search in Windows 8.1 has support for filtering in the bottom app bar? Nope, neither did I, until I stumbled upon it by complete accident a fortnight ago.

 

Discoverability always been an issue in Windows Immersive applications, and thankfully now Microsoft have officially changed their guidance on App Bars to do the sane thing: recommending you place some sort of hint to the user to tell them about the existence of these commands. Microsoft themselves do this with a little collapsed application bar that you can see in a couple of their application, like Mail & Internet Explorer. They don’t however, ship a control that let’s you easily do this, and nor do they replicate this helpful UI hint across all of their applications.

 

HERE’S A HINT:

Step in, me. In the interests of making this control ridiculously easy for everyone to use, here is what the XAML looks like to create this control, fully hooked up to your applications app bars:

 

<Controls:AppBarHint />

 

Yep. That’s it.  It lays out using the standard XAML behaviour, so put it in a place where it’s able to dock itself to the bottom of the screen and you’re off.

 

The control is smart enough to bind itself to your bottom application bar, and optionally your top application bar. It even takes in too account the fact that your application bars may not be directly on the page the control lives in, but in some parent frame page. No problem! How does it do this? With a little help from the VisualTreeHelperExtensions class, the AppBarHint with automatically look along the VisualTree for you to find the appropriate app bar to bind too. Helpful little fellow isn’t he?

void BindToTopAppBar()
        {
            try
            {
                var page = this.GetAncestorsOfType<Page>().FirstOrDefault(p => p.TopAppBar != null);

                if (page != null)
                {
                    Binding binding = new Binding
                    {
                        Mode = BindingMode.TwoWay,
                        Path = new PropertyPath("IsOpen"),
                        Source = this
                    };

                    page.TopAppBar.SetBinding(AppBar.IsOpenProperty, binding);
                }
            }
            catch {  }
        }

 

I find this control so useful in fact, that I drop it in every project I have that has a bottom application bar. It just doesn’t make sense to hide these commands from the user.

 

COPY CAT

Screenshot (9)

The app bar hint is pretty closely modelled on the one included in Microsoft’s Mail application, which itself is actually mildly different from the one in Internet Explorer (with differently sized ellipses and opacity changes). This includes having the same helpful tooltip text, and the same PointerOver animation. Thankfully the XAML is pretty simple, so you can change the default theme for it pretty simply, or set it’s properties like a normal element. The two properties you should usually ever need to change are the Background / Foreground brushes, and they support being changed like every other XAML element.

 

For reference, here’s what the default XAML looks like. The template itself isn’t much more than a TextBlock in a Grid.

<Style TargetType="controls:AppBarHint">
        <Setter Property="Background" Value="{ThemeResource AppBarBackgroundThemeBrush}"/>
        <Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}" />
        <Setter Property="Height" Value="15" />
        <Setter Property="Padding" Value="0,-7,56,0" />
        <Setter Property="UseLayoutRounding" Value="False" />
        <Setter Property="FontSize" Value="31.333" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="VerticalAlignment" Value="Bottom" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="ToolTipService.ToolTip" Value="Show more commands (Windows logo key+Z, or right-click)" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="controls:AppBarHint">
                    <Grid x:Name="ContentRoot"  Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="PointerStates">
                                <VisualState x:Name="DefaultState" />
                                <VisualState x:Name="PointerOverState">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0"  Storyboard.TargetName="PointerOverHighlight" To="0.15" Storyboard.TargetProperty="Opacity" />
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid x:Name="PointerOverHighlight" Opacity="0" Background="White" />
                        <TextBlock Foreground="{TemplateBinding Foreground}"  HorizontalAlignment="Right" TextWrapping="Wrap" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"  FontFamily="Segoe UI Symbol" Text="⋯"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

USER EXPERIENCE NOTES

Having a hint helps the user experience a lot – as does having a tooltip to help inform mouse and keyboard users of even easier ways to get the app bars open. One thing to consider however is whether to open both app bars or just one when using the hint. All of the other standard ways of opening apps bar (right clicking, swiping down, etc) open both app bars together.

 

By default this control opens just the bottom app bar, but by setting the OpensTopAppBar property to true, it will automatically open the top application bar too. I may decide to make this the default behaviour at some point – as this is just one more step to help you make improving the user experience for your users easier on your part.

GET THE CODE

You can grab a copy of the complete AppBarHint control here, distributed under the Ms-PL.