Universal Windows Platform – ZuneView App

ZuneView App shows how to create a simple application that can display any Microsoft Zune with the Devices and Colours that were available and Save an Image of the selected Device and Colour.

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 ZuneViewApp and select a Location and then select Ok to create the Project
vs2017-new-project-zuneview-app

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-zuneview-app

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.Runtime.InteropServices.WindowsRuntime;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace ZuneViewApp
{
    public enum ZuneViewType
    {
        Keel, Draco, Scorpius, Pavo
    }

    public class ZuneViewStyle
    {
        public string Name { get; set; }
        public Color Colour { get; set; }
        public ZuneViewType[] Types { get; set; }

        public ZuneViewStyle(string name, Color colour, ZuneViewType[] types)
        {
            Name = name;
            Types = types;
            Colour = colour;
        }
    }

    public class ZuneViewDevice
    {
        public string Name { get; set; }
        public ZuneViewType Type { get; set; }
        public Brush Fill { get; set; } // Change to Style

        public ZuneViewDevice(ZuneViewType type, Brush fill)
        {
            Type = type;
            Fill = fill;
        }

        public ZuneViewDevice(string name, ZuneViewType type, Brush fill)
        {
            Name = name;
            Type = type;
            Fill = fill;
        }
    }

    public class ZuneViewDeviceTemplateSelector : DataTemplateSelector
    {
        public DataTemplate Keel { get; set; }
        public DataTemplate Draco { get; set; }
        public DataTemplate Scorpius { get; set; }
        public DataTemplate Pavo { get; set; }

        protected override DataTemplate SelectTemplateCore(object value, DependencyObject container)
        {
            if (value is ZuneViewDevice item)
            {
                switch (item.Type)
                {
                    case ZuneViewType.Keel:
                        return Keel;
                    case ZuneViewType.Draco:
                        return Draco;
                    case ZuneViewType.Scorpius:
                        return Scorpius;
                    case ZuneViewType.Pavo:
                        return Pavo;
                }
            }
            return null;
        }
    }

    public class Library
    {
        private const int scale_size = 1000;
        private const double image_dpi = 96.0;
        private const string file_extension = ".png";
        private readonly Color brown = Color.FromArgb(255, 68, 48, 22);
        private readonly Color black = Color.FromArgb(255, 28, 28, 28);
        private readonly Color red = Color.FromArgb(255, 132, 20, 44);
        private readonly Color platinum = Color.FromArgb(255, 220, 220, 220);

        private List<ZuneViewStyle> _styles = new List<ZuneViewStyle>();
        private List<ZuneViewDevice> _devices = new List<ZuneViewDevice>();

        private void AddStyle(string name, Color colour, params ZuneViewType[] types)
        {
            _styles.Add(new ZuneViewStyle(name, colour, types));
        }

        private void SetStyles()
        {
            AddStyle("Brown", brown, ZuneViewType.Keel);
            AddStyle("Halo", Color.FromArgb(255, 70, 90, 90), ZuneViewType.Keel);
            AddStyle("Red", Color.FromArgb(255, 234, 48, 104), ZuneViewType.Keel);
            AddStyle("Pink", Color.FromArgb(255, 228, 182, 194), ZuneViewType.Keel);
            AddStyle("Magenta", Color.FromArgb(255, 230, 62, 100), ZuneViewType.Keel);
            AddStyle("Blue", Color.FromArgb(255, 14, 84, 150), ZuneViewType.Keel);
            AddStyle("Orange", Color.FromArgb(255, 246, 116, 0), ZuneViewType.Keel);
            AddStyle("Black", Color.FromArgb(255, 16, 24, 22), ZuneViewType.Keel);
            AddStyle("White", Color.FromArgb(255, 235, 235, 235),
            ZuneViewType.Keel, ZuneViewType.Draco, ZuneViewType.Scorpius);
            AddStyle("Black", black, ZuneViewType.Draco, ZuneViewType.Scorpius);
            AddStyle("Red", red, ZuneViewType.Draco, ZuneViewType.Scorpius);
            AddStyle("Blue", Color.FromArgb(255, 2, 70, 130),
            ZuneViewType.Draco, ZuneViewType.Scorpius);
            AddStyle("Green", Color.FromArgb(255, 140, 132, 66), ZuneViewType.Scorpius);
            AddStyle("Pink", Color.FromArgb(255, 246, 164, 196), ZuneViewType.Scorpius);
            AddStyle("Citron", Color.FromArgb(255, 210, 198, 6), ZuneViewType.Scorpius);
            AddStyle("Platinum", platinum, ZuneViewType.Pavo);
            AddStyle("Onyx", Color.FromArgb(255, 24, 42, 44), ZuneViewType.Pavo);
            AddStyle("Red", Color.FromArgb(255, 124, 30, 54), ZuneViewType.Pavo);
            AddStyle("Blue", Color.FromArgb(255, 30, 104, 146), ZuneViewType.Pavo);
            AddStyle("Green", Color.FromArgb(255, 166, 172, 92), ZuneViewType.Pavo);
            AddStyle("Pink", Color.FromArgb(255, 192, 162, 182), ZuneViewType.Pavo);
            AddStyle("Magenta", Color.FromArgb(255, 140, 88, 140), ZuneViewType.Pavo);
            AddStyle("Purple", Color.FromArgb(255, 94, 90, 120), ZuneViewType.Pavo);
            AddStyle("Atomic", Color.FromArgb(255, 186, 24, 0), ZuneViewType.Pavo);
        }

        private void AddDevice(string name, ZuneViewType type, Color colour)
        {
            _devices.Add(new ZuneViewDevice(name, type, new SolidColorBrush(colour)));
        }

        private void SetDevices()
        {
            AddDevice("Zune 30", ZuneViewType.Keel, brown);
            AddDevice("Zune 80 120", ZuneViewType.Draco, black);
            AddDevice("Zune 4 8 16", ZuneViewType.Scorpius, red);
            AddDevice("Zune HD", ZuneViewType.Pavo, platinum);
        }

        private void SetItem(ref ItemsControl display, ref ComboBox devices, ref ComboBox styles)
        {
            ZuneViewDevice device = (ZuneViewDevice)devices.SelectedItem ?? _devices.First();
            ZuneViewStyle style = (ZuneViewStyle)styles.SelectedItem ?? _styles.First();
            display.ItemsSource = new List<ZuneViewDevice>()
            {
                new ZuneViewDevice(device.Type, new SolidColorBrush(style.Colour))
            };
        }

        private async void Render(UIElement element, StorageFile file)
        {
            using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
                RenderTargetBitmap target = new RenderTargetBitmap();
                await target.RenderAsync(element, scale_size, 0);
                IBuffer buffer = await target.GetPixelsAsync();
                encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied,
                (uint)target.PixelWidth, (uint)target.PixelHeight, image_dpi, image_dpi, buffer.ToArray());
                await encoder.FlushAsync();
                target = null;
                buffer = null;
                encoder = null;
            }
        }

        public Library()
        {
            SetDevices();
            SetStyles();
        }

        public void Init(ref ItemsControl display, ref ComboBox devices, ref ComboBox styles)
        {
            devices.ItemsSource = _devices;
            styles.ItemsSource = _styles;
            devices.SelectedIndex = 0;
            styles.SelectedIndex = 0;
            SetItem(ref display, ref devices, ref styles);
        }

        public void Style(ref ItemsControl display, ref ComboBox devices, ref ComboBox styles)
        {
            SetItem(ref display, ref devices, ref styles);
        }

        public void Device(ref ItemsControl display, ref ComboBox devices, ref ComboBox styles)
        {
            ZuneViewDevice selected = (ZuneViewDevice)devices.SelectedItem;
            IEnumerable<ZuneViewStyle> list = _styles.Where(w => w.Types.Contains(selected.Type));
            styles.ItemsSource = list;
            styles.SelectedIndex = 0;
            SetItem(ref display, ref devices, ref styles);
        }

        public async void Save(UIElement element, ComboBox devices)
        {
            try
            {
                ZuneViewDevice selected = (ZuneViewDevice)devices.SelectedItem;
                FileSavePicker picker = new FileSavePicker
                {
                    DefaultFileExtension = file_extension,
                    SuggestedFileName = selected.Name,
                    SuggestedStartLocation = PickerLocationId.PicturesLibrary
                };
                picker.FileTypeChoices.Add("Image", new List<string>() { file_extension });
                StorageFile file = await picker.PickSaveFileAsync();
                if (file != null)
                {
                    Render(element, file);
                }
            }
            finally
            {
                // Ignore Exceptions
            }
        }
    }
}

In the Code File for Library there are using statements to include the necessary functionality. There is an enum for ZuneViewType for the different types of Zune Device.

The ZuneViewStyle Class has Properties for the Name of the Style, Color and an Array of ZuneViewType. Thee ZuneViewDevice Class has Properties for the Name of the Device, the ZuneViewType and Brush. The ZuneViewDeviceTemplateSelector is used to return the appropriate DataTemplate based on ZuneViewDevice.

The Library Class has various const and readonly Values including Color for examples for each Type of Device. There are List of ZuneViewStyle and List if ZuneViewDevice Members. There is a SetStyles Method which uses the AddStyle Method to add the Names, Colours and Devices for each Style and there is a SetDevices Method which uses the AddDevice Method to add the Names, Types and Example Colours of each Device. There is a Render Method which takes a UIElement and StorageFile – with an IRandomAccessStream from this it will use a BitmapEncoder with RenderTargetBitmap to create the Image to be Rendered.

Also in the Library Class there is a Constructor which calls the SetDevices and SetStyles Methods. The Init Method helps set up the look-and-feel of the Application, Style and Device will help set up the required items for the given Device that has been selected. The Save Method is used with a FileSavePicker to then call the Render Method with the selected file from PickSaveFileAsync.

Step 8

In the Solution Explorer select MainPage.xaml

vs2017-mainpage-zuneview-app

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 above the Grid element, enter the following XAML:

<Page.Resources>
	<LinearGradientBrush x:Key="ZuneKeel">
		<GradientStop Offset="0" Color="#020202"/>
		<GradientStop Offset="1" Color="#424242"/>
	</LinearGradientBrush>
	<LinearGradientBrush x:Key="ZunePad">
		<GradientStop Offset="0" Color="#66000000"/>
		<GradientStop Offset="1" Color="#22000000"/>
	</LinearGradientBrush>
	<LinearGradientBrush x:Key="ZunePadOuter">
		<GradientStop Offset="0" Color="#66FFFFFF"/>
		<GradientStop Offset="1" Color="#22FFFFFF"/>
	</LinearGradientBrush>
	<LinearGradientBrush x:Key="ZuneScreen">
		<GradientStop Offset="1" Color="#231F20"/>
		<GradientStop Offset="0" Color="#524F4F"/>
		<LinearGradientBrush.Transform>
			<RotateTransform Angle="68" CenterX="0.5" CenterY="0.5"/>
		</LinearGradientBrush.Transform>
	</LinearGradientBrush>
	<DataTemplate x:Key="KeelTemplate" x:DataType="local:ZuneViewDevice">
		<Canvas Width="21" Height="36">
			<Rectangle Height="36" Width="21" Fill="{x:Bind Path=Fill, Mode=OneWay}" 
			Canvas.Left="1" Canvas.Top="0" RadiusX="1" RadiusY="1">
			</Rectangle>
			<Rectangle Canvas.Left="2" Canvas.Top="1" Fill="{StaticResource ZuneScreen}" 
			Height="24" Stroke="#5D5D5D" Width="19" />
			<Ellipse Canvas.Left="6" Canvas.Top="24" Fill="{StaticResource ZuneKeel}" 
			Stroke="#5D5D5D" StrokeThickness="1.5" Height="11" Width="11"/>
		</Canvas>
	</DataTemplate>
	<DataTemplate x:Key="DracoTemplate" x:DataType="local:ZuneViewDevice">
		<Canvas Width="21" Height="36">
			<Rectangle Height="36" Width="20" Fill="{x:Bind Path=Fill, Mode=OneWay}" 
			Canvas.Left="1" Canvas.Top="0" RadiusX="1" RadiusY="1">
			</Rectangle>
			<Rectangle Fill="{StaticResource ZuneScreen}" Canvas.Left="2" Canvas.Top="1" 
			Height="24" Stroke="#191616" Width="18"/>
			<Rectangle Canvas.Left="6.5" Canvas.Top="25" Height="9" Width="9" RadiusX="3" RadiusY="3" 
			Fill="{StaticResource ZunePad}" Stroke="{StaticResource ZunePadOuter}"/>
		</Canvas>
	</DataTemplate>
	<DataTemplate x:Key="ScorpiusTemplate" x:DataType="local:ZuneViewDevice">
		<Canvas Width="21" Height="36">
			<Rectangle Height="35" Width="16" Fill="{x:Bind Path=Fill, Mode=OneWay}" 
			Canvas.Left="4" Canvas.Top="0"/>
			<Rectangle Canvas.Left="5" Canvas.Top="1" Fill="{StaticResource ZuneScreen}" 
			Height="18" Stroke="#191616" Width="14" />
			<Rectangle Canvas.Left="7" Canvas.Top="22" Fill="{StaticResource ZunePad}" 
			Stroke="{StaticResource ZunePadOuter}" Height="10" Width="10" RadiusX="3" RadiusY="3"/>
		</Canvas>
	</DataTemplate>
	<DataTemplate x:Key="PavoTemplate" x:DataType="local:ZuneViewDevice">
		<Canvas Width="21" Height="36">
			<Rectangle Height="32" Width="16" Fill="{x:Bind Path=Fill, Mode=OneWay}" Canvas.Left="4" Canvas.Top="2" 
			RadiusX="1.5" RadiusY="1.5">
			</Rectangle>
			<Rectangle Fill="{StaticResource ZuneScreen}" Canvas.Left="5" Canvas.Top="3.25" Height="24.5" 
			Width="14" Stroke="Black" StrokeThickness="1" StrokeMiterLimit="1"/>
			<Rectangle Fill="Black" Canvas.Left="5" Canvas.Top="27" Height="2" Width="14"/>
			<Polygon Points="0,0 8,0 6,1.5 2,1.5 0,0" Canvas.Left="8" Canvas.Top="29" 
			Height="1.5" Width="8">
				<Polygon.Fill>
					<LinearGradientBrush>
						<GradientStop Offset="0" Color="#FF162025"/>
						<GradientStop Offset="1" Color="#FF0C1417"/>
					</LinearGradientBrush>
				</Polygon.Fill>
			</Polygon>
		</Canvas>
	</DataTemplate>
	<DataTemplate x:Key="DevicesTemplate" x:DataType="local:ZuneViewDevice">
		<StackPanel Orientation="Horizontal">
			<Viewbox Margin="2" Width="{StaticResource ControlContentThemeFontSize}"
			Height="{StaticResource ControlContentThemeFontSize}">
				<ContentControl Content="{Binding}"
				ContentTemplateSelector="{StaticResource ZuneViewDeviceTemplateSelector}"/>
			</Viewbox>
			<TextBlock Text="{x:Bind Path=Name, Mode=OneWay}"/>
		</StackPanel>
	</DataTemplate>
	<DataTemplate x:Key="StylesTemplate" x:DataType="local:ZuneViewStyle">
		<StackPanel Orientation="Horizontal">
			<Grid Margin="2" Width="{StaticResource ControlContentThemeFontSize}" 
			Height="{StaticResource ControlContentThemeFontSize}">
				<Grid.Background>
					<SolidColorBrush Color="{x:Bind Path=Colour, Mode=OneWay}"/>
				</Grid.Background>
			</Grid>
			<TextBlock Text="{x:Bind Path=Name, Mode=OneWay}"/>
		</StackPanel>
	</DataTemplate>
	<DataTemplate x:Key="ContentTemplate">
		<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ZuneViewDeviceTemplateSelector}"/>
	</DataTemplate>
	<local:ZuneViewDeviceTemplateSelector x:Key="ZuneViewDeviceTemplateSelector" 
	Keel="{StaticResource KeelTemplate}" Draco="{StaticResource DracoTemplate}" 
	Scorpius="{StaticResource ScorpiusTemplate}" Pavo="{StaticResource PavoTemplate}"/>
</Page.Resources>

Then while still in the XAML View between the Grid and /Grid elements, enter the following XAML:

<Grid Margin="50">
	<Grid.RowDefinitions>
		<RowDefinition Height="Auto"/>
		<RowDefinition Height="*"/>
	</Grid.RowDefinitions>
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="50*"/>
		<ColumnDefinition Width="50*"/>
	</Grid.ColumnDefinitions>
	<ComboBox Name="Devices" Grid.Row="0" Grid.Column="0" Margin="5"
	HorizontalAlignment="Stretch" SelectionChanged="Devices_SelectionChanged" 
	ItemTemplate="{StaticResource DevicesTemplate}"/>
	<ComboBox Name="Styles" Grid.Row="0" Grid.Column="1" Margin="5"
	HorizontalAlignment="Stretch" SelectionChanged="Styles_SelectionChanged"
	ItemTemplate="{StaticResource StylesTemplate}"/>
	<Grid Margin="50" Grid.Row="1" Grid.ColumnSpan="2" HorizontalAlignment="Center">
		<Viewbox>
			<ItemsControl Name="Display" ItemTemplate="{StaticResource ContentTemplate}"/>
		</Viewbox>
	</Grid>
</Grid>
<CommandBar VerticalAlignment="Bottom">
	<AppBarButton Icon="Save" Label="Save" Click="Save_Click"/>
</CommandBar>

Within the Page.Resources block of XAML above the Grid Element contains various Resources including LinearGradientBrush to help create the look-and-feel of the Devices and a DataTemplate for each ZuneViewDevice which is made up of Canvas, Rectangle and Ellipse Elements. There is also the ZuneViewDeviceTemplateSelector to help display the correct ZuneViewDevice Template.

Within the main Grid Element, the first block of XAML is a Grid Control which has two Rows and Columns, in the first Row are two ComboBox Controls in a Column each for Devices and Styles and in the second Row is a ItemsControl within a Viewbox which will be used to show the selected Device. The second block of XAML is a CommandBar with AppBarButton for Save which calls Save_Click.

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();

protected override void OnNavigatedTo(NavigationEventArgs e)
{
	library.Init(ref Display, ref Devices, ref Styles);
}

private void Devices_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
	library.Device(ref Display, ref Devices, ref Styles);
}

private void Styles_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
	library.Style(ref Display, ref Devices, ref Styles);
}

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

There is an OnNavigatedTo Event Handler which calls the Init Method in the Library Class, there is a Devices_SelectionChanged and Styles_SelectionChanged Event Handlers to call the Device and Style Methods in the Library Class respectively and Save_Click which calls the Save Method of 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

Once the Application has started running you can tap on the first ComboBox to select a Microsoft Zune and the second ComboBox to select a Colour for it and then can Save an Image of the selected Device in the selected Style

uwp-ran-zuneview-app

Step 15

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

vs2017-close

This example allows you to view Microsoft Zune of the Devices and Colours that were available to show how to use RenderTargetBitmap to Save an Image of a UIElement. The Assets used were originally created for ZuneCardr which was a Zune Social application that could read and export the Zune Card with other images and originally also allowed a Zune to be displayed also in an older version and make a great, nostalgic and hopefully fun way of learning how to create Images from XAML.

Keel was the code name of the original Zune, Draco was the code name of the follow-up the Zune 80 which was also later available in a 120GB Version, Scorpius was the code name of the smaller Zune and Pavo the code name for the Zune HD. Zune and all associated image designs and names are the trademarks and/or property of Microsoft and are shared here for educational purposes only.

Creative Commons License

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s