I've got a class that has an ObservableCollection of itself embedded within the class.
I'm trying to create a user control that also has a reference to itself in order to display the contents of the observable collection. However, I'm getting a runtime error whenever I'm trying to run the app.
The error is not overly meaningful:
XAML parsing failed.
E_RUNTIME_SETVALUE [Line: 91 Position: 58] (which is the line that has the recursive call to the user control)
The class looks something like this (it's been made shorter for illustration purposes)
public class BookChapterVm : IBookChapterVm
{
public int Id {get;set;}
public string ChapterText {get;set;}
public ObservableCollection<IBookChapterVm> Chapters { get; set; } = new ObservableCollection<IBookChapterVm>();
}
The user control looks something like this (again, unnecessary parts are removed)
<UserControl
x:Class="Cgs.Ux.UserControls.HelpTextEditor.BookChapterEditorCtrl">
<ListView
ItemsSource="{x:Bind Vm.Chapters, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="help:BookChapterVm">
<StackPanel Orientation="Horizontal">
<local:BookChapterEditorCtrl Vm="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
I've also tried to set up a recursive data template, but it basically ended up with the same error.
Here is a working exemple :
In your page :
<local:RecursiveContainer ViewModel="{Binding}" />
The code behind :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = BuildBookChapterVM();
}
private BookChapterVM BuildBookChapterVM()
{
BookChapterVM vm1 = new BookChapterVM { ChapterText = "1" };
BookChapterVM vm21 = new BookChapterVM { ChapterText = "21" };
BookChapterVM vm22 = new BookChapterVM { ChapterText = "22" };
BookChapterVM vm211 = new BookChapterVM { ChapterText = "211" };
vm1.Chapters.Add(vm21);
vm1.Chapters.Add(vm22);
vm21.Chapters.Add(vm211);
return vm1;
}
}
public class BookChapterVM
{
public int Id { get; set; }
public string ChapterText { get; set; }
public ObservableCollection<BookChapterVM> Chapters { get; set; } = new ObservableCollection<BookChapterVM>();
}
The UserControl XAML :
<UserControl
x:Class="WpfApp2.RecursiveContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="{Binding ChapterText}" />
<ItemsControl HorizontalContentAlignment="Stretch" ItemsSource="{Binding Chapters, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:RecursiveContainer
Margin="10,5,0,5"
HorizontalAlignment="Stretch"
ViewModel="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>
The UC code behind :
public partial class RecursiveContainer : UserControl
{
public RecursiveContainer()
{
InitializeComponent();
}
public BookChapterVM ViewModel
{
get { return (BookChapterVM)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(RecursiveContainer), typeof(RecursiveContainer));
}
See image as proof of concept.
I hope it will help you ;)
I am trying to create a UWP feet/inches control that takes a value in inches, and splits it into two fields, feet and inches. The control, when updated by the user, should update the backend viewmodel with the new inches value.
Requirements
Change the value in 1, then the values in 2 and 3 update
accordingly.
Change the value in 3, the values in 1 and 2 update accordingly
The way I have this written it gets into an endless loop. I am not even sure the FeetInches control is written properly. How would I change this to meet the above requirements?
Downloadable/runnable code is available in GitHub, but here it is inline per SO guidelines...
App.xaml
<Application x:Class="UWPFeetInches.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPFeetInches">
<Application.Resources>
<ResourceDictionary>
<local:NullDecimalConverter x:Key="NullDecimalConverter" />
</ResourceDictionary>
</Application.Resources>
</Application>
MainPage.xaml
<Page
x:Class="UWPFeetInches.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:UWPFeetInches"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Enter Inches Value:"
Margin="0,50,10,0"
VerticalAlignment="Center" />
<TextBox Grid.Row="0" Grid.Column="1"
Margin="0,50,10,0"
Text="{x:Bind ViewModel.Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" />
<TextBlock Grid.Row="1" Grid.Column="0"
Margin="0,50,10,0"
Text="Inches Display:"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Margin="0,50,10,0"
Text="{x:Bind ViewModel.InchesDisplay, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0"
Text="Feet / Inches Control:"
Margin="0,50,10,0"
VerticalAlignment="Center" />
<local:FeetInches Grid.Row="2" Grid.Column="1"
Margin="0,50,10,0"
Value="{x:Bind ViewModel.Inches, Mode=TwoWay}" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace UWPFeetInches
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.ViewModel = new InchesViewModel();
}
public InchesViewModel ViewModel { get; set; }
}
public class InchesViewModel : INotifyPropertyChanged
{
private decimal? _Inches;
public decimal? Inches
{
get { return _Inches; }
set
{
if (value != _Inches)
{
_Inches = value;
InchesDisplay = _Inches == null ? "{null}" : _Inches.ToString();
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Inches)));
}
}
}
private string _InchesDisplay;
public string InchesDisplay
{
get { return _InchesDisplay; }
set
{
if (value != _InchesDisplay)
{
_InchesDisplay = value;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(InchesDisplay)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
NullDecimalConverter.cs
using System;
using Windows.UI.Xaml.Data;
namespace UWPFeetInches
{
public sealed class NullDecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is decimal m)
{
return m == 0 ? "" : m.ToString();
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (!string.IsNullOrWhiteSpace(value?.ToString()))
{
if (Decimal.TryParse(value.ToString(), out decimal m))
{
return m;
}
}
return null;
}
}
}
FeetInches.xaml
<UserControl
x:Class="UWPFeetInches.FeetInches"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:UWPFeetInches"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<StackPanel Orientation="Horizontal">
<TextBox Header="Feet"
Margin="0,0,10,0"
Text="{x:Bind Feet, Mode=TwoWay}" />
<TextBox Header="Inches"
Text="{x:Bind Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" />
</StackPanel>
</UserControl>
FeetInches.xaml.cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace UWPFeetInches
{
public sealed partial class FeetInches : UserControl
{
public FeetInches()
{
this.InitializeComponent();
}
#region Feet
public int Feet
{
get { return (int)GetValue(FeetProperty); }
set
{
SetValue(FeetProperty, value);
}
}
public static readonly DependencyProperty FeetProperty = DependencyProperty.Register(nameof(Feet), typeof(int), typeof(FeetInches), new PropertyMetadata(0, OnPropertyChanged));
#endregion
#region Inches
public decimal Inches
{
get { return (decimal)GetValue(InchesProperty); }
set
{
SetValue(InchesProperty, value);
}
}
public static readonly DependencyProperty InchesProperty = DependencyProperty.Register(nameof(Inches), typeof(decimal), typeof(FeetInches), new PropertyMetadata(0M, OnPropertyChanged));
#endregion
#region Value
public decimal? Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(decimal?), typeof(FeetInches), new PropertyMetadata(null, ValueOnPropertyChanged));
#endregion
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as FeetInches;
control.Value = control.Feet * 12 + control.Inches;
}
private static void ValueOnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as FeetInches;
var inches = control.Value;
control.Feet = inches.HasValue ? (int)(inches.Value / 12M) : 0;
control.Inches = inches.HasValue ? inches.Value - (control.Feet * 12M) : 0M;
}
}
}
When you subscribe the PropertyChanged event for the three dependency properties, it will get into an endless loop. You could try to subscribe the LostFocus event of TextBox in FeetInches.xaml to replace your OnPropertyChanged event for your Feet and Inches properties, in that case, it won't get into an endless loop. For example:
.xaml:
<TextBox Header="Feet"
Margin="0,0,10,0"
x:Name="MyFeet"
Text="{x:Bind Feet, Mode=TwoWay}" LostFocus="TextBox_LostFocus"/>
<TextBox Header="Inches"
x:Name="MyInches"
Text="{x:Bind Inches, Mode=TwoWay, Converter={StaticResource NullDecimalConverter}}" LostFocus="TextBox_LostFocus"/>
.cs:
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
Decimal.TryParse(textbox.Text, out decimal m);
if (textbox.Name.Equals("MyFeet"))
{
this.Value = m * 12 + this.Inches;
}
else
{
this.Value = this.Feet * 12 + m;
}
}
When triggered the LostFocus event, the property bound with the Text of TextBox has not changed, so you need to use the value of Text directly instead of the Feet/Inches property and you need to judge which TextBox triggers this event.
Or if you still want to use OnPropertyChanged event for Feet and Inches properties instead of LostFocus event, you can declare a property(e.g. bool valueChanged) to limit the calling of ValueOnPropertyChanged and OnPropertyChanged event.
.cs:
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (valueChanged == false) {
var control = d as FeetInches;
control.Value = control.Feet * 12 + control.Inches;
}
}
private static bool valueChanged = false;
private static void ValueOnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
valueChanged = true;
var control = d as FeetInches;
var inches = control.Value;
control.Feet = inches.HasValue ? (int)(inches.Value / 12M) : 0;
control.Inches = inches.HasValue ? inches.Value - (control.Feet * 12M) : 0M;
valueChanged = false;
}
Update:
You can use the Property property from DependencyPropertyChangedEventArgs data to check which dependency property changes.
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.Property == FeetProperty) {
//do something
}
else{
//do something
}
}
I have an entry control in my XAML where I set the initial value on page appear through data binding. Initially the value is appearing but when I am updating it from another view model it is not getting updated on UI.
Below is the XAML code and XAML.CS
<ListView
x:Name="workList"
Grid.Row="2"
SeparatorColor="{DynamicResource AccentColor}"
ItemsSource="{ Binding WorkItems }"
Margin="5"
CachingStrategy="RecycleElement"
RowHeight="440"
SeparatorVisibility="Default"
SelectionMode="None"
HasUnevenRows="False">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:LoadItemPutawayTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
mc:Ignorable="d"
x:Class="Sanipex.LoadItemPutawayTemplate">
<Grid
RowSpacing="0"
Padding="0"
Margin="0,10,0,0"
>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Entry
x:Name="OverrideLoc"
Grid.Row="0"
TextColor="Black"
WidthRequest="110"
Text="{Binding toLocation}"
grial:EntryProperties.BorderCornerRadius="10"
grial:EntryProperties.BorderStyle="RoundRect"
grial:EntryProperties.BorderColor="Black"
HorizontalOptions="StartAndExpand"
VerticalOptions="Center"
Focused="OverrideLoc_Focused"
TextChanged="OverrideLoc_TextChanged"
grial:EntryProperties.HorizontalPadding="5"
FontAttributes="Bold"
PlaceholderColor="Black"
FontSize="20"/>
</Grid>
public partial class ItemPutAway : ContentPage
{
private static ItemPutAwayViewModel obj;
public ItemPutAway()
{
InitializeComponent();
obj = new ItemPutAwayViewModel();
BindingContext = obj;
}
public static ItemPutAwayViewModel itemPutAwayViewModel
{
get
{
return obj;
}
}
protected override async void OnAppearing()
{
obj.LoadData();
}
}
Below is my first view model code
public class ItemPutAwayViewModel : INotifyPropertyChanged
{
private IList<WorkItem> workItems;
public event PropertyChangedEventHandler PropertyChanged;
public string ltoLocation;
public string toLocation
{
get => ltoLocation;
set
{
ltoLocation = value;
OnPropertyChanged(nameof(toLocation));
}
}
public IList<WorkItem> WorkItems
{
get => workItems;
set
{
workItems = value;
OnPropertyChanged(nameof(WorkItems));
}
}
public void LoadData()
{
WorkItems = App.dataManager.GetItemPutAwayWorks();
}
public void setLocation(string _location)
{
toLocation = _location;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Below is the code through which I am trying to update the toLocation binding value to different value from another XAML page as below:
public partial class AvailableLocationsPopUp : PopupPage
{
private static AvailableLocationViewModel obj;
public AvailableLocationsPopUp(WorkItem _workItem)
{
InitializeComponent();
obj = new AvailableLocationViewModel(gWorkItem.itemid);
BindingContext = obj;
}
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
private void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
Location content = e.Item as Location;
ItemPutAway.itemPutAwayViewModel.setLocation("ABC-XYZ");
PopupNavigation.Instance.PopAsync();
}
}
As I mentioned in the discussion, you have to also implement the INotifyPropertyChanged interface of the class WorkItem.
Implement INotifyPropertyChanged in ItemPutAwayViewModel will only help for changes in the WorkItems(like add or remove one WorkItem), not the changes inside the WorkItem.
So, the code should be:
public class WorkItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _toLocation;
public string toLocation
{
get => _toLocation;
set
{
_toLocation = value;
NotifyPropertyChanged();
}
}
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have this simple ListView filled from an ObservableCollection. Once the list is bound, I would like to access the parent vm view model from inside this ItemTemplate so that I can bind the command called cmd_delete_mesh. How is this done for a UWP Xaml app (not wpf)?
<ListView x:Name="mesh_list" SelectedItem="{x:Bind vm.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind vm.meshes}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem>
<Button Command="{Binding cmd_delete_mesh}"/>
You can do like so:
<ListView x:Name="mesh_list" SelectedItem="{x:Bind vm.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind vm.meshes}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem>
<Button Command="{Binding ElementName=mesh_list, Path=DataContext.vm.cmd_delete_mesh}"/>
I do this from code unfortunately... I’ll post an example of my code soon
You could define your command in your model and declare an event in it. In your ViewModel, when you initialize the 'meshes' collection, you could register this event for every item in this collection. Then, when the command is executed, you just need to raise the event and do some operations in its event handler.
I made a simple code sample for your reference:
<ListView x:Name="mesh_list" SelectedItem="{x:Bind ViewModel.selected_mesh, Mode=TwoWay}" ItemsSource="{x:Bind ViewModel.meshes,Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SubTest">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Name }"></TextBlock>
<Button Command="{x:Bind cmd_delete_mesh}" Content="delete"/>
</StackPanel>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
ViewModel = new Test("test data");
}
private Test ViewModel { get; set; }
}
public class Test : ViewModelBase
{
public string Name { get; set; }
private SubTest _selected_mesh;
public SubTest selected_mesh
{
get { return _selected_mesh; }
set
{
if (_selected_mesh != value)
{
_selected_mesh = value;
RaisePropertyChanged("selected_mesh");
}
}
}
public ObservableCollection<SubTest> meshes { get; set; } = new ObservableCollection<SubTest>();
public Test(string name)
{
this.Name = name;
for (int i = 0; i < 10; i++)
{
var sub = new SubTest() { Name = "String " + i };
sub.DeleteParentItem += Test_DeleteParentItem;
meshes.Add(sub);
}
}
private void Test_DeleteParentItem()
{
if (selected_mesh != null)
{
DeleteItem(selected_mesh);
}
}
private void DeleteItem(SubTest subTest)
{
//TODO...
}
}
public class SubTest
{
public RelayCommand cmd_delete_mesh { get; set; }
public string Name { get; set; }
public event Action DeleteParentItem;
public SubTest()
{
cmd_delete_mesh = new RelayCommand(DeleteItem);
}
private void DeleteItem()
{
if (DeleteParentItem != null)
{
DeleteParentItem.Invoke();
}
}
}
Please note that the ViewModelBase and RelayCommand are from mvvmlight.
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
I am making a UWP and cannot correctly grasp DataBinding and INotifyPropertyChanged
I am trying to bind some TextBox in a ContentDialog to properties in my code-behind cs file.
Here's my view model:
class UserViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string _fname { get; set; }
public string _lname { get; set; }
public string Fname
{
get { return _fname; }
set
{
_fname = value;
this.OnPropertyChanged();
}
}
public string Lname
{
get { return _lname; }
set
{
_lname = value;
this.OnPropertyChanged();
}
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Code behind:
public sealed partial class MainPage : Page
{
UserViewModel User { get; set; }
public MainPage()
{
this.InitializeComponent();
User = new UserViewModel();
}
....
....
private void SomeButton_Click(object sender, TappedRoutedEventArgs e)
{
//GetUserDetails is a static method that returns UserViewModel
User = UserStore.GetUserDetails();
//show the content dialog
ContentDialogResult result = await UpdateUserDialog.ShowAsync();
}
}
Here's the XAML for the ContentDialog:
<ContentDialog Name="UpdateUserDialog">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Name="tbFirstNameUpdate"
Text="{x:Bind Path=User.Fname, Mode=OneWay}"
Style="{StaticResource SignUpTextBox}"/>
<TextBox Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Name="tbLastNameUpdate"
Text="{x:Bind Path=User.Lname, Mode=OneWay}"
Style="{StaticResource SignUpTextBox}"/>
</ContentDialog>
NOTE: Binding works well when I initialize the view model in the MainPage constructor itself like this:
User = new UserViewModel { Fname = "name", Lname = "name" };
You don't fire a PropertyChanged event when you replace the value of the User property with a new view model instance.
You may however simply replace
User = UserStore.GetUserDetails();
by
var user = UserStore.GetUserDetails();
User.Fname = user.Fname;
User.Lname = user.Lname;
and hence update the existing instance of your view model.
You should set the DataContext property to the view model instance:
public MainPage()
{
this.InitializeComponent();
User = new UserViewModel();
DataContext = User;
}
See: https://learn.microsoft.com/en-us/windows/uwp/data-binding/data-binding-in-depth