Tag Archives: XAML

WPF Performance – TextBlock

WPF typically doesn’t have many performance issues and, where it does, this can usually be fixed by virtualisation. Having said that, even if you never need to use this, it’s useful to know that you can eek that last ounce of performance out of the system.

Here’s a basic program that contains a TextBlock:

<Window x:Class="TextBlockTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TextBlockTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        x:Name="MainWindowView">
    <Grid>
        <ScrollViewer>
            <ItemsControl ItemsSource="{Binding BigList, ElementName=MainWindowView}" Margin="0,-1,0,1">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</Window>

Code behind:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TextBlockTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ObservableCollection<string> BigList { get; set; }

        public MainWindow()
        {
            BigList = new ObservableCollection<string>();
            for (int i = 0; i <= 10000; i++)
            {
                BigList.Add($"Item {i}");
            }

            InitializeComponent();
        }
    }
}

Let’s, for a minute, imagine this is slow, and profile it:

The layout is taking most of the time. Remember that each control needs to be created, and remember that the TextBlock does slightly more than just display text:

The whole panel took 3.46s. Not terrible, performance, but can it be improved? The answer is: yes, it can! Very, very slightly.

Let’s create a Custom Control:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace FastTextBlock
{
   
    public class MyTextBlockTest : Control
    {
        private FormattedText _formattedText;

        static MyTextBlockTest()
        {
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(MyTextBlockTest), new FrameworkPropertyMetadata(typeof(MyTextBlockTest)));
        }

        public static readonly DependencyProperty TextProperty =
             DependencyProperty.Register(
                 "Text", 
                 typeof(string),
                 typeof(MyTextBlockTest), 
                 new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure,
                    (o, e) => ((MyTextBlockTest)o).TextPropertyChanged((string)e.NewValue)));

        private void TextPropertyChanged(string text)
        {
            var typeface = new Typeface(
                new FontFamily("Times New Roman"),
                FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);

            _formattedText = new FormattedText(
                text, CultureInfo.CurrentCulture,
                FlowDirection.LeftToRight, typeface, 15, Brushes.Black);
        }


        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            if (_formattedText != null)
            {
                drawingContext.DrawText(_formattedText, new Point());
            }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            //return base.MeasureOverride(constraint);

            return _formattedText != null
            ? new Size(_formattedText.Width, _formattedText.Height)
            : new Size();
        }
    }
}

Here’s the new XAML:

    <Grid>
        <ScrollViewer>
            <ItemsControl ItemsSource="{Binding BigList, ElementName=MainWindowView}" Margin="0,-1,0,1">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <!--<TextBlock Text="{Binding}"/>-->
                        <controls:MyTextBlockTest Text="{Binding}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Grid>

Shaves around 10ms off the time:

Even more time can be shaved by moving up an element (that is, inheriting from a more base class than `Control`. In fact, `Control` inherits from `FrameworkElement`:

public class MyTextBlockTest : FrameworkElement

Shaves another 10ms off:

Conclusion

Clearly, this isn’t a huge performance boost, and in 99% of use cases, this would be massively premature optimisation. However, the time that this really comes into its own is where you have a compound control (a control that consists of other controls), and you have lots of them (hundreds). See my next post for details.

References:

https://social.msdn.microsoft.com/Forums/en-US/94ddd25e-7093-4986-b8c8-b647924251f6/manual-rendering-of-a-wpf-user-control?forum=wpf

http://www.codemag.com/article/100023

http://stackoverflow.com/questions/20338044/how-do-i-make-a-custom-uielement-derived-class-that-contains-and-displays-othe

http://stackoverflow.com/questions/42494455/wpf-custom-control-inside-itemscontrol

XAML Translations based on the display size in UWP

Having recently released my latest app into the store, I noticed that some of the buttons didn’t fit well on the phone version. I’d already come across translations for the HTML/WinJS version of Windows 8 apps, but not the XAML version, and not Windows 10 UWP.

You can do this in Expression Blend

First, double click the view file:

transuwp1

In the States window, you’ll then have the ability to create a new VisualStateGroup:

transuwp2

Create the required states; here’s mine:

transuwp3

If you click the lightning bolt to the side of the group, you should get the following:

transuwp4

If you create an adaptive trigger as above, you’ll be able to set the minimum width or height for the change. I’ve set the minimum width for desktop form factor to 500.

Back to code

What that produces for you (remember that you can alt-tab between Blend and VS and it should deal with the changes relatively gracefully) is a piece of code along the lines of this, within the XAML view:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <VisualStateManager.VisualStateGroups>

            <VisualStateGroup x:Name="VisualStateGroup">

                <VisualState x:Name="Normal">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="500"/>
                    </VisualState.StateTriggers>
                </VisualState>

                <VisualState x:Name="Mobile">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="320"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="tgPlay.(Grid.Column)" Value="0"/>
                        <Setter Target="tgPlay.(Grid.Row)" Value="1"/>
                        <Setter Target="btnCommandCreateVideo.(Grid.Row)" Value="1"/>
                        <Setter Target="btnCommandCreateVideo.(Grid.Column)" Value="1"/>
                        <Setter Target="btnCommandClear.(Grid.Row)" Value="1"/>
                        <Setter Target="btnCommandClear.(Grid.Column)" Value="2"/>
                        <Setter Target="numericUpDown.(Grid.Column)" Value="0"/>
                        <Setter Target="numericUpDown.(Grid.Row)" Value="1"/>
                        <Setter Target="numericUpDown.(Grid.ColumnSpan)" Value="3"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

There’s a number of points here – the first is that you need to name your controls; that’s how they are referenced. If you use Blend and you don’t do this then blend will give them a name; for example, I didn’t give “numericUpDown” a name. If I had another control of the same type then it would just number them.

The second point is the adaptive trigger. 500 and 320 seem to be the generally accepted divisions between desktop and narrow form factor. This approach worked for my specific requirement, although what Blend produces does require some re-work, otherwise it just ends up as a mess.

Creating a speech bubble with rounded corners

Following on from an earlier post I decided that the speech bubble in that post would look more like a … bubble if the corners were rounded. The relevant geometry to achieve this is a Cubic Bezier Curve. The MSDN page is here.

The abbreviated form is C. It works with 3 parameters:

C[start co-ordinates] [middle co-ordinate] [final co-ordinate]

bezier

The following is the geometry for the speech bubble:

<PathGeometry x:Key="SpeechBubble">M 10,10 L20,0 L30,10 L190,10 C190,10 200,10 200,20 L200,90 C200,90 200,100 190,100 L10,100 C10,100 0,100 0,90 L0,20 C0,20 0,10 10,10 Z</PathGeometry>

The idea is that instead of drawing the line right to the corner, I go to within 10 pixels and curve round the corner.

roundedtooltip

Tooltip Speech Bubbles

I recently tried to introduce a concept of a tooltip that appeared as a speech bubble; that is, a box that has a pointer to the originating control.

Standing on the Shoulders of Giants

The following is a rundown of links that I used extensively during this investigation:

http://stackoverflow.com/questions/337181/how-do-i-create-a-custom-wpf-control-like-a-cartoon-bubble-with-constant-corners

http://stackoverflow.com/questions/337181/how-do-i-create-a-custom-wpf-control-like-a-cartoon-bubble-with-constant-corners

http://jobijoy.blogspot.co.uk/2008/12/xaml-balloon-comments-expression-blend.html

http://stackoverflow.com/questions/11446250/how-to-style-wpf-tooltip-like-a-speech-bubble

http://stevenhollidge.blogspot.co.uk/2012/04/custom-tooltip-and-popup.html

Possible Approaches

In investigating this, I created four separate projects; these essentially boiled down to three different approaches:
1. A styled tooltip with no arrow

tt1

2. A tooltip using the Expression Blend “Callout” method

tt2

3. A styled tooltip using the “PathGeometry” to define a pointer

tt3

(1) had the advantage that it looked much better, but doesn’t have a concept of an arrow to the source control. (2) was by far the easiest, but the arrow style makes it look a bit like a cartoon.

In the end I opted for (3), the solution looks a little like this:

    <Window.Resources>
        <LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
    
        <Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="HorizontalOffset" Value="0" />
            <Setter Property="VerticalOffset" Value="0" />
            <Setter Property="Background" Value="GhostWhite" />
            <Setter Property="Foreground" Value="Gray" />
            <Setter Property="FontSize" Value="12" />
            <Setter Property="FontFamily" Value="Segoe UI" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToolTip">
                        <Canvas Width="200" Height="100">
                            <Path x:Name="Container"
                          Canvas.Left="0"
                          Canvas.Top="0"
                          Margin="0"
                          Data="M 50,10 L60,0 L70,10 L100,10 L100,100 L0,100 L0,10 L50,10"
                          Fill="{TemplateBinding Background}"
                          Stroke="Black">
                                <Path.Effect>
                                    <DropShadowEffect BlurRadius="10"
                                              Opacity="0.5"
                                              ShadowDepth="4" />
                                </Path.Effect>
                            </Path>
                            <TextBlock Canvas.Left="50"
                               Canvas.Top="28"
                               Width="100"
                               Height="65"
                               Text="{TemplateBinding Content}"
                               TextWrapping="Wrapwithoverflow" />
                        </Canvas>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

You can style the bubble a bit:

        <LinearGradientBrush x:Key="LightBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="SolidBorderBrush" Color="#888" />
    
        <Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
            <Setter Property="OverridesDefaultStyle" Value="true" />
            <Setter Property="HorizontalOffset" Value="-50" />
            <Setter Property="VerticalOffset" Value="0" />
            <Setter Property="Background" Value="#BE1C1C1C" />
            <Setter Property="Foreground" Value="Gray" />
            <Setter Property="FontSize" Value="12" />
            <Setter Property="FontFamily" Value="Segoe UI" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToolTip">
                        <Canvas Width="200" Height="100">

                            <Path x:Name="Container"
                          Canvas.Left="0"
                          Canvas.Top="0"                                  
                          Margin="0"
                          Data="M 50,10 L60,0 L70,10 L200,10 L200,100 L0,100 L0,10 L50,10"                                  
                          Stroke="Black">
                                <Path.Effect>
                                    <DropShadowEffect BlurRadius="10"
                                              Opacity="0.5"
                                              ShadowDepth="4" />
                                </Path.Effect>
                                <Path.Fill>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="#CF181818" Offset="0"/>
                                        <GradientStop Color="#BE1C1C1C" Offset="1"/>
                                    </LinearGradientBrush>

                                </Path.Fill>
                            </Path>
                            <TextBlock Canvas.Left="50"
                               Canvas.Top="28"
                               Width="100"
                               Height="65"
                               Text="{TemplateBinding Content}"
                               TextWrapping="Wrapwithoverflow" />
                        </Canvas>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

I then looked into the concept of centring the arrow, the following links looked like they might help:

http://stackoverflow.com/questions/14167763/wpf-path-geometry-is-there-a-way-to-bind-the-data-property

http://www.blackwasp.co.uk/WPFPathMarkupsyntax.aspx

And I did try expanding the syntax:

                                <Path.Data>
                                    <PathGeometry>
                                        <PathGeometry.Figures>
                                            <PathFigureCollection>
                                                <PathFigure IsClosed="True" StartPoint="{Binding RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ControlToCentreConverter}}">
                                                    <PathFigure.Segments>
                                                        <PathSegmentCollection>
                                                            <LineSegment Point="60,0"/>
                                                            <LineSegment Point="70,10"/>
                                                            <LineSegment Point="200,10"/>
                                                            <LineSegment Point="200,100"/>
                                                            <LineSegment Point="0,100"/>
                                                            <LineSegment Point="0,10"/>
                                                        </PathSegmentCollection>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                            </PathFigureCollection>
                                        </PathGeometry.Figures>
                                    </PathGeometry>

The idea was to bind the line segments. I’m not saying it’s not possible; it clearly is, but it started to get prohibitively complex. If anyone comes up with a simple way of doing this (or even a complex one) then please add a link in the comments.

InkCanvas

Pointlessly Long Introduction (feel free to skip)

Some time ago, in a previous job, I was asked to add spell checking to a WPF textbox. I did some research as to how to do that, and came to the conclusion that the only way was using MS Word automation. I must have spent a good three or four hours writing code that interrogated Word and performed spell checking. It wasn’t until I got to auto correct that one of my searches threw up a property on the text box: “SpellCheck.IsEnabled”.

(At the time of writing) I recently attended a developer conference, and at it, I was shown a control called InkCanvas! Having recently spent a considerable amount of time trying to use a Canvas for drawing, I felt like I’d just found the SpellCheck.IsEnabled property again.

Using the InkCanvas Control

In comparison to the Canvas, the InkCanvas basically works out of the box. If you use the InkToolbar with it, you’ll get some errors, but they aren’t actually errors; for example:

1>C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(2048,5): warning MSB3781: The SDK “InkToolbarControl, Version=0.3.2” depends on the following SDK(s) “Microsoft.VCLibs, version=14.0”, which have not been added to the project or were not found. Please ensure that you add these dependencies to your project or you may experience runtime issues. You can add dependencies to your project through the Reference Manager.

Here’s the XAML that I used to get it working. Don’t be phased by the fact that x:Bind doesn’t seem to resolve.

            <InkCanvas x:Name="drawInkCanvas">                    
            </InkCanvas>
            <inkTools:InkToolbar TargetInkCanvas="{x:Bind drawInkCanvas}" 
                                PenColor="#FFE61021" 
                                VerticalAlignment="Top" HorizontalAlignment="Right"/>

Enabling Input

I found only one thing that caused confusion, and it took a while to solve. Basically, the above XAML doesn’t allow you to actually draw anything; you need this:

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            drawInkCanvas.InkPresenter.InputDeviceTypes =
                Windows.UI.Core.CoreInputDeviceTypes.Mouse |
                Windows.UI.Core.CoreInputDeviceTypes.Pen |
                Windows.UI.Core.CoreInputDeviceTypes.Touch;

        }

Saving the Image

To save what you’ve drawn, you can use something similar to this code:

            if (canvas != null && canvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
            {
                if (file != null)
                {
                    using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                    {
                        await canvas.InkPresenter.StrokeContainer.SaveAsync(stream);
                    }
                }
                Clear(canvas);
            }

Load and Display a list of Images in Windows 10

The following is a quick tutorial on how to load a series of images from the machine and display them on screen. This is something that I’m working on for my next Store Application.

The XAML

Let’s start with the screen layout – the GridView control makes this quite an easy task in Windows 10:

            <GridView ItemsSource="{Binding Images}">
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <Image Source="{Binding}" Stretch="Uniform" 
                               Width="100" Height="100"/>
                    </DataTemplate>
                </GridView.ItemTemplate>
            </GridView>

Binding

In this case, I have bound the view to a ViewModel; the relevant part of which looks like this:

        private ObservableCollection<BitmapImage> _images;
        public ObservableCollection<BitmapImage> Images
        {
            get
            {
                if (_images == null)
                {
                    _images = new ObservableCollection<BitmapImage>();
                }
                return _images;
            }
            set
            {
                _images = value;
                NotifyPropertyChanged();
            }
        }

Basically, just an observable collection of BitmapImage.

Load the Image

Here’s where we populate the list:

        private async void Load()
        {
            FileOpenPicker openPicker = new FileOpenPicker();
            openPicker.ViewMode = PickerViewMode.Thumbnail;
            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            openPicker.FileTypeFilter.Add(".jpg");
            openPicker.FileTypeFilter.Add(".jpeg");
            openPicker.FileTypeFilter.Add(".png");
            openPicker.FileTypeFilter.Add(".gif");

            StorageFile file = await openPicker.PickSingleFileAsync();
            if (file != null)
            {
                var fileStream = await file.OpenAsync(FileAccessMode.Read);

                BitmapImage bi = new BitmapImage();

                bi.SetSource(fileStream);
                Images.Add(bi);

            }            
        }

Conclusion

I spent a short period of time wondering what had happened to the UniformGrid for Windows 10, but as you can see, the GridView just makes this job much easier.

How to Draw a Diagonal Line Using Only XAML (and no path data)

I’ve been playing around with an app to maintain a score. The first thing that I wanted to do was to split the screen, but I wanted a diagonal split; similar to this:

phonescreen

There are a number of ways that this can be achieved; however, this is a method using only XAML. The same method will (I believe) work on Windows 8, WPF and Windows Phone.

First, split the screen into three rows or columns (which depends on how you want the orientation):

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

This needs to be an odd number of rows / columns if you want the line to be central; additionally, an increased number of rows or columns will result in a smaller incline. Next, draw a transparent rectangle across the middle grid square:

        <Rectangle Name="Placeholder" Grid.Row="1" 
                   HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                   Opacity="0"/>

Finally, simply draw a line, binding the X2, Y2 values to the height and width of your rectangle:

        <Line Grid.Row="1" 
              X1="0" X2="{Binding ActualWidth, ElementName=Placeholder}"
              Y1="0" Y2="{Binding ActualHeight, ElementName=Placeholder}"
              StrokeThickness="2" Stroke="Blue"/>

Conclusion

I’m not saying the is the best, most efficient, or only way of doing this. However, it does mean that I can do it on both Windows Phone and Windows Store (and in WPF if I were so inclined) using a single shared project.

Startup Uri Not Working – Cannot locate recource ‘mainwindow.xaml’

I recently re-visited this project, and found that, amongst other things, it would no longer run up. Clearly something had changed, and it was a while until I realised what. The error I was getting was this:

Cannot locate resource

The App.Xaml looked like this:

<Application x:Class="TFSUtils.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="clr-namespace:TFSUtils.ViewModel"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             StartupUri="MainWindow.xaml"
             mc:Ignorable="d">

Solution

I finally realised that I must have copied the MainWindow.Xaml into the Views folder as a last minute cleanup. The fix was very simple:

<Application x:Class="TFSUtils.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="clr-namespace:TFSUtils.ViewModel"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             StartupUri="View/MainWindow.xaml"
             mc:Ignorable="d">

Conclusion

So, there you go: long untouched and untested code just spontaneously breaks! Magic.

Using a RepeatButton to Rapidly Increment Values

Recently, I’ve been playing with the WPF `RepeatButton`. I’ve not used it before, as it does seem to fulfil a very specific purpose. The use that I was trying to put it to was to act in the same way as you would expect your alarm clock to behave if you changed the time.

Say it’s currently 3:10 and you need it to be 3:15; you’d simply press the plus button five times, right? But what if you want it to be 17:50? Well, you’d hold the plus button down, but you wouldn’t expect it to increase by minutes; you’d maybe expect it to jump by 30 mins at a time after holding the plus button for a while.

The RepeatButton can do the same… but unfortunately not out of the box.

The Box

What you can do out of the box is this:

 <RepeatButton Content="+" Name="IncreaseButton"/>

And to handle the click:

IncreaseButton.Click += new RoutedEventHandler(IncreaseButton_Click);

Finally:

private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{            
    Value += 1;
}

What’s wrong with that?

So far, so good, but what if I want the increment to increase though, as described in the alarm clock scenario above? I have two options here. One thing you can do is to increase the frequency that you increment; however, what I wanted to do was to increase the increment. The way I did it was to store a count:

private int _incrementButtonRepeat;

Then, in the `click` event:

private void IncreaseButton_Click(object sender, RoutedEventArgs e)
{            
    if (_incrementButtonRepeat > 5)
        Value += 5;
    else
        Value += 1;
    _incrementButtonRepeat++;        
}

Finally, you’ll need to reset:

private void IncreaseButton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    _incrementButtonRepeat = 0;
}

A little gotcha here is the MouseUp command never seems to fire, because it’s apparently handled internally. Use the preview to intercept it first.

Conclusion

You could create a very basic control that encapsulates this behaviour, based on some parameters; then you’d never have to consider this again.

Creating and Binding to a User Control in MVVM Cross

While creating my game, I recently came across the problem of navigation. This post describes how to create a custom user control and react to the event inside.

The usual disclaimer still applies here; although this is an MVVM Cross post, the contents of it should be applicable for any MVVM Framework or, in fact, any XAML binding at all.

User Control

The user control that I’m going to use is simply a navigation bar to appear at the top of each screen.

The XAML for the user control is here:

<UserControl
    x:Name="NavigationControlRoot">
    
    <Grid DataContext="{Binding ElementName=NavigationControlRoot}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Button Content="Home" Grid.Column="0" Command="{Binding HomeClick}" />
        <Button Content="Back" Grid.Column="1" Command="{Binding BackClick}" />
    </Grid>
</UserControl>

So, I’ve got two commands, binding to the code behind. The key point here is that the root control (grid) binds to the DataContext of the user control – effectively this binds it to the containing DataContext. Here’s the code behind:

    public sealed partial class NavigationPanel : UserControl
    {
        public static DependencyProperty BackCommandProperty =
            DependencyProperty.Register(
                "BackClick",
                typeof(ICommand),
                typeof(NavigationPanel),
                new PropertyMetadata(null));

        public static DependencyProperty HomeCommandProperty =
            DependencyProperty.Register(
                "HomeClick",
                typeof(ICommand),
                typeof(NavigationPanel),
                new PropertyMetadata(null));

        public NavigationPanel()
        {
            this.InitializeComponent();
        }
        
        public ICommand BackClick
        {
            get
            {
                return (ICommand)GetValue(BackCommandProperty);
            }

            set
            {
                SetValue(BackCommandProperty, value);
            }
        }

        public ICommand HomeClick
        {
            get
            {
                return (ICommand)GetValue(HomeCommandProperty);
            }

            set
            {
                SetValue(HomeCommandProperty, value);
            }
        }        
    }

So, you’ve now exposed two dependency properties and bound them to the XAML in the user control.

Host App

Here’s the relevant XAML for the hosting view:

<controls:NavigationPanel BackClick="{Binding BackClickCommand}"                                  
                                  HomeClick="{Binding HomeClickCommand}"/>

And that just binds to the ViewModel as you would expect.

        private IMvxCommand _homeClickCommand;
        public IMvxCommand HomeClickCommand
        {
            get
            {
                if (_homeClickCommand == null)
                {
                    _homeClickCommand = new MvxCommand(() => GoHome());
                }
                return _homeClickCommand;
            }
            set
            {
                _homeClickCommand = value;
            }
        }

        private void GoHome()
        {
            ShowViewModel<MainViewModel>();
        }

For me, this is in a base view model, so it responds to the same command in every view.

Conclusion

I think the final version of this code will interact with an IMvxPresenter in some way, which may make another post.