title: UWP Custom control binding not working in either direction
body: <p>I'm working on a custom number picker control in UWP and I'm trying to bind a view model to its SelectedValue property. Presently, even with two way binding and the update trigger set to <code>PropertyChanged</code>, my binding does not work in either direction. For now I've worked around it using event handlers, but I'd like to break this control out into a library for custom controls for our company and have it be usable out of the box. Following is my control code and the basic code of the page I'm using the control in:</p>
  
  <p>NumberPicker.xaml:</p>
  
  <pre><code>&lt;ItemsControl
      x:Class="UWPApp.Scorekeeper.NumberPicker"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UWPApp.Scorekeeper"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:vms="using:UWPApp.Scorekeeper.Models.ViewModels"
      mc:Ignorable="d"
      x:Name="Select" 
      Loaded="Select_Loaded" 
      ItemsSource="{x:Bind ItemsCollection}"
      d:DesignHeight="300"
      d:DesignWidth="400"&gt;
      &lt;ItemsControl.ItemTemplate&gt;
          &lt;DataTemplate x:DataType="local:NumberItem"&gt;
              &lt;Viewbox HorizontalAlignment="Stretch" Height="115"&gt;
                  &lt;TextBlock Text="{x:Bind Value}"&gt;&lt;/TextBlock&gt;
              &lt;/Viewbox&gt;
          &lt;/DataTemplate&gt;
      &lt;/ItemsControl.ItemTemplate&gt;
      &lt;ItemsControl.Template&gt;
              &lt;ControlTemplate&gt;
                  &lt;Grid BorderThickness="4" BorderBrush="Black"&gt;
                      &lt;Grid.RowDefinitions&gt;
                          &lt;RowDefinition Height="1*"/&gt;
                          &lt;RowDefinition Height="2*"/&gt;
                          &lt;RowDefinition Height="1*"/&gt;
                      &lt;/Grid.RowDefinitions&gt;
                      &lt;Rectangle Opacity=".5"&gt;
                          &lt;Rectangle.Fill&gt;
                              &lt;LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1"&gt;
                                  &lt;GradientStop Offset="0" Color="Black"/&gt;
                                  &lt;GradientStop Offset="1" Color="Transparent"/&gt;
                              &lt;/LinearGradientBrush&gt;
                          &lt;/Rectangle.Fill&gt;
                      &lt;/Rectangle&gt;
                      &lt;ScrollViewer Grid.RowSpan="3" ViewChanged="Select_ViewChanged" VerticalSnapPointsType="Mandatory" VerticalSnapPointsAlignment="Center" x:Name="MinutesSelect" HorizontalScrollMode="Disabled" VerticalScrollMode="Auto" VerticalScrollBarVisibility="Visible"&gt;
                          &lt;ItemsPresenter&gt;&lt;/ItemsPresenter&gt;
                      &lt;/ScrollViewer&gt;
                      &lt;Rectangle Grid.Row="2" Opacity=".5"&gt;
                          &lt;Rectangle.Fill&gt;
                              &lt;LinearGradientBrush StartPoint=".5,1" EndPoint=".5,0"&gt;
                                  &lt;GradientStop Offset="0" Color="Black"/&gt;
                                  &lt;GradientStop Offset="1" Color="Transparent"/&gt;
                              &lt;/LinearGradientBrush&gt;
                          &lt;/Rectangle.Fill&gt;
                      &lt;/Rectangle&gt;
                  &lt;/Grid&gt;
              &lt;/ControlTemplate&gt;
          &lt;/ItemsControl.Template&gt;
      &lt;ItemsControl.ItemsPanel&gt;
          &lt;ItemsPanelTemplate&gt;
              &lt;StackPanel Orientation="Vertical"&gt;
              &lt;/StackPanel&gt;
          &lt;/ItemsPanelTemplate&gt;
      &lt;/ItemsControl.ItemsPanel&gt;
  &lt;/ItemsControl&gt;
  </code></pre>
  
  <p>NumberPicker.xaml.cs:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Linq;
  using Windows.UI.Core;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using UWPApp.Scorekeeper.Models.ViewModels;
  using UWPApp.Scorekeeper.Toolbox;
  
  
  namespace UWPApp.Scorekeeper
  {
      public class NumberItem
  {
      public NumberItem(int? value)
      {
          Value = value;
      }
      public int? Value { get; set; }
  }
  
  public sealed partial class NumberPicker : ItemsControl
  {
  
  
      public event SelectionChangedEventHandler SelectionChanged;
  
      public int RangeBottom { get; set; }
  
      public int RangeTop { get; set; }
  
      public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(int?), typeof(NumberPicker), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedValueChanged)));
  
      public static readonly DependencyProperty SelectionChangedProperty = DependencyProperty.Register("SelectionChanged", typeof(SelectionChangedEventHandler), typeof(NumberPicker), new PropertyMetadata(null));
  
      private static void OnSelectedValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          var picker = d as NumberPicker;
          picker.SelectionChanged?.Invoke(picker, new SelectionChangedEventArgs(new List&lt;object&gt; { e.OldValue }, new List&lt;object&gt; { e.NewValue }));
          return;
      }
  
      public int? SelectedValue { get { return (int?)GetValue(SelectedValueProperty); } set { SetValue(SelectedValueProperty, value); } }
  
      public ObservableCollection&lt;NumberItem&gt; ItemsCollection { get; set; }
  
      public NumberPicker()
      {
          this.InitializeComponent();
          DataContext = this;
          ItemsCollection = new ObservableCollection&lt;NumberItem&gt;();
      }
  
      private void Select_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
      {
          if (!e.IsIntermediate)
          {
              var scroll = sender as ScrollViewer;
              var position = scroll.VerticalOffset;
              var value = Math.Floor(position / 115d);
              SelectedValue = ((int)value);
          }
      }
  
      private void Select_Loaded(object sender, RoutedEventArgs e)
      {
          var count = RangeTop - RangeBottom + 1;
          var items = Enumerable.Range(RangeBottom, count).Select(m =&gt; new NumberItem(m)).ToList();
          foreach (var item in items)
          {
              ItemsCollection.Add(item);
          }
          ItemsCollection.Insert(0, new NumberItem(null));
          ItemsCollection.Add(new NumberItem(null));
          var period = TimeSpan.FromMilliseconds(10);
          Windows.System.Threading.ThreadPoolTimer.CreateTimer(async (source) =&gt;
          {
              await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =&gt;
              {
                  var scroll = Select.FindFirstChild&lt;ScrollViewer&gt;();
                  if (SelectedValue != null)
                  {
                      var position = SelectedValue * 115d + 81.5;
                      scroll.ChangeView(null, position, null, true);
                  }
              });
          }, period);
      }
  }
  }
  </code></pre>
  
  <p>Page.xaml:</p>
  
  <pre><code>&lt;Page
      x:Class="UWPApp.Scorekeeper.SelectPenaltyTime"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UWPApp.Scorekeeper"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:vms="using:UWPApp.Scorekeeper.Models.ViewModels"
      mc:Ignorable="d"
      x:Name="PageElement"
      Background="{ThemeResource SystemControlBackgroundAccentBrush}"
      d:DesignHeight="600"
      d:DesignWidth="1024"&gt;
  
      &lt;ContentPresenter x:Name="MainContent" Margin="0,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"&gt;
          &lt;Grid Background="{ThemeResource SystemControlBackgroundAccentBrush}"&gt;
              &lt;Grid.RowDefinitions&gt;
                  &lt;RowDefinition Height="110*"/&gt;
                  &lt;RowDefinition Height="43*"/&gt;
                  &lt;RowDefinition Height="47*"/&gt;
              &lt;/Grid.RowDefinitions&gt;
              &lt;local:NumberPicker Margin="100,0,750,0" RangeBottom="0" RangeTop="20" SelectedValue="{Binding ElementName=PageElement,Path=ViewModel.Minutes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"&gt;&lt;/local:NumberPicker&gt;
          &lt;/Grid&gt;
      &lt;/ContentPresenter&gt;
  &lt;/Page&gt;
  </code></pre>
  
  <p>Page.xaml.cs:</p>
  
  <pre><code>using System;
  using System.Collections.ObjectModel;
  using System.ComponentModel;
  using System.Linq;
  using System.Runtime.CompilerServices;
  using System.Windows.Input;
  using Windows.UI.Popups;
  using Windows.UI.Xaml;
  using Windows.UI.Xaml.Controls;
  using Windows.UI.Xaml.Navigation;
  using UWPApp.Scorekeeper.Models.TransportClasses;
  using UWPApp.Scorekeeper.Models.ViewModels;
  
  namespace UWPApp.Scorekeeper
  {
      public sealed partial class SelectPenaltyTime : Page
      {
          public GameStateModel StateModel { get; set; }
  
          public AddPenalty_FVM ViewModel { get; set; }
  
          public SelectPenaltyTime()
          {
              this.InitializeComponent();
          }
  
          protected override void OnNavigatedTo(NavigationEventArgs e)
          {
              var message = e.Parameter as PenaltyMessage;
              StateModel = message.StateModel;
              ViewModel = message.ViewModel;
          }
  
          private void NumberPicker_Loaded(object sender, RoutedEventArgs e)
          {
              var picker = sender as NumberPicker;
              picker.SelectedValue = ViewModel.Minutes;
          }
  
          private void NumberPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
          {
              var picker = sender as NumberPicker;
              ViewModel.Minutes = picker.SelectedValue;
          }
      }
  }
  </code></pre>
  
  <p>AddPenalty_FVM:</p>
  
  <pre><code>public class AddPenalty_FVM
      {
          public int? Minutes { get; set; }
      }
  </code></pre>