Universal Windows Platform – Stacked Control

Stacked Control demonstrates how to create a Control to display values in the form of a horizontal stacked bar chart – in this case displaying the first few values of the famous Fibonacci sequence.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in the Name as StackedControl and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Stacked.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Stacked.cs the following should be entered:

using System.Collections.Generic;
using System.Linq;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace StackedControl
{
    public class Stacked : Grid
    {
        private List _items = new List();

        private List Percentages()
        {
            List results = new List();
            double total = _items.Sum();
            foreach (double item in _items)
            {
                results.Add((item / total) * 100);
            }
            return results.OrderBy(o => o).ToList();
        }

        private Rectangle GetRectangle(Color colour, int column)
        {
            Rectangle rect = new Rectangle()
            {
                Fill = new SolidColorBrush(colour)
            };
            rect.SetValue(Grid.ColumnProperty, column);
            return rect;
        }

        private void Layout()
        {
            List percentages = Percentages();
            this.ColumnDefinitions.Clear();
            for (int index = 0; index < percentages.Count(); index++)
            {
                double percentage = percentages[index];
                ColumnDefinition column = new ColumnDefinition()
                {
                    Width = new GridLength(percentage, GridUnitType.Star)
                };
                this.ColumnDefinitions.Add(column);
                Color colour = (index < Palette.Count()) ? Palette[index] : Colors.Black;
                this.Children.Add(GetRectangle(colour, index));
            }
        }

        public List Palette { get; set; } = new List();

        public List Items
        {
            get { return _items; }
            set { _items = value; Layout(); }
        }

        public void Fibonacci(params Color[] colours)
        {
            Palette = colours.ToList();
            int fibonacci(int value) => value > 1 ?
            fibonacci(value - 1) + fibonacci(value - 2) : value;
            Items = Enumerable.Range(0, Palette.Count())
            .Select(fibonacci).Select(s => (double)s).ToList();
        }
    }
}

The Stacked Class itself which inherits from a Grid and has a List of double to represent the values of the Chart. Tne Percentages Method works out the percentage of each item that’s needed for the Chart based on it’s Value, GetRectangle is used to get a Rectangle to use as a Chart item, Layout is used to create the look-and-feel of the Chart, Pallette is the List of Color to be used in the Chart and a Property which encapsulates the Chart items. There’s a Fibonacci Method which allows the Chart to be populated based on the values of the Fibonacci sequence.

Step 8

Once done select from the Menu, Build, then Build Solution

vs2017-build-build-solution

Step 9

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 10

From the Menu choose View and then Designer

vs2017-view-designer

Step 11

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<local:Stacked Margin="50" x:Name="Display"/>

The MainPage just has the Stacked Control itself.

Step 12

From the Menu choose View and then Code

vs2017-view-code

Step 13

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

protected override void OnNavigatedTo(NavigationEventArgs e)
{            
	Display.Fibonacci(
	Windows.UI.Colors.Black,
	Windows.UI.Colors.Gray,
	Windows.UI.Colors.Red,
	Windows.UI.Colors.Orange,
	Windows.UI.Colors.Yellow,
	Windows.UI.Colors.Green,
	Windows.UI.Colors.Cyan,
	Windows.UI.Colors.Blue,
	Windows.UI.Colors.Magenta,
	Windows.UI.Colors.Purple);
}

Below the MainPage() Method there is the OnNavigatedTo Event Handler which triggers the Fibonacci Method of the Stacked Control.

Step 14

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 15

After the Application has started it should then appear displaying a Stacked Chart displaying the first few numbers of the Fibonacci sequence

ran-stacked-control

Step 16

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This Control is used to display some of the Fibonacci numbers as an example and has a basic colour palette set which the example uses to set how many item to add to the chart and it could be changed so you could also set an “Orientation” or style it in any way desired and used for things such as a space used indicator for example.

Creative Commons License

Advertisements

Universal Windows Platform – Lights Control

Lights Control demonstrates how to create a Lights Control to display indicator lights – in this case was used to display the sequence of lights for a UK Traffic Light.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in the Name as LightsControl and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Lights.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Lights.cs the following should be entered:

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace LightsControl
{
    public class Light : Grid, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public static readonly DependencyProperty ForegroundProperty =
        DependencyProperty.Register("Foreground", typeof(Brush),
        typeof(Light), new PropertyMetadata(new SolidColorBrush(Colors.Black)));

        public static readonly DependencyProperty SizeProperty =
        DependencyProperty.Register("Size", typeof(double),
        typeof(Light), new PropertyMetadata((double)100));

        public static readonly DependencyProperty OffProperty =
        DependencyProperty.Register("Off", typeof(Visibility),
        typeof(Light), new PropertyMetadata(Visibility.Collapsed));

        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set
            {
                SetValue(ForegroundProperty, value);
                NotifyPropertyChanged("Foreground");
            }
        }

        public double Size
        {
            get { return (double)GetValue(SizeProperty); }
            set
            {
                SetValue(SizeProperty, value);
                NotifyPropertyChanged("Size");
            }
        }

        public Visibility Off
        {
            get { return (Visibility)GetValue(OffProperty); }
            set
            {
                SetValue(OffProperty, value);
                NotifyPropertyChanged("Off");
            }
        }

        public Light()
        {
            this.Margin = new Thickness(5);
            Ellipse element = new Ellipse()
            {
                Stretch = Stretch.Fill,
                Height = Size,
                Width = Size
            };
            element.SetBinding(Ellipse.FillProperty, new Binding()
            {
                Source = this,
                Path = new PropertyPath("Foreground"),
                Mode = BindingMode.TwoWay
            });
            Ellipse overlay = new Ellipse()
            {
                Fill = new SolidColorBrush(Colors.Black),
                Stretch = Stretch.Fill,
                Opacity = 0.75,
                Height = Size,
                Width = Size
            };
            overlay.SetBinding(Ellipse.VisibilityProperty, new Binding()
            {
                Source = this,
                Path = new PropertyPath("Off"),
                Mode = BindingMode.TwoWay
            });
            this.Children.Add(element);
            this.Children.Add(overlay);
        }

        public bool IsOn
        {
            get { return Off == Visibility.Collapsed; }
            set
            {
                Off = value ? Visibility.Collapsed : Visibility.Visible;
                NotifyPropertyChanged("IsOn");
                NotifyPropertyChanged("Off");
            }
        }
    }

    public class Lights : StackPanel
    {
        private Light _red = new Light { Foreground = new SolidColorBrush(Colors.Red) };
        private Light _orange = new Light { Foreground = new SolidColorBrush(Colors.Orange) };
        private Light _green = new Light { Foreground = new SolidColorBrush(Colors.Green) };

        private async Task Delay(int seconds = 2)
        {
            await Task.Delay(seconds * 1000);
            return true;
        }

        public Lights()
        {
            this.Orientation = Orientation.Vertical;
            this.Children.Add(_red);
            this.Children.Add(_orange);
            this.Children.Add(_green);
        }

        public async void Traffic()
        {
            _red.IsOn = false;
            _orange.IsOn = false;
            _green.IsOn = true;
            await Delay();
            _green.IsOn = false;
            await Delay();
            _orange.IsOn = true;
            await Delay();
            _orange.IsOn = false;
            await Delay();
            _red.IsOn = true;
            await Delay();
            _red.IsOn = true;
            await Delay();
            _orange.IsOn = true;
            await Delay();
            _red.IsOn = false;
            _orange.IsOn = false;
            _green.IsOn = true;
            await Delay();
        }
    }
}

There is a Light Class which implements the INotifyPropertyChanged Interface and contains a NotifyPropertyChanged Method to allow the Control to respond to updates, there are DependencyProperty and Properties for Foreground, Size and Off – this indicates if the Light should be enabled or not. The Light Constructor is used to create the look-and-feel of the Light itself and there is an IsOn Property to set the Off Property accordingly.

The Lights Class extends a StackPanel with three Light Controls within and a Delay Method which will be used to pause any output and a Traffic Method which contains the whole sequence for a traffic light – based on the UK traffic light sequence.

Step 8

Once done select from the Menu, Build, then Build Solution

vs2017-build-build-solution

Step 9

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 10

From the Menu choose View and then Designer

vs2017-view-designer

Step 11

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Viewbox>
	<local:Lights Margin="50" x:Name="Display" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Viewbox>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Icon="Play" Label="Play" Click="Play_Click"/>
</CommandBar>

The MainPage has the Lights Control itself along with a CommandBar with an AppBarButton to trigger the Play_Click Event.

Step 12

From the Menu choose View and then Code

vs2017-view-code

Step 13

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

private void Play_Click(object sender, RoutedEventArgs e)
{
	Display.Traffic();
}

Below the MainPage() Method there is the Play_Click Event Handler which triggers the Traffic Method of the Lights Control.

Step 14

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 15

After the Application has started running you can then select Play to go cycle through the UK traffic light sequence displayed using the Control.

ran-lights-control

Step 16

To Exit the Application select the Close button in the top right of the Application

vs2017-close

The Control is used to cycle through the light sequence from a UK Traffic light to demonstrate the principle of using the lights and to show they update accordingly and it could be expanded upon to display differently or as-is with more combinations of lights or appear more like traffic lights or even less like them to represent something else.

Creative Commons License

Universal Windows Platform – Matrix Control

Matrix Control demonstrates how to create a Matrix Control to display the current Time and Date.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in the Name as MatrixControl and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Matrix.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Matrix.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace MatrixControl
{
    public class Matrix : StackPanel
    {
        private readonly byte[][] table =
        {
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 0
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,0,0,1,1,0,0,0,
            0,1,1,1,1,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,0,0,0,0,0
            }, // 1 
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 2
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 3
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,0,0,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 4
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 5
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 6
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 7
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 8
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,1,1,1,1,1,1,0,
            0,1,1,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,1,1,0,
            0,1,1,1,1,1,1,0,
            0,0,0,0,0,0,0,0
            }, // 9
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0
            }, // Space
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,0,0,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,1,1,0,0,0,
            0,0,0,0,0,0,0,0
            }, // Colon
            new byte[] {
            0,0,0,0,0,0,0,0,
            0,0,0,0,0,1,1,0,
            0,0,0,0,1,1,0,0,
            0,0,0,1,1,0,0,0,
            0,0,1,1,0,0,0,0,
            0,1,1,0,0,0,0,0,
            0,0,0,0,0,0,0,0
            } // Slash
        };

        private readonly List glyphs =
            new List { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ', ':', '/' };
        private const int columns = 8;
        private const int rows = 7;
        private const int padding = 1;

        private string _value;
        private int _count;

        public enum Sources
        {
            Value = 0,
            Time = 1,
            Date = 2,
            TimeDate = 3
        }

        public static readonly DependencyProperty ForegroundProperty =
        DependencyProperty.Register("Foreground", typeof(Brush),
        typeof(Matrix), null);

        public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register("Source", typeof(Sources),
        typeof(Matrix), new PropertyMetadata(Sources.Time));

        public static readonly DependencyProperty SizeProperty =
        DependencyProperty.Register("Size", typeof(UIElement),
        typeof(Matrix), new PropertyMetadata(4));

        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }

        public Sources Source
        {
            get { return (Sources)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public int Size
        {
            get { return (int)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        private Rectangle AddElement(string name, int left, int top)
        {
            Rectangle element = new Rectangle()
            {
                Tag = name,
                Height = Size,
                Width = Size,
                Fill = Foreground,
                RadiusX = 1,
                RadiusY = 1,
                Opacity = 0,
                Margin = new Thickness(2)
            };
            element.SetBinding(Rectangle.FillProperty, new Binding()
            {
                Source = this,
                Path = new PropertyPath("Foreground"),
                Mode = BindingMode.TwoWay
            });
            element.SetValue(Canvas.LeftProperty, left);
            element.SetValue(Canvas.TopProperty, top);
            return element;
        }

        private void AddSection(string name)
        {
            Canvas layout = new Canvas()
            {
                Tag = name,
                Height = rows * Size,
                Width = columns * Size
            };
            int x = 0;
            int y = 0;
            int index = 0;
            for (int row = 0; row < rows; row++)
            {
                for (int column = 0; column < columns; column++)
                {
                    layout.Children.Add(AddElement($"{name}.{index}", x, y));
                    x = (x + Size + padding);
                    index++;
                }
                x = 0;
                y = (y + Size + padding);
            }
            this.Children.Add(layout);
        }

        private Rectangle SetElement(Canvas layout, string name)
        {
            return layout.Children.Cast().FirstOrDefault(f => (string)f.Tag == name);
        }

        private Canvas SetSection(string name)
        {
            return this.Children.Cast().FirstOrDefault(f => (string)f.Tag == name);
        }

        private void SetLayout(string name, char glyph)
        {
            Canvas layout = SetSection(name);
            int pos = glyphs.IndexOf(glyph);
            byte[] values = table[pos];
            for (int index = 0; index < layout.Children.Count; index++)
            {
                SetElement(layout, $"{name}.{index}").Opacity = values[index];
            }
        }

        private void GetLayout()
        {
            char[] array = _value.ToCharArray();
            int length = array.Length;
            List list = Enumerable.Range(0, length).ToList();
            if (_count != length)
            {
                this.Children.Clear();
                foreach (int item in list)
                {
                    AddSection(item.ToString());
                }
                _count = length;
            }
            foreach (int item in list)
            {
                SetLayout(item.ToString(), array[item]);
            }
        }

        public Matrix()
        {
            this.Spacing = 0;
            this.Orientation = Orientation.Horizontal;
            DispatcherTimer timer = new DispatcherTimer()
            {
                Interval = TimeSpan.FromMilliseconds(250)
            };
            timer.Tick += (object s, object args) =>
            {
                if (Source != Sources.Value)
                {
                    string format = string.Empty;
                    switch (Source)
                    {
                        case Sources.Time:
                            format = "HH:mm:ss";
                            break;
                        case Sources.Date:
                            format = "dd/MM/yyyy";
                            break;
                        case Sources.TimeDate:
                            format = "HH:mm:ss dd/MM/yyyy";
                            break;
                    }
                    Value = DateTime.Now.ToString(format);
                    
                }
            };
            timer.Start();
        }

        public string Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
                GetLayout();
            }
        }
    }
}

In the Matrix Class there’s a byte[][] which represents the positions of elements to make up each Symbol or glyph for each number between 0 and 9 plus : and / – where 1 represents an element which will be visible and 0 represents an element which will not be visible. There’s a list of char to represent all the characters supported by the table of symbols, there’s values for the “columns”, “rows” of a Matrix element. There are DependencyProperty and Properties for Foreground, Source and Size.

The is an AddElement Method which will create part of a Section of the Display as a Rectangle and used AddSection to create a Section itself and position the Elements, SetElement is used to toggle the Opacity of a Rectangle Element and SetSection to do the same for a Section of the Display. SetLayout is used to update the Display itself with the Value passed in or set with Source and GetLayout is used to create the look-and-feel of the Control and the Matrix Constructor sets up the Source of the Control for Time, Date and TimeDate and there’s a Value Property.

Step 8

Once done select from the Menu, Build, then Build Solution

vs2017-build-build-solution

Step 9

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 10

From the Menu choose View and then Designer

vs2017-view-designer

Step 11

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Viewbox>
	<local:Matrix Margin="50" Name="Display" Source="TimeDate" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{ThemeResource AccentButtonBackground}"/>
</Viewbox>

The MainPage has the Matrix Control itself with the Source set to TimeDate and will also work in the Designer

Step 12

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 13

After the Application has started running it should then appear displaying a Matrix Display that will show the current Time and Date.

ran-matrix-control

Step 14

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This example creates a grid-like display to be able to show more than just the Time but the Date as well abut also could be extended to show even more characters if needed. In order to create the glyphs or Symbols used in the example their layouts needed to be designed for the characters 0 – 9 plus : and /.

Creative Commons License

Universal Windows Platform – Custom ComboBox

Custom Combobox demonstrates how to create a custom Style for a ComboBox Control

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once the Project is created from the Solution Explorer select App.xaml

vs2017-app

Step 6

From the Menu choose View and then Designer

vs2017-view-designer

Step 7

Once in the Design View for App.xaml between the Application and /Application elements the following should be entered:

<Application.Resources>
	<Style x:Key="CustomComboBox" TargetType="ComboBox">
		<Setter Property="Padding" Value="12,5,0,7"/>
		<Setter Property="MinWidth" Value="{ThemeResource ComboBoxThemeMinWidth}"/>
		<Setter Property="Foreground" Value="Gold"/>
		<Setter Property="Background" Value="{ThemeResource ComboBoxBackground}"/>
		<Setter Property="BorderBrush" Value="{ThemeResource ComboBoxBorderBrush}"/>
		<Setter Property="BorderThickness" Value="{ThemeResource ComboBoxBorderThemeThickness}"/>
		<Setter Property="TabNavigation" Value="Once"/>
		<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
		<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
		<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled"/>
		<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/>
		<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True"/>
		<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
		<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True"/>
		<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
		<Setter Property="HorizontalAlignment" Value="Left"/>
		<Setter Property="VerticalAlignment" Value="Top"/>
		<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
		<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
		<Setter Property="ItemsPanel">
			<Setter.Value>
				<ItemsPanelTemplate>
					<CarouselPanel/>
				</ItemsPanelTemplate>
			</Setter.Value>
		</Setter>
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="ComboBox">
					<Grid x:Name="LayoutRoot">
						<Grid.Resources>
							<Storyboard x:Key="OverlayOpeningAnimation">
								<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
									<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="0.0"/>
									<SplineDoubleKeyFrame KeySpline="0.1,0.9 0.2,1.0" KeyTime="0:0:0.383" Value="1.0"/>
								</DoubleAnimationUsingKeyFrames>
							</Storyboard>
							<Storyboard x:Key="OverlayClosingAnimation">
								<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
									<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="1.0"/>
									<SplineDoubleKeyFrame KeySpline="0.1,0.9 0.2,1.0" KeyTime="0:0:0.216" Value="0.0"/>
								</DoubleAnimationUsingKeyFrames>
							</Storyboard>
						</Grid.Resources>
						<Grid.ColumnDefinitions>
							<ColumnDefinition Width="*"/>
							<ColumnDefinition Width="32"/>
						</Grid.ColumnDefinitions>
						<Grid.RowDefinitions>
							<RowDefinition Height="Auto"/>
							<RowDefinition Height="*"/>
						</Grid.RowDefinitions>
						<VisualStateManager.VisualStateGroups>
							<VisualStateGroup x:Name="CommonStates">
								<VisualState x:Name="Normal"/>
								<VisualState x:Name="PointerOver">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBackgroundPointerOver}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="GoldenRod"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Pressed">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBackgroundPressed}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBorderBrushPressed}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Disabled">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBackgroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="Background">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBorderBrushDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="HeaderContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxDropDownGlyphForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
							<VisualStateGroup x:Name="FocusStates">
								<VisualState x:Name="Focused">
									<Storyboard>
										<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HighlightBackground"/>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="HighlightBackground">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxBackgroundBorderBrushFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxDropDownGlyphForegroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="FocusedPressed">
									<Storyboard>
										<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="HighlightBackground"/>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxForegroundFocusedPressed}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextBlock">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxPlaceHolderForegroundFocusedPressed}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="DropDownGlyph">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ComboBoxDropDownGlyphForegroundFocusedPressed}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Unfocused"/>
								<VisualState x:Name="PointerFocused"/>
								<VisualState x:Name="FocusedDropDown">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="PopupBorder">
											<DiscreteObjectKeyFrame KeyTime="0">
												<DiscreteObjectKeyFrame.Value>
													<Visibility>Visible</Visibility>
												</DiscreteObjectKeyFrame.Value>
											</DiscreteObjectKeyFrame>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
							<VisualStateGroup x:Name="DropDownStates">
								<VisualState x:Name="Opened">
									<Storyboard>
										<SplitOpenThemeAnimation ClosedTargetName="ContentPresenter"
										OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"
										OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Closed">
									<Storyboard>
										<SplitCloseThemeAnimation ClosedTargetName="ContentPresenter"
										OffsetFromCenter="{Binding TemplateSettings.DropDownOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"
										OpenedTargetName="PopupBorder" OpenedLength="{Binding TemplateSettings.DropDownOpenedHeight, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
						</VisualStateManager.VisualStateGroups>
						<Border x:Name="Background" BorderBrush="Salmon" BorderThickness="{TemplateBinding BorderThickness}"
						Background="LightSalmon" Grid.ColumnSpan="2" Grid.Row="1" CornerRadius="15"/>
						<Border x:Name="HighlightBackground" BorderBrush="Gold" BorderThickness="{TemplateBinding BorderThickness}"
						Background="{ThemeResource ComboBoxBackgroundUnfocused}" Grid.ColumnSpan="2" Opacity="0" Grid.Row="1" CornerRadius="15"/>
						<ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
						Margin="{TemplateBinding Padding}" Grid.Row="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" CornerRadius="15">
							<TextBlock x:Name="PlaceholderTextBlock" Foreground="{ThemeResource ComboBoxPlaceHolderForeground}"
							Text="{TemplateBinding PlaceholderText}"/>
						</ContentPresenter>
						<FontIcon x:Name="DropDownGlyph" AutomationProperties.AccessibilityView="Raw" Grid.Column="1" Foreground="Gold"
						FontSize="12" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="" HorizontalAlignment="Right"
						IsHitTestVisible="False" Margin="0,10,10,10" Grid.Row="1" VerticalAlignment="Center"/>
						<Popup x:Name="Popup">
							<Border x:Name="PopupBorder" BorderBrush="Salmon"
							BorderThickness="{ThemeResource ComboBoxDropdownBorderThickness}"
							Background="LightSalmon" HorizontalAlignment="Stretch"
							Margin="0,-1,0,-1" CornerRadius="15">
								<ScrollViewer x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw"
									BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
									Foreground="{ThemeResource ComboBoxDropDownForeground}"
									HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
									HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
									IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
									IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
									IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
									MinWidth="{Binding TemplateSettings.DropDownContentMinWidth, RelativeSource={RelativeSource Mode=TemplatedParent}}"
									VerticalSnapPointsType="OptionalSingle" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
									VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" VerticalSnapPointsAlignment="Near" ZoomMode="Disabled">
									<ItemsPresenter Margin="{ThemeResource ComboBoxDropdownContentMargin}"/>
								</ScrollViewer>
							</Border>
						</Popup>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
</Application.Resources>

Within the App.xaml is defined an Application.Resources with a Style of “CustomComboBox” and a TargetType of ComboBox – this example takes a copy of the style for the standard ComboBox and customises many of the properties including Foreground set to “Gold”, during the VisualState of PointerOver a BorderBrush has been set to “GoldenRod”.

The Border has the default BorderBrush set to “Salmon” and the Background to “LightSalmon” with a CornerRadius set to “15” and the HighlightBackground has the BorderBrush set to “Gold” and the CornerRadius set to “15”, the ContentPresenter also has the CornerRadius set to “15”. The Popup has it’s BorderBrush set to “Salmon” and the Background to “LightSalmon” – some other elements including the Header have been removed to simplify the ComboBox.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<ComboBox SelectedIndex="0" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource CustomComboBox}">
	<ComboBoxItem Content="ComboBox"/>
	<ComboBoxItem Content="One"/>
	<ComboBoxItem Content="Two"/>
	<ComboBoxItem Content="Three"/>
	<ComboBoxItem Content="Four"/>
</ComboBox>

The block of XAML represents the ComboBox Control with the Style of the the CustomCombobox.

Step 11

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 12

After the Application has started running you can then select ComboBox if not already selected and then select one of the items from the ComboBox

ran-custom-combobox

Step 13

To Exit the Application select the Close button in the top right of the Application

vs2017-close

For this example Expression Blend was used to create a copy of the Style for a ComboBox which was then customised with a look-and-feel that’s been used in previous tutorials just to demonstrate how different a standard input control can be modified to look completelt different from the standard, this could be to confirm to a corporate colour scheme or part of a fully customised design, this is just the start of how far you can take customising controls for an application.

Creative Commons License

Universal Windows Platform – Custom TextBox

Custom Textbox demonstrates how to create a custom Style for a TextBox Control

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once the Project is created from the Solution Explorer select App.xaml

vs2017-app

Step 6

From the Menu choose View and then Designer

vs2017-view-designer

Step 7

Once in the Design View for App.xaml between the Application and /Application elements the following should be entered:

<Application.Resources>
	<Style x:Key="CustomTextBox" TargetType="TextBox">
		<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}"/>
		<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}"/>
		<Setter Property="Foreground" Value="Gold"/>
		<Setter Property="Background" Value="{ThemeResource TextControlBackground}"/>
		<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}"/>
		<Setter Property="BorderThickness" Value="2"/>
		<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
		<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
		<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto"/>
		<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto"/>
		<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
		<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
		<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False"/>
		<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="TextBox">
					<Grid>
						<Grid.ColumnDefinitions>
							<ColumnDefinition Width="*"/>
							<ColumnDefinition Width="Auto"/>
						</Grid.ColumnDefinitions>
						<Grid.RowDefinitions>
							<RowDefinition Height="Auto"/>
							<RowDefinition Height="*"/>
						</Grid.RowDefinitions>
						<VisualStateManager.VisualStateGroups>
							<VisualStateGroup x:Name="CommonStates">
								<VisualState x:Name="Disabled">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="HeaderContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlHeaderForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundDisabled}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Normal"/>
								<VisualState x:Name="PointerOver">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="GoldenRod"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundPointerOver}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundPointerOver}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundPointerOver}"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
								<VisualState x:Name="Focused">
									<Storyboard>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="PlaceholderTextContentPresenter">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlPlaceholderForegroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="BorderElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="Gold"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundFocused}"/>
										</ObjectAnimationUsingKeyFrames>
										<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="RequestedTheme" Storyboard.TargetName="ContentElement">
											<DiscreteObjectKeyFrame KeyTime="0" Value="Light"/>
										</ObjectAnimationUsingKeyFrames>
									</Storyboard>
								</VisualState>
							</VisualStateGroup>
						</VisualStateManager.VisualStateGroups>
						<Border x:Name="BorderElement" BorderBrush="Salmon" BorderThickness="{TemplateBinding BorderThickness}" Background="LightSalmon" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="1" CornerRadius="15"/>
						<ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
						HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
						IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}"
						Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
						ZoomMode="Disabled"/>
						<ContentPresenter x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="2" Content="{TemplateBinding PlaceholderText}" Foreground="Gold" IsHitTestVisible="False" Margin="{TemplateBinding BorderThickness}"
						Padding="{TemplateBinding Padding}" Grid.Row="1" TextWrapping="{TemplateBinding TextWrapping}"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
</Application.Resources>

Within the App.xaml is defined an Application.Resources with a Style of “CustomTextBoxStyle” and a TargetType of TextBox – this example takes a copy of the style for the standard TextBox and customises many of the properties including Foreground set to “Gold”, during the VisualState of PointerOver a BorderBrush has been set to “GoldenRod” and the VisualState of Focused has the BorderBrush set to “Gold”.

The Border has the default BorderBrush set to “Salmon” and the Background to “LightSalmon” with a CornerRadius set to “15” and the ContentPresenter has the Foreground set to “Gold” – some other elements including the Header and DeleteButton have been removed to simplify the TextBox.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<TextBox Text="TextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource CustomTextBox}"/>

The block of XAML represents the TextBox Control with the Style of the the CustomTextbox.

Step 11

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 12

After the Application has started running you can then select TextBox if not already and then type in some Text then you can deselect the TextBox once finished

ran-custom-checkbox

Step 13

To Exit the Application select the Close button in the top right of the Application

vs2017-close

For this example Expression Blend was used to create a copy of the standard Style for a TextBox which was then customised with a look-and-feel that’s been used in previous tutorials just to demonstrate how different a standard input control can be modified, more sophisticated customisation is possible – the example is just the start of the idea of creating a completely custom look to an application by modifying controls in this way.

Creative Commons License

Universal Windows Platform – Contacts App

Contacts App demonstrates how to create an application which allows Contacts to be added, viewed, edited or deleted from an in-app Windows 10 Contacts List

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Contacts;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

public class Item
{
    public string Id { get; set; }
    public string Address { get; set; }
    public string DisplayName { get; set; }
}

public class Library
{
    private const string app_title = "Contacts App";

    private async Task Store()
    {
        return await ContactManager.RequestStoreAsync(ContactStoreAccessType.AppContactsReadWrite);
    }

    private InputScope Scope(InputScopeNameValue value)
    {
        InputScope scope = new InputScope();
        InputScopeName name = new InputScopeName()
        {
            NameValue = value
        };
        scope.Names.Add(name);
        return scope;
    }

    private async Task Dialog(Contact contact)
    {
        Thickness margin = new Thickness(5);
        TextBox firstname = new TextBox()
        {
            Text = contact.FirstName,
            Margin = margin,
            PlaceholderText = "First Name"
        };
        TextBox lastname = new TextBox()
        {
            Text = contact.LastName,
            Margin = margin,
            PlaceholderText = "Last Name"
        };
        TextBox email = new TextBox()
        {
            Text = contact?.Emails?.FirstOrDefault()?.Address ?? string.Empty,
            Margin = margin,
            PlaceholderText = "Email Address",
            InputScope = Scope(InputScopeNameValue.EmailSmtpAddress)
        };
        StackPanel panel = new StackPanel()
        {
            Orientation = Orientation.Vertical
        };
        panel.Children.Add(firstname);
        panel.Children.Add(lastname);
        panel.Children.Add(email);
        ContentDialog dialog = new ContentDialog()
        {
            Title = "Contact",
            PrimaryButtonText = "Save",
            CloseButtonText = "Cancel",
            Content = panel
        };
        ContentDialogResult result = await dialog.ShowAsync();
        if (result == ContentDialogResult.Primary)
        {
            contact.FirstName = firstname.Text;
            contact.LastName = lastname.Text;
            contact.Emails.Add(new ContactEmail() { Address = email.Text });
            return contact;
        }
        return null;
    }

    private async Task GetAsync()
    {
        ContactList result = null;
        ContactStore store = await Store();
        if (store != null)
        {
            IReadOnlyList list = await store.FindContactListsAsync();
            if (list.Count == 0)
            {
                result = await store.CreateContactListAsync(app_title);
            }
            else
            {
                result = list.FirstOrDefault(s => s.DisplayName == app_title);
            }
        }
        return result;
    }

    private async Task<IReadOnlyList> ListContactsAsync()
    {
        ContactStore store = await Store();
        if (store != null)
        {
            ContactList list = await GetAsync();
            if (list != null)
            {
                ContactReader reader = list.GetContactReader();
                if (reader != null)
                {
                    ContactBatch batch = await reader.ReadBatchAsync();
                    if (batch != null)
                    {
                        return batch.Contacts;
                    }
                }
            }
        }
        return null;
    }

    private async Task GetContactAsync(string id)
    {
        ContactList list = await GetAsync();
        if (list != null)
        {
            return await list.GetContactAsync(id);
        }
        return null;
    }

    public async Task<List> ListAsync()
    {
        List results = new List();
        IReadOnlyList contacts = await ListContactsAsync();
        foreach (Contact contact in contacts)
        {
            results.Add(new Item
            {
                Id = contact.Id,
                DisplayName = contact.DisplayName,
                Address = contact?.Emails?.FirstOrDefault()?.Address
            });
        }
        return results;
    }

    public async Task AddAsync(AppBarButton button)
    {
        Contact contact = await Dialog(new Contact());
        if (contact != null)
        {
            ContactList list = await GetAsync();
            if (list != null)
            {
                await list.SaveContactAsync(contact);
                return true;
            }
        }
        return false;
    }

    public async Task EditAsync(AppBarButton button)
    {
        Item item = (Item)button.Tag;
        Contact contact = await GetContactAsync(item.Id);
        if (contact != null)
        {
            contact = await Dialog(contact);
            if (contact != null)
            {
                ContactList list = await GetAsync();
                if (list != null)
                {
                    await list.SaveContactAsync(contact);
                    return true;
                }
            }
        }
        return false;
    }

    public async Task DeleteAsync(AppBarButton button)
    {
        Item item = (Item)button.Tag;
        Contact contact = await GetContactAsync(item.Id);
        if (contact != null)
        {
            ContactList list = await GetAsync();
            if (list != null)
            {
                await list.DeleteContactAsync(contact);
                return true;
            }
        }
        return false;
    }
}

There’s an Item class to store a string Id plus a string to store an Address and DisplayName for a Contact. The Library class has a const for the name of Contacts List, then there’s the ContactStore which is specific to the Application, there’s also a method Scope which returns InputScope based on a given InputScopeNameValue. The Dialog method has a Contact passed in and creates a ContentDialog with a given layout to include the First Name, Last Name and Email Address of a Contact object, it also returns the values that have been entered as a Contact when the Save button is selected.

The GetAsync method uses the ContactStore to get the ContactList for the application with FindContactListsAsync and creates one if it doesn’t exist with CreateContactListAsync, ListContactsAsync gets the a list of Contact from the ContactList via ContactReader with GetContactReader and ContactBatch with ReadBatchAsync. GetContactAsync gets an Contact from the ContactList with the GetContactAsync method. The ListAsync is used to populate the list of Item which is based from a list of Contact from the ListContactsAsync method. The Add method uses Dialog to create a new Contact and writes it to the ContactList with SaveContactAsync, Edit uses Dialog with an existing Contact from the list in the UI to make changes and then save this with SaveContactAsync and Delete will remove an existing Contact from the ContactList with the DeleteContactAsync method.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<ListBox Name="Display" Loaded="Display_Loaded">
		<ListBox.ItemContainerStyle>
			<Style TargetType="ListBoxItem">
				<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
			</Style>
		</ListBox.ItemContainerStyle>
		<ListBox.ItemTemplate>
			<DataTemplate>
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="Auto"/>
					</Grid.ColumnDefinitions>
					<Grid Padding="5" Grid.Column="0" Background="{ThemeResource AccentButtonBackground}">
						<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"
					Foreground="{ThemeResource AccentButtonForeground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="1" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding Address}" VerticalAlignment="Center"
					Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<StackPanel Grid.Column="3" Orientation="Horizontal">
						<AppBarButton Name="Edit" Icon="Edit" Label="Edit" Tag="{Binding}" Click="Edit_Click"/>
						<AppBarButton Name="Delete" Icon="Delete" Label="Delete" Tag="{Binding}" Click="Delete_Click"/>
					</StackPanel>
				</Grid>
			</DataTemplate>
		</ListBox.ItemTemplate>
	</ListBox>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Name="Add" Icon="Add" Label="New" Click="Add_Click"/>
</CommandBar>

The MainPage has a Listbox to display Contacts with an ItemTemplate to define how this looks. The second block of XAML is is the CommandBar which contains Add – to add a Contact.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private async void Display_Loaded(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = await library.ListAsync();
}

private async void Add_Click(object sender, RoutedEventArgs e)
{
	if (await library.AddAsync(Add))
	{
		Display.ItemsSource = await library.ListAsync();
	}
}

private async void Edit_Click(object sender, RoutedEventArgs e)
{
	if (await library.EditAsync((AppBarButton)sender))
	{
		Display.ItemsSource = await library.ListAsync();
	}
}

private async void Delete_Click(object sender, RoutedEventArgs e)
{
	if (await library.DeleteAsync((AppBarButton)sender))
	{
		Display.ItemsSource = await library.ListAsync();
	}
}

Below the MainPage() Method an instance of the Library Class is created, then Display_Loaded is used to set the ListBox to show Contacts, Add_Click is used to create Contacts then updates the ListBox which is also done by Edit_Click to amend an existing Contact and Delete_Click is used to remove a Contact.

Step 13

In the Solution Explorer select Package.appxmanifest

vs2017-mainpage

Step 14

From the Menu choose View and then Designer

vs2017-view-designer

Step 15

Finally in the Package.appxmanifest select Capabilities and then make sure the Contacts option is checked

vs2017-manifest-contacts-app

Step 16

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 17

After the Application has started running you can then select Add to create a new Contact, this will display a Dialog where you can enter the First Name, Last Name and Email Address for a Contact. Contacts added will be displayed in the List where you can Edit or Delete them.

ran-contacts-app

Step 18

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This example shows how easy it is to integrate with the built-in Contacts functionality of Windows 10 – you always need to provide your own UI to add / edit or view any Contacts but it’s quite straightforward to implement and is very similar to how Appointment works with Calendars. You can set many more properties of a Contact beyond those shown to add items such as multiple Email Address types and Telephone Numbers.

Creative Commons License

Universal Windows Platform – Calendar App

Calendar App demonstrates how to create an application which allows Appointments to be added, viewed, edited or deleted from an in-app Windows 10 Calendar.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Appointments;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

public class Item
{
    public string Id { get; set; }
    public DateTimeOffset StartTime { get; set; }
    public string When { get { return StartTime.ToString("dd MMMM yyyy HH:mm:ss"); } }
    public string Subject { get; set; }
    public string Details { get; set; }
}

public class Library
{
    private const string app_title = "Calendar App";

    private async Task Store()
    {
        return await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AppCalendarsReadWrite);
    }

    private DateTime GetDateTime(DateTimeOffset date, TimeSpan time)
    {
        return new DateTime(date.Year, date.Month, date.Day, time.Hours, time.Minutes, time.Seconds);
    }

    private async Task Dialog(Appointment appointment, ResourceDictionary resources)
    {
        Thickness margin = new Thickness(5);
        DatePicker date = new DatePicker()
        {
            Date = appointment.StartTime,
            Margin = margin,
            HorizontalAlignment = HorizontalAlignment.Stretch
        };
        TimePicker time = new TimePicker()
        {
            Time = appointment.StartTime.TimeOfDay,
            Margin = margin,
            HorizontalAlignment = HorizontalAlignment.Stretch
        };
        TextBox subject = new TextBox()
        {
            Text = appointment.Subject,
            Margin = margin,
            PlaceholderText = "Subject"
        };
        TextBox details = new TextBox()
        {
            Text = appointment.Details,
            Margin = margin,
            PlaceholderText = "Details",
            AcceptsReturn = true,
            TextWrapping = TextWrapping.Wrap,
            Height = (double)resources["SearchBoxSuggestionPopupThemeMaxHeight"]
        };
        StackPanel panel = new StackPanel()
        {
            Orientation = Orientation.Vertical
        };
        panel.Children.Add(date);
        panel.Children.Add(time);
        panel.Children.Add(subject);
        panel.Children.Add(details);
        ContentDialog dialog = new ContentDialog()
        {
            Title = "Appointment",
            PrimaryButtonText = "Save",
            CloseButtonText = "Cancel",
            Content = panel
        };
        ContentDialogResult result = await dialog.ShowAsync();
        if (result == ContentDialogResult.Primary)
        {
            appointment.StartTime = GetDateTime(date.Date, time.Time);
            appointment.Subject = subject.Text;
            appointment.Details = details.Text;
            return appointment;
        }
        return null;
    }

    private async Task GetAsync()
    {
        AppointmentCalendar result = null;
        AppointmentStore store = await Store();
        if (store != null)
        {
            IReadOnlyList list = await store.FindAppointmentCalendarsAsync();
            if (list.Count == 0)
            {
                result = await store.CreateAppointmentCalendarAsync(app_title);
            }
            else
            {
                result = list.FirstOrDefault(s => s.DisplayName == app_title);
            }
        }
        return result;
    }

    private async Task<IReadOnlyList> ListAppointmentsAsync(DateTimeOffset start, TimeSpan range)
    {
        AppointmentStore store = await Store();
        if (store != null)
        {
            AppointmentCalendar calendar = await GetAsync();
            if (calendar != null)
            {
                return await calendar.FindAppointmentsAsync(start, range);
            }
        }
        return null;
    }

    private async Task GetAppointmentAsync(string id)
    {
        AppointmentCalendar calendar = await GetAsync();
        if (calendar != null)
        {
            return await calendar.GetAppointmentAsync(id);
        }
        return null;
    }

    public void Start(ref CalendarDatePicker picker)
    {
        DateTime now = DateTime.Now;
        if (picker.Date == null)
        {
            picker.Date = GetDateTime(DateTime.Now, new TimeSpan(0, 0, 0));
        }
    }

    public async Task<List> ListAsync(DateTimeOffset start)
    {
        start = start.AddDays(-1);
        DateTimeOffset finish = start.AddMonths(1);
        TimeSpan range = finish - start;
        List results = new List();
        IReadOnlyList appointments = await ListAppointmentsAsync(start, range);
        foreach (Appointment appointment in appointments)
        {
            results.Add(new Item()
            {
                Id = appointment.LocalId,
                StartTime = appointment.StartTime,
                Subject = appointment.Subject,
                Details = appointment.Details
            });
        }
        return results;
    }

    public async Task AddAsync(AppBarButton button, ResourceDictionary resources)
    {
        Appointment appointment = await Dialog(new Appointment(), resources);
        if (appointment != null)
        {
            AppointmentCalendar calendar = await GetAsync();
            if (calendar != null)
            {
                await calendar.SaveAppointmentAsync(appointment);
                return true;
            }
        }
        return false;
    }

    public async Task EditAsync(AppBarButton button, ResourceDictionary resources)
    {
        Item item = (Item)button.Tag;
        Appointment appointment = await GetAppointmentAsync(item.Id);
        if (appointment != null)
        {
            appointment = await Dialog(appointment, resources);
            if (appointment != null)
            {
                AppointmentCalendar calendar = await GetAsync();
                if (calendar != null)
                {
                    await calendar.SaveAppointmentAsync(appointment);
                    return true;
                }
            }
        }
        return false;
    }

    public async Task DeleteAsync(AppBarButton button)
    {
        Item item = (Item)button.Tag;
        Appointment appointment = await GetAppointmentAsync(item.Id);
        if (appointment != null)
        {
            AppointmentCalendar calendar = await GetAsync();
            if (calendar != null)
            {
                await calendar.DeleteAppointmentAsync(item.Id);
                return true;
            }
        }
        return false;
    }
}

There’s an Item class to store a string Id plus a DateTimeOffset and string to store when appointment is for plus the Subject and Details as a string. The Library class has a const for the name of Appointment Calendar, then there’s the AppointmentStore which is specific to the Application, there’s also a method GetDateTime to get the Date and Time from a DateTimeOffset and TimeSpan. The Dialog method has an Appointment and ResourceDictionary passed in and creates a ContentDialog with a given layout to include the Date, Time, Subject and Details of an Appointment object, it also returns the values that have been entered as an Appointment when the Save button is selected.

The GetAsync method uses the AppointmentStore to get the AppointmentCalendar for the application with FindAppointmentCalendarsAsync and creates one if it doesn’t exist with CreateAppointmentCalendarAsync, ListAppointmentsAsync gets the a list of Appointment from the AppointmentCalendar within a given period from DateTimeOffsetStart to a TimeSpan range with FindAppointmentsAsync. GetAppointmentAsync gets an Appointment from the AppointmentCalendar with the GetAppointmentAsync method. Start is used to get the initial CalendarDatePicker date used by the UI to define the start date to display appointments from to be from today’s day – with DateTime.Now. ListAsync is used to populate the list of Item which is based from a list of Appointment from the ListAppointmentsAsync method. The Add method uses Dialog to create a new Appointment and writes it to the AppointmentCalendar with SaveAppointmentAsync, Edit uses Dialog with an existing Appointment from the list in the UI to make changes and then save this with SaveAppointmentAsync and Delete will remove an existing Appointment from the AppointmentCalendar with the DeleteAppointmentAsync method.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto"/>
		<RowDefinition Height="*"/>
	</Grid.RowDefinitions>
	<CalendarDatePicker Grid.Row="0" Name="Picker" HorizontalAlignment="Stretch"
	PlaceholderText="Display Appointments From" DateChanged="Picker_DateChanged"/>
	<ListBox Grid.Row="1" Name="Display" Loaded="Display_Loaded">
		<ListBox.ItemContainerStyle>
			<Style TargetType="ListBoxItem">
				<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
			</Style>
		</ListBox.ItemContainerStyle>
		<ListBox.ItemTemplate>
			<DataTemplate>
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="Auto"/>
					</Grid.ColumnDefinitions>
					<Grid Padding="5" Grid.Column="0" Background="{ThemeResource AccentButtonBackground}">
						<TextBlock Text="{Binding When}" VerticalAlignment="Center"
					Foreground="{ThemeResource AccentButtonForeground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="1" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding Subject}" VerticalAlignment="Center"
					Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<StackPanel Grid.Column="3" Orientation="Horizontal">
						<AppBarButton Name="Edit" Icon="Edit" Label="Edit" Tag="{Binding}" Click="Edit_Click"/>
						<AppBarButton Name="Delete" Icon="Delete" Label="Delete" Tag="{Binding}" Click="Delete_Click"/>
					</StackPanel>
				</Grid>
			</DataTemplate>
		</ListBox.ItemTemplate>
	</ListBox>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Name="Add" Icon="Add" Label="New" Click="Add_Click"/>
</CommandBar>

The MainPage has a Grid with two rows – the first is a CalendarDatePicker and the second is a Listbox to display Appointments with an ItemTemplate to define how this looks. The second block of XAML is is the CommandBar which contains Add – to add an Appointment.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private async void Display_Loaded(object sender, RoutedEventArgs e)
{
	library.Start(ref Picker);
	Display.ItemsSource = await library.ListAsync((DateTimeOffset)Picker.Date);
}

private async void Picker_DateChanged(CalendarDatePicker sender, CalendarDatePickerDateChangedEventArgs args)
{
	Display.ItemsSource = await library.ListAsync((DateTimeOffset)Picker.Date);
}

private async void Add_Click(object sender, RoutedEventArgs e)
{
	if (await library.AddAsync(Add, App.Current.Resources))
	{
		Display.ItemsSource = await library.ListAsync((DateTimeOffset)Picker.Date);
	}
}

private async void Edit_Click(object sender, RoutedEventArgs e)
{
	if (await library.EditAsync((AppBarButton)sender, App.Current.Resources))
	{
		Display.ItemsSource = await library.ListAsync((DateTimeOffset)Picker.Date);
	}
}

private async void Delete_Click(object sender, RoutedEventArgs e)
{
	if (await library.DeleteAsync((AppBarButton)sender))
	{
		Display.ItemsSource = await library.ListAsync((DateTimeOffset)Picker.Date);
	}
}

Below the MainPage() Method an instance of the Library Class is created, then Display_Loaded is used to set the ListBox to show Appointments, Picker_DateChanged is used to set the Appointments to show in the ListBox, Add_Click is used to create Appointments then updates the ListBox which is also done by Edit_Click to amend an existing Appointment and Delete_Click is used to remove an Appointment.

Step 13

In the Solution Explorer select Package.appxmanifest

vs2017-manifest-library

Step 14

From the Menu choose View and then Designer

vs2017-view-designer

Step 15

Finally in the Package.appxmanifest select Capabilities and then make sure the Appointments option is checked

vs2017-manifest-calendar-app

Step 16

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 17

After the Application has started running you can then select Add to create a new Appointment, this will display a Dialog where you can enter the Date, Time, Subject and Details for an Appointment. Appointments added will be displayed in the List where you can Edit or Delete them and you can control the date range to start showing Appointments from with the CalendarDatePicker at the top.

ran-calendar-app

Step 18

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This example shows how to integrate Appointments with the Windows 10 Calendars – any Appointment added will show up in the system calendars under the name of “Calendar App” – it’s also important that this is an extended Capability which is why the option for allowing access to Appointments in the Application Manifest is important otherwise the functionality won’t work.

Creative Commons License

Universal Windows Platform – Speaking App

Speaking App demonstrates how to create an application which allows text files to be opened then read out in different Voices using text-to-speech – they can also be saved back as a Text file or the spoken version as an Audio file.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Media.SpeechSynthesis;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

public class Library
{
    private const string extension_txt = ".txt";
    private const string extension_wav = ".wav";

    private SpeechSynthesizer synth = new SpeechSynthesizer();

    private async Task OpenAsync()
    {
        try
        {
            FileOpenPicker picker = new FileOpenPicker()
            {
                SuggestedStartLocation = PickerLocationId.ComputerFolder
            };
            picker.FileTypeFilter.Add(extension_txt);
            StorageFile open = await picker.PickSingleFileAsync();
            if (open != null)
            {
                return await FileIO.ReadTextAsync(open);
            }
        }
        finally
        {
        }
        return null;
    }

    private async void SaveAsync(string contents)
    {
        try
        {
            FileSavePicker picker = new FileSavePicker()
            {
                SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
                DefaultFileExtension = extension_txt,
                SuggestedFileName = "Document"
            };
            picker.FileTypeChoices.Add("Text File", new List() { extension_txt });
            picker.FileTypeChoices.Add("Wave File", new List() { extension_wav });
            StorageFile save = await picker.PickSaveFileAsync();
            if (save != null)
            {
                if (save.FileType == extension_txt)
                {
                    await FileIO.WriteTextAsync(save, contents);
                }
                else if (save.FileType == extension_wav)
                {
                    using (SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(contents))
                    {
                        using (DataReader reader = new DataReader(stream))
                        {
                            await reader.LoadAsync((uint)stream.Size);
                            IBuffer buffer = reader.ReadBuffer((uint)stream.Size);
                            await FileIO.WriteBufferAsync(save, buffer);
                        }
                    }
                }
            }
        }
        finally
        {
        }
    }

    private async void Speak(string text, MediaElement media)
    {
        try
        {
            if (media.CurrentState == MediaElementState.Playing)
            {
                media.Stop();
            }
            else
            {
                SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
                media.AutoPlay = true;
                media.SetSource(stream, stream.ContentType);
                media.Play();
            }
        }
        finally
        {
        }
    }

    public Dictionary Voices()
    {
        Dictionary results = new Dictionary();
        foreach (VoiceInformation voice in SpeechSynthesizer.AllVoices.OrderBy(o => o.DisplayName))
        {
            results.Add(voice.Id, voice.DisplayName);
        }
        return results;
    }

    public void Voice(string id)
    {
        synth.Voice = SpeechSynthesizer.AllVoices.First(f => f.Id == id);
    }

    public void New(ref TextBox text, ref MediaElement media)
    {
        media.Source = null;
        text.Text = string.Empty;
    }

    public async void Open(TextBox text)
    {
        string content = await OpenAsync();
        if (content != null)
        {
            text.Text = content;
        }
    }

    public void Save(ref TextBox text)
    {
        SaveAsync(text.Text);
    }

    public void Play(ref TextBox text, ref MediaElement media)
    {
        Speak(text.Text, media);
    }
}

The Library class defines two const for file extensions for “.txt” and “.wav” then there’s a SpeechSynthesizer which is the core of the text-to-speak functionality. The OpenAsync method uses a FileOpenPicker to select a file with the “.txt” extension and returns a string using PickSingleFileAsync and ReadTextAsync to get the contents of the Text file. The SaveAsync takes in contexts for a Text file as a string and uses a FileSavePicker with a default extension as “.txt” but also there’s two file type choices – one is the “.txt” and the other is the “.wav” extension to get the StorageFile with PickSaveFileAsync, then if the extension is “.txt” then the file is saved with WriteTextAsync, it the extension is “.wav” then a SpeechSynthesisStream is used with a DataReader and LoadAsync to get the contents of the SpeechSynthesisStream as a IBuffer which is then written using WriteBufferAsync.

The Speak method takes in string text to read and a MediaElement to use for playback – if the this is Playing then Stop is called otherwise a SpeechSynthesisStream is obtained from SynthesizeTextToStreamAsync using the text and then the MediaElement is set to have AutoPlay to true and the SetSource set from the SpeechSynthesisStream. Then Voices is used to get a list of all the voices that the SpeechSynthesizer supports and Voice gets a Voice by an Id, New is used to reset the TextBox and MediaElement, Open reads in a Text file, Save writes to a Text File or Wave File and Play reads the Text contents using text-to-speech.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50" Name="Display" Loaded="Display_Loaded">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto"/>
		<RowDefinition Height="*"/>
	</Grid.RowDefinitions>
	<ComboBox Grid.Row="0" Name="Voice" HorizontalAlignment="Stretch"
		SelectedValuePath="Key" DisplayMemberPath="Value"
		SelectionChanged="Voice_SelectionChanged"/>
	<TextBox Grid.Row="1" Name="Input" AcceptsReturn="True" TextWrapping="Wrap"/>
	<MediaElement Name="Media" AutoPlay="False"/>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Icon="Page2" Label="New" Click="New_Click"/>
	<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click"/>
	<AppBarButton Icon="Save" Label="Save" Click="Save_Click"/>
	<AppBarButton Icon="Play" Label="Play" Click="Play_Click"/>
</CommandBar>

The MainPage has a Grid with two rows – the first represents the ComboBox for selecting Voices from then the second row has a TextBox for entering text to be read out with text-to-speech and there’s also a MediaElement. The second block of XAML is is the CommandBar which contains New – to start a new Document, Open – to get a Document, Save – to store a Document and Play to initiate the text-to-speech.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private void Display_Loaded(object sender, RoutedEventArgs e)
{
	Voice.ItemsSource = library.Voices();
	Voice.SelectedIndex = 0;
}

private void Voice_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
	library.Voice((string)Voice.SelectedValue);
}

private void New_Click(object sender, RoutedEventArgs e)
{
	library.New(ref Input, ref Media);
}

private void Open_Click(object sender, RoutedEventArgs e)
{
	library.Open(Input);
}

private void Save_Click(object sender, RoutedEventArgs e)
{
	library.Save(ref Input);
}

private void Play_Click(object sender, RoutedEventArgs e)
{
	library.Play(ref Input, ref Media);
}

Below the MainPage() Method an instance of the Library Class is created, then Display_Loaded is used to set the ComboBox to the Voices to use for the text-to-speech, Voice_SelectionChanged to use the selected Voice, New_Click to call the New Method in the Library Class, Open_Click, Save_Click and Play_Click to call the related Methods in the Library Class.

Step 13

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 14

After the Application has started running you can then select New to reset the TextBox or use Open to set the content to a Text Document or Save contents to a Text Document or Speech to a Wave File and use Play to convert content from text-to-speech – you can choose the Voice to speak with from the ComboBox.

ran-speaking-app

Step 15

To Exit the Application select the Close button in the top right of the Application

vs2017-close

Text-to-speech is an interesting technology to use as it enables additional options and functionality when thinking about text beyond what can be done visually by building in the functionality you can have dictation features within any application with just a few lines of code to turn text into speech. This example shows how easy it is to use the SpeechSynthesizer to translate simple text into speech, it also supports Speech Synthesis Markup Language (SSML) which allows more finer control over how the context is spoken if needed.

Creative Commons License

Universal Windows Platform – Operator App

Operator App demonstrates how to create a basic Calculator based on some common C# Operators for addition, subtraction, multiplication and division.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.Windows.Input;
using Windows.UI.Xaml.Controls;

public class CommandHandler : ICommand
{
    public event EventHandler CanExecuteChanged = null;
    private Action _action;

    public CommandHandler(Action action)
    {
        _action = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _action();
    }
}

public class Item
{
    public enum ItemMode
    {
        Operand = 1,
        Operator = 2
    }

    public ItemMode Mode { get; set; }
    public Func Action { get; set; }
    public ICommand Command { get { return new CommandHandler(() => this.Action(this)); } }
    public object Value { get; set; }
}

public class Library
{
    private static double first_operand;
    private static double second_operand;
    private static string operation = null;

    private static TextBlock _output;

    private static Func action = (Item item) =>
    {
        switch (item.Mode)
        {
            case Item.ItemMode.Operand:
                _output.Text += (int)item.Value;
                break;
            case Item.ItemMode.Operator:
                string selected = (string)item.Value;
                if (selected == "=")
                {
                    double result = 0.0;
                    if (_output.Text.Length > 0)
                    {
                        second_operand = double.Parse(_output.Text);
                        switch (operation)
                        {
                            case "/":
                                if (second_operand > 0)
                                {
                                    result = first_operand / second_operand;
                                }
                                break;
                            case "*":
                                result = first_operand * second_operand;
                                break;
                            case "-":
                                result = first_operand - second_operand;
                                break;
                            case "+":
                                result = first_operand + second_operand;
                                break;
                        }
                        _output.Text = result.ToString();
                    }
                }
                else if (selected == " 0)
                    {
                        _output.Text = _output.Text.Substring(0, _output.Text.Length - 1);
                    }
                }
                else
                {
                    if (_output.Text.Length > 0)
                    {
                        first_operand = int.Parse(_output.Text);
                        _output.Text = string.Empty;
                        operation = item.Value.ToString();
                    }
                }
                break;
        }
        return 0;
    };

    private List list = new List
    {
        new Item { Mode = Item.ItemMode.Operand, Value = 7, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 8, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 9, Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "/", Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 4, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 5, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 6, Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "*", Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 1, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 2, Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 3, Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "-", Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "<", Action = action },
        new Item { Mode = Item.ItemMode.Operand, Value = 0, Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "=", Action = action },
        new Item { Mode = Item.ItemMode.Operator, Value = "+", Action = action },
    };

    public List New(ref TextBlock text)
    {
        _output = text;
        return list;
    }
}

There’s a CommandHandler Class that will help handle any commanding events and actions from the Library Class. There’s also a Item Class with an ItemMode for either Operand or Operator, an Action to represent what happens during commanding and an ICommand value that uses the CommandHandler and defines what’s passed into the Action and there’s a Value for storing content for the Item.

The Library Class defines two double variables to store the operands used in a Calculation and there’s a string for the kind of Calculation operation to be performed and a variable for a TextBlock for Output. A lambda function is defined which defines what an Action will do during commanding and has a switch statement which will if the ItemMode is Operand will append the TextBlock with the Value from an Item Object, if the ItemMode is Operator then it will check if that is an = which will product the result of a Calculation or if not then will use another switch statement to determine if it is /, *, or + to perform the selected kind of Calculation – there’s also check to see if the Operator is < which will remove numbers from the Output to be used if any mistakes entered. There’s then a List of Item which defined all the Operand and Operator items supported and sets the Action to be the same for each one and then a New method which sets a passed in TextBlock for Output and returns the list of Item.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Viewbox Margin="50">
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition Height="Auto"/>
			<RowDefinition Height="*"/>
		</Grid.RowDefinitions>
		<TextBlock Grid.Row="0" Name="Output" TextAlignment="Right" FontSize="30"
		HorizontalAlignment="Stretch" Foreground="{ThemeResource AccentButtonBackground}"/>
		<ItemsControl Grid.Row="1" Name="Display" Loaded="Display_Loaded">
			<ItemsControl.ItemTemplate>
				<DataTemplate>
					<Button Margin="5" Height="50" Width="50"
				Background="{ThemeResource AccentButtonBackground}" Command="{Binding Command}">
						<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20"
						Foreground="{ThemeResource AccentButtonForeground}" Text="{Binding Value}"/>
					</Button>
				</DataTemplate>
			</ItemsControl.ItemTemplate>
			<ItemsControl.ItemsPanel>
				<ItemsPanelTemplate>
					<ItemsWrapGrid MaximumRowsOrColumns="4" Orientation="Horizontal"/>
				</ItemsPanelTemplate>
			</ItemsControl.ItemsPanel>
		</ItemsControl>
	</Grid>
</Viewbox>

The MainPage has a Grid with two rows – the first represents the TextBlock for output and sets the Foreground and Background properties amongst others. Then there’s an ItemsControl which has it’s ItemsSource set to the New method from the Library Class in the Display_Loaded Event, within this is a DataTemplate defining the Button and also has the Foreground and Background properties set plus the Command for commanding to trigger the appropriate Action – to make sure the layout appears correctly the ItemsPanelTemplate is set to be a ItemsWrapGrid with MaximumRowsOrColumns of 4 and the Orientation set to Horizontal.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private void Display_Loaded(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = library.New(ref Output);
}

Below the MainPage() Method an instance of the Library Class is created, then Display_Loaded is used to set the ItemsSource of the ItemsControl to display the look-and-feel of the application.

Step 13

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 14

After the Application has started running you can then select any of the Button controls and select either an Operator such as *, +, /, or < to delete a number from the display or any number from 0 to 9 then you can use = to get the answer.

ran-operator-app

Step 15

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This is by no means a fully featured calculator – for example there’s no . but it could be extended to support other Operators from C# such as % for Modulus, the main point of the example was to show the flexability of combining commanding with a list of items to product the necessary functionality of an application and to implement the layout.

Creative Commons License

Universal Windows Platform – ZipFile App

ZipFile App demonstrates how to create New and Open Zip archives plus Add, Remove and Extract files.

Step 1

If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu find and select Visual Studio 2017.

vs2017

Step 2

Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…

vs2017-file-new-project

Step 3

From New Project choose Visual C# from Installed, Templates then choose Blank App (Universal Windows) and then type in a Name and select a Location and then select Ok to create the Project
vs2017-new-project-window

Step 4

Then in New Universal Windows Project you need to select the Target Version this should be at least the Windows 10, version 1803 (10.0; Build 17134) which is the April 2018 Update and the Minimum Version to be the same.

vs2017-target-platform

The Target Version will control what features your application can use in Windows 10 so by picking the most recent version you’ll be able to take advantage of those features. To make sure you always have the most recent version, in Visual Studio 2017 select Tools Extensions and Updates… then and then see if there are any Updates

Step 5

Once done select from the Menu, Project, then Add New Item…

vs2017-project-add-new-item

Step 6

From the Add New Item window select Visual C#, then Code from Installed then select Code File from the list, then type in the Name as Library.cs before selecting Add to add the file to the Project

vs2017-add-new-item-library

Step 7

Once in the Code View for Library.cs the following should be entered:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;

public class ZipItem
{
    public string Name { get; set; }
    public string ActualBytes { get; set; }
    public string CompressedBytes { get; set; }
}

public class Library
{
    public const string extension_all = "*";
    public const string extension_zip = ".zip";
    public const string desc_archive = "Archive";
    public const string desc_file = "File";

    private string _token = string.Empty;
    StorageItemAccessList _access = StorageApplicationPermissions.FutureAccessList;

    public string Name { get; set; }

    private async Task GetByteFromFile(StorageFile storageFile)
    {
        using (IRandomAccessStream stream = await storageFile.OpenReadAsync())
        {
            using (DataReader dataReader = new DataReader(stream))
            {
                byte[] bytes = new byte[stream.Size];
                await dataReader.LoadAsync((uint)stream.Size);
                dataReader.ReadBytes(bytes);
                return bytes;
            }
        }
    }

    private async Task OpenFileAsync(string extension)
    {
        try
        {
            FileOpenPicker picker = new FileOpenPicker()
            {
                SuggestedStartLocation = PickerLocationId.ComputerFolder
            };
            picker.FileTypeFilter.Add(extension);
            StorageFile open = await picker.PickSingleFileAsync();
            if (open != null)
            {
                return open;
            }
        }
        finally
        {
        }
        return null;
    }

    private async Task SaveFileAsync(string filename, string extension, string description)
    {
        try
        {
            FileSavePicker picker = new FileSavePicker()
            {
                SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
                DefaultFileExtension = extension,
                SuggestedFileName = filename
            };
            picker.FileTypeChoices.Add(description, new List() { extension });
            StorageFile save = await picker.PickSaveFileAsync();
            if (save != null)
            {
                return save;
            }
        }
        finally
        {
        }
        return null;
    }

    public async Task<List> List()
    {
        List results = null;
        StorageFile file = await _access.GetFileAsync(_token);
        if (file != null)
        {
            using (Stream stream = await file.OpenStreamForReadAsync())
            {
                using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read))
                {
                    results = new List();
                    foreach (ZipArchiveEntry entry in archive.Entries)
                    {
                        if (entry.Name != string.Empty)
                        {
                            results.Add(new ZipItem()
                            {
                                Name = entry.Name,
                                CompressedBytes = $"Compressed Size {entry.CompressedLength}",
                                ActualBytes = $"Actual Size {entry.Length}"
                            });
                        }
                    }
                }
            }
        }
        return results;
    }

    public async Task NewAsync()
    {
        StorageFile file = await SaveFileAsync(desc_archive, extension_zip, desc_archive);
        if (file != null)
        {
            _token = _access.Add(file);
            Name = file.Name;
            using (Stream stream = await file.OpenStreamForWriteAsync())
            {
                new ZipArchive(stream, ZipArchiveMode.Create);
            }
            return true;
        }
        return false;
    }

    public async Task<List> OpenAsync()
    {
        StorageFile file = await OpenFileAsync(extension_zip);
        if (file != null)
        {
            Name = file.Name;
            _token = _access.Add(file);
        }
        return await List();
    }

    public async Task<List> AddAsync()
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                StorageFile source = await OpenFileAsync(extension_all);
                if (source != null)
                {
                    using (Stream stream = await file.OpenStreamForWriteAsync())
                    {
                        using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Update))
                        {
                            ZipArchiveEntry existing = zipArchive.GetEntry(source.Name);
                            if (existing != null)
                            {
                                existing.Delete();
                            }
                            ZipArchiveEntry entry = zipArchive.CreateEntry(source.Name);
                            using (Stream entryStream = entry.Open())
                            {
                                byte[] data = await GetByteFromFile(source);
                                entryStream.Write(data, 0, data.Length);
                            }
                        }
                    }
                }
            }
            return await List();
        }
        return null;
    }

    public async void Extract(ZipItem item)
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                using (Stream stream = await file.OpenStreamForReadAsync())
                {
                    using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read))
                    {
                        ZipArchiveEntry entry = archive.GetEntry(item.Name);
                        if (entry != null)
                        {
                            StorageFile source = await SaveFileAsync(entry.Name, Path.GetExtension(entry.Name), desc_file);
                            if (source != null)
                            {
                                using (Stream output = await source.OpenStreamForWriteAsync())
                                {
                                    await entry.Open().CopyToAsync(output);
                                    await output.FlushAsync();
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public async Task<List> RemoveAsync(ZipItem item)
    {
        if (!string.IsNullOrEmpty(_token))
        {
            StorageFile file = await _access.GetFileAsync(_token);
            if (file != null)
            {
                using (Stream stream = await file.OpenStreamForWriteAsync())
                {
                    using (ZipArchive zipArchive = new ZipArchive(stream, ZipArchiveMode.Update))
                    {
                        ZipArchiveEntry entry = zipArchive.GetEntry(item.Name);
                        if (entry != null)
                        {
                            entry.Delete();
                        }
                    }
                }
                return await List();
            }
        }
        return null;
    }
}

There’s a ZipItem Class to represent each item within the Archive then in the Library Class there are const values to set file extensions and descriptions plus variables for a “token” and StorageItemAccessList. There’s a GetByteFromFile method that converts a StorageFile into a byte[] with the use of a IRandomAccessStream plus a DataReader. The OpenFileAsync method uses a FileOpenPicker to select a file with the passed in extension and returns a StorageFile and SaveFileAsync uses a FileSavePicker to return a StorageFile that will be created and written to with the passed in filename, extension and description, and there’s a string property to store the name of the archive.

Also in the Library Class there’s a List method that uses a StorageFile from the StorageItemAccessList using the “token” – if this file is present then a Stream is used to then get the ZipArchive from and then each ZipArchiveEntry in the archive is looped through to create a list of ZipItem and is returned my that method. The NewAsync method uses the SaveFileAsync to create the StorageFile to create a new archive and returns true if successful or false if not. The OpenAsync method uses the OpenFileAsync to open an archive from the StorageFile and returns the list of ZipItem.

Again also in the Library Class is an AddAsync method uses a StorageFile from the StorageItemAccessList using the “token”. It then uses the OpenFileAsync to get the file to add to the archive – if the archive file is present then a Stream is used to then get the ZipArchive and then it checks for an existing entry – if there is one then it is deleted with Delete if not then a new entry is created with CreateEntry using the GetByteFromFile to insert the file into the archive. The Extract method is used to retrieve a file from an archive and uses a StorageFile from the StorageItemAccessList using the “token” and if this file is present then a Stream is used to then get the ZipArchive and then an entry is retrieved by name with the GetEntry method and then if present the SaveFileAsync method is used to get a StorageFile to write the file Stream to, then there’s a RemoveAsync method to remove a given item from the archive.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-library

Step 9

From the Menu choose View and then Designer

vs2017-view-designer

Step 10

The Design View will be displayed along with the XAML View and in this between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto"/>
		<RowDefinition Height="*"/>
	</Grid.RowDefinitions>
	<TextBlock Grid.Row="0" Name="Label" Margin="5" Style="{StaticResource SubtitleTextBlockStyle}"/>
	<ListBox Grid.Row="1" Name="Display">
		<ListBox.ItemContainerStyle>
			<Style TargetType="ListBoxItem">
				<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
			</Style>
		</ListBox.ItemContainerStyle>
		<ListBox.ItemTemplate>
			<DataTemplate>
				<Grid>
					<Grid.ColumnDefinitions>
						<ColumnDefinition Width="40*"/>
						<ColumnDefinition Width="20*"/>
						<ColumnDefinition Width="20*"/>
						<ColumnDefinition Width="Auto"/>
					</Grid.ColumnDefinitions>
					<Grid Padding="5" Grid.Column="0" Background="{ThemeResource AccentButtonBackground}">
						<TextBlock Text="{Binding Name}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonForeground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="1" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding ActualBytes}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<Grid Padding="5" Grid.Column="2" Background="{ThemeResource AccentButtonForeground}">
						<TextBlock Text="{Binding CompressedBytes}" VerticalAlignment="Center"
						Foreground="{ThemeResource AccentButtonBackground}"/>
					</Grid>
					<StackPanel Grid.Column="3" Orientation="Horizontal">
						<AppBarButton Name="Extract" Icon="SaveLocal" Label="Extract" Tag="{Binding}" Click="Extract_Click"/>
						<AppBarButton Name="Remove" Icon="Remove" Label="Remove" Tag="{Binding}" Click="Remove_Click"/>
					</StackPanel>
				</Grid>
			</DataTemplate>
		</ListBox.ItemTemplate>
	</ListBox>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Icon="Page2" Label="Open" Click="New_Click"/>
	<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click"/>
	<AppBarButton Icon="Add" Label="Add" Click="Add_Click"/>
</CommandBar>

The MainPage has an AppBarButton for New, Open and Add. Then theres a ListBox that will have a list of ZipItem set to it’s DataSource and has a DataTemplate that displays a list of Name, Actual Size and Compressed Size for each entry in an archive plus an AppBarButton for Extract and Remove for each archive entry item displayed.

Step 11

From the Menu choose View and then Code

vs2017-view-code

Step 12

Once in the Code View, below the end of public MainPage() { … } the following Code should be entered:

Library library = new Library();

private async void New_Click(object sender, RoutedEventArgs e)
{
	if (await library.NewAsync())
	{
		Label.Text = library.Name;
	}
}

private async void Open_Click(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = await library.OpenAsync();
	Label.Text = library.Name;
}

private async void Add_Click(object sender, RoutedEventArgs e)
{
	Display.ItemsSource = await library.AddAsync();
}

private void Extract_Click(object sender, RoutedEventArgs e)
{
	ZipItem item = (ZipItem)((AppBarButton)sender).Tag;
	library.Extract(item);
}

private async void Remove_Click(object sender, RoutedEventArgs e)
{
	ZipItem item = (ZipItem)((AppBarButton)sender).Tag;
	Display.ItemsSource = await library.RemoveAsync(item);
}

Below the MainPage() Method an instance of the Library Class is created, then New_Click is used to create a new Zip File, Open_Click will call OpenAsync to open a Zip File and then you can use Add_Click to add to the Archive, Extract_Click to Extract a file and Remove_Click to remove a file from the Archive.

Step 13

That completes the Universal Windows Platform Application so Save the Project then in Visual Studio select the Local Machine to run the Application

vs2017-local-machine

Step 14

After the Application has started running you can then select New to create a Zip archive using a FileSavePicker then you need to use Add to add to the archive using the FileOpenPicker. You can view any files in the archive in the ListBox plus you can Extract a file using a FileSavePicker or Remove a file from the archive.

ran-zipfile-app

Step 15

To Exit the Application select the Close button in the top right of the Application

vs2017-close

This example shows how you can use the ZipArchive compression features in System.IO.Compression and use this within a Universal Windows Platform application – usually this is used directly with paths but due to the sandboxed nature of UWP apps the lesser seen methods demonstrated in the example are used instead. This example demonstrates all the key features of dealing with a zip archive and is pretty close to the functionality within Windows 10 itself for dealing with Zip files.

Creative Commons License