Introduction to UWP (Universal Windows Platform) — Quick Start

1. Xaml

1.1. XAML Introduction

  • XAML Overview
  • Primarily used to create visual UI elements.
  • XAML syntax is based on XML.
  • Declaring a namespace alias:
xmlns:controls="using:Common.Controls"
  • Using classes from a namespace:
<controls:MyControl />
  • Reusable resources (Resource),

    x:Key

<Style x:Key="TextBlock_Style" />
  • Control element Name,

x:Name

XAML:

<MyControl x:Name="myControl" />

C#:

private MyControl myControl;
  • Localization

    x:Uid

<TextBlock x:Uid="sampleText" />

1.2. Basic Controls – TextBlock, Button

<Page x:Class="MyPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <TextBlock Text="UWP Introduction" />
        <Button Content="UWP Introduction" Click="Button_Click" />
    </StackPanel>
</Page>

2. MVVM

MVVM Detailed Introduction

View <=> ViewModel <=> Model

  • View should primarily contain UI presentation elements, mostly implemented in XAML;
  • ViewModel should minimize business logic, delegating actions to Model functions;
  • Model should encapsulate all business data and logic, with minimal dependency on View and ViewModel;

3. Interaction Between ViewModel and Model

3.1. ViewModel Manipulating Model Data

ViewModel:

public class ViewModel
{
    private Model model;
    private void ChangeA()
    {
        this.model.A = "A";
    }
}

Model:

public class Model
{
    public string A { get; set; }
    public string B { get; set; }
}

3.2. Model Notifying ViewModel – event

ViewModel:

public class ViewModel
{
    private Model model;
    private void ChangeA()
    {
        model.BEventArgs += this.Handler;
    }
    private void Handler(object sender, EventArgs e)
    {
        AnyActions();
    }
}

Model:

public delegate void BChangedHandler(object sender, EventArgs e);

public class Model
{
    public string A { get; set; }
    private string _B;
    public string B
    {
        get { return this._B; }
        set
        {
            this._B = value;
            BEventArgs?.Invoke(this, new EventArgs());
        }
    }
    public event BChangedHandler BEventArgs;
}

4. Interaction Between View and ViewModel

4.1. Data Binding

4.1.1. Binding to ViewModel

View:

<TextBlock Text="{Binding SampleText}" />

ViewModel:

public string SampleText { get; set; }

4.1.2. Binding to Other Controls

View:

<TextBlock x:Name="TextBlock1" Text="SampleText" />
<Button Content="{Binding ElementName=TextBlock1, Path=Text}" />

4.1.3. Specifying DataContext

public ViewModelClass ViewModel { get; set; }

...
SpecifiedControl.DataContext = ViewModel;
...

5. (View and ViewModel) Implementing Notifications – INotifyPropertyChanged

When SampleText changes, it notifies the bound DependencyProperty.

public class MyControlViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void Notify(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    private string _sampleText;
    public string SampleText
    {
        get => _sampleText;
        set
        {
            _sampleText = value;
            Notify(nameof(SampleText));
        }
    }
}

Or inherit from ViewModelBase:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void Notify(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

6. (View and ViewModel) ListView Binding to ObservableCollection

ListView

Implement binding to an observable ItemSource in ListView.

View:

<ListView ItemsSource="{Binding Items}">

ViewModel:

public ObservableCollection<Recording> Items { get; set; }
  • ObservableCollection only notifies when items are added/removed or the entire list is refreshed.
  • To notify the UI when item content changes, Recording must implement INotifyPropertyChanged.

7. (View) ListView Item Templates (DataTemplate | UserControl)

7.1. DataTemplate

View:

<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate DataType="local:Recording">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding A}" />
                <TextBlock Text="{Binding B}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

ViewModel:

public ObservableCollection<RecordingViewModel> Items { get; set; }

public class RecordingViewModel : INotifyPropertyChanged
{
    ...
    // Implement INotifyPropertyChanged
    ...

    private Recording _recording;

    public string A
    {
        get => _recording.A;
        set
        {
            _recording.A = value;
            Notify(nameof(A));
        }
    }

    public string B => _recording.B;

    public RecordingViewModel(Recording recording)
    {
        _recording = recording;
    }
}

Model:

public class Recording
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    ... ...
}

Comparison:

Separating ViewModel/Model

7.2. UserControl

7.2.1. DependencyProperty

View:

<control:MyControl Text="App is on searching" IsSearching="{Binding ViewModel.IsSearching}" />

ViewModel:

public class MyControl
{
...
public static readonly DependencyProperty IsSearchingProperty =
    DependencyProperty.Register
    (
        "IsSearching", typeof(bool),
        typeof(MyControl), null
    );

public bool IsSearching
{
    get => (bool)GetValue(IsSearchingProperty);
    set => SetValue(IsSearchingProperty, value);
}
...
}

8. (ViewModel and Model) Data Conversion – IValueConverter

Use IValueConverter for data conversion between ViewModel and Model.

public class ShowAllButtonVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is IList list)
        {
            if (list.Count > 3)
            {
                return Visibility.Visible;
            }
        }
        return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        if (value is IList list)
        {
            if (list.Count > 3)
            {
                return Visibility.Visible;
            }
        }
        return Visibility.Collapsed;
    }
}

9. (View) Modifying Control Styles

9.1. Customizing Styles Within Controls

View:

<TextBlock Foreground="Red" Text="SampleText" />

9.2. Using Unified Styles – ResourceDictionary

View:

<Window.Resources>
    <ResourceDictionary>
        <Style TargetType="TextBlock" x:Key="ImportantText">
            <Setter Property="Foreground" Value="Red" />
        </Style>
    </ResourceDictionary>
</Window.Resources>
...
<TextBlock Text="SampleText" Style="{StaticResource ImportantText}" />

9.3. Using ThemeResource

View:

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <ResourceDictionary x:Key="Light">
                <Style TargetType="TextBlock" x:Key="ImportantText">
                    <Setter Property="Foreground" Value="Red" />
                </Style>
            </ResourceDictionary>

            <ResourceDictionary x:Key="Dark">
                <Style TargetType="TextBlock" x:Key="ImportantText">
                    <Setter Property="Foreground" Value="Yellow" />
                </Style>
            </ResourceDictionary>

            <ResourceDictionary x:Key="HighContrast">
                <Style TargetType="TextBlock" x:Key="ImportantText">
                    <Setter Property="Foreground" Value="Black" />
                </Style>
            </ResourceDictionary>
        </ResourceDictionary.ThemeDictionaries>
    </ResourceDictionary>
</Window.Resources>
...
<TextBlock Text="SampleText" Style="{ThemeResource ImportantText}" />

10. Using Panels

10.1. Grid

Characteristics:

  • Default Height/Width matches parent element.

Recommendations:

  • Define child element layout sizes within Grid.
  • Use proportional sizing.

10.2. StackPanel

Characteristics:

  • Can exceed parent element boundaries.
  • Height or Width adjusts based on panel content.

Recommendations:

  • Use Padding and proportions flexibly.
  • Assign Width or Height values to StackPanel in parent elements to prevent control overflow.

11. (View) Adaptive UI

  • Use VisualStateManager.VisualStateGroup
<VisualStateGroup>
    <VisualState x:Name="WideLayout">
        <VisualState.StateTriggers>
            <AdaptiveTrigger x:Name="WideLayoutTrigger" MinWindowWidth="1280" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
            <Setter Target="SystemUpdateSideGrid.Width" Value="800" />
            <Setter Target="SystemUpdateSideGrid.Grid.Row" Value="0" />
        </VisualState.Setters>
    </VisualState>

    <VisualState x:Name="MidLayout">
        <VisualState.StateTriggers>
            <AdaptiveTrigger x:Name="MidLayoutTrigger" MinWindowWidth="700" />
        </VisualState.StateTriggers>
        <VisualState.Setters>
            <Setter Target="SystemUpdateSideGrid.Width" Value="400" />
            <Setter Target="SystemUpdateSideGrid.Grid.Row" Value="1" />
        </VisualState.Setters>
    </VisualState>
    ...
</VisualStateGroup>

12. Layout Principles

  • Avoid explicit element sizing.
  • Do not use screen coordinates for element positioning.
  • Child elements share available space within containers.
  • Use nested layout containers.

13. Localization

  • Use x:Uid for unique element identifiers:
<TextBlock x:Uid="S_TextBlock1" />
  • Use Resources File:
<data name="S_TextBlock1.Text" xml:space="preserve">
    <value>Sample text</value>
</data>

14. Naming Conventions

Big camel-case: firstName

Little camel-case: FirstName

  • Class: big camel-case
  • Property: big camel-case
  • Field: little camel-case with prefix “_”
  • Control in XAML: little camel-case

15. Notes

  • Exercise caution when renaming ViewModel Properties, as XAML Binding names won’t update automatically during refactoring.