First Impressions of AKS, Azure’s New Managed Kubernetes Container Service

Programmatic Ponderings

Kubernetes as a Service

On October 24, 2017, less than a month prior to writing this post, Microsoft released the public preview of Managed Kubernetes for Azure Container Service (AKS). According to Microsoft, the goal of AKS is to simplify the deployment, management, and operations of Kubernetes. According to Gabe MonroyPM Lead, Containers @ Microsoft Azure, in a blog post, AKS ‘features an Azure-hosted control plane, automated upgrades, self-healing, easy scaling.’ Monroy goes on to say, ‘with AKS, customers get the benefit of open source Kubernetes without complexity and operational overhead.

Unquestionably, Kubernetes has become the leading Container-as-a-Service (CaaS) choice, at least for now. Along with the release of AKS by Microsoft, there have been other recent announcements, which reinforce Kubernetes dominance. In late September, Rancher Labs announced the release of Rancher 2.0. According to Rancher, Rancher 2.0 would be based…

View original post 2,337 more words

PullToRefreshListView for UWP

Hello there developer who was just asked to incorporate the PullToRefresh behavior into one of your ListViews for the UWP app you are building. And of course you are looking to steal code on the internet. No blaming, I do it all the time. I actually want you to use this.

photo-15-642x322In case you just crawled out of your cave and aren’t really sure what PullToRefresh is here you go.

 

 

After evaluating the few solutions that were available on the internet I realized none of them fulfilled my requirements perfectly so I had to make my own. To be exact I had to steal code and was hoping that after some tweaking and refactoring I might be able to make it work.

After shamelessly stealing great code from  @dotMorten ‘s blog here RefreshBox for Windows Phone 7 as well as some interesting ideas from an otherwise mediocre Microsoft sample here XamlPullToRefresh I ended up with the code below.

Enjoy!

P.S Scroll to the bottom for some interesting details about this solution.


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Input;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
namespace PullToRefreshListView
{
/// <summary>
/// Refresh box. Pull down beyond the top limit on the listview to
/// trigger a refresh, similar to iPhone's lists.
/// </summary>
public class PullToRefreshListView : ListView
{
ScrollViewer _elementScrollViewer;
PullToRefreshOuterPanel _pullToRefreshOuterPanel;
PullToRefreshInnerPanel _pullToRefreshInnerPanel;
public PullToRefreshListView()
{
DefaultStyleKey = typeof(PullToRefreshListView);
}
ScrollViewer ElementScrollViewer
{
get { return _elementScrollViewer; }
set
{
if (_elementScrollViewer != null)
{
_elementScrollViewer.ViewChanging -= OnElementScrollViewerViewChanging;
_elementScrollViewer.DirectManipulationStarted -= OnElementScrollViewerManipulationStarted;
}
_elementScrollViewer = value;
if (_elementScrollViewer != null)
{
_elementScrollViewer.ViewChanging += OnElementScrollViewerViewChanging;
_elementScrollViewer.DirectManipulationStarted += OnElementScrollViewerManipulationStarted;
}
}
}
PullToRefreshInnerPanel PullToRefreshInnerPanel
{
get { return _pullToRefreshInnerPanel; }
set
{
if (_pullToRefreshInnerPanel != null)
_pullToRefreshInnerPanel.SizeChanged -= OnPullToRefreshInnerPanelSizeChanged;
_pullToRefreshInnerPanel = value;
if (_pullToRefreshInnerPanel != null)
_pullToRefreshInnerPanel.SizeChanged += OnPullToRefreshInnerPanelSizeChanged;
}
}
PullToRefreshOuterPanel PullToRefreshOuterPanel
{
get { return _pullToRefreshOuterPanel; }
set
{
if (_pullToRefreshOuterPanel != null)
_pullToRefreshOuterPanel.SizeChanged -= OnPullToRefreshOuterPanelSizeChanged;
_pullToRefreshOuterPanel = value;
if (_pullToRefreshOuterPanel != null)
_pullToRefreshOuterPanel.SizeChanged += OnPullToRefreshOuterPanelSizeChanged;
}
}
FrameworkElement PullToRefreshIndicator { get; set; }
/// <summary>
/// Is the scrollviewer currently being pulled?
/// </summary>
public bool IsPulling { get; private set; }
void OnPullToRefreshInnerPanelSizeChanged(object sender, SizeChangedEventArgs e)
{
PullToRefreshIndicator.Width = PullToRefreshInnerPanel.ActualWidth;
// Hide the refresh indicator
ElementScrollViewer.ChangeView(null, PullToRefreshIndicator.Height, null, true);
}
void OnPullToRefreshOuterPanelSizeChanged(object sender, SizeChangedEventArgs e)
{
PullToRefreshInnerPanel.InvalidateMeasure();
}
/// <summary>
/// Called when the scroll position of the scrollviewer has changed.
/// Used to figure out if the user has over-panned the scrollviewer and if so
/// schedule a refresh when the user releases their finger.
/// </summary>
void OnElementScrollViewerViewChanging(object sender, ScrollViewerViewChangingEventArgs e)
{
if (e.NextView.VerticalOffset == 0)
{
IsPulling = true;
ChangeVisualState(true);
}
else
{
IsPulling = false;
ChangeVisualState(true);
}
// check whether the user released their finger when VerticalOffset=0
// and the direction is up.
if (e.IsInertial &&
e.FinalView.VerticalOffset == PullToRefreshIndicator.Height &&
ElementScrollViewer.VerticalOffset == 0)
{
// wait for the manipulation to end before firing the Refresh event.
ElementScrollViewer.DirectManipulationCompleted += OnElementScrollViewerManipulationCompleted;
}
}
/// <summary>
/// Called when a user manipulation has started. Makes the pull indicator visible.
/// </summary>
void OnElementScrollViewerManipulationStarted(object sender, object e)
{
ElementScrollViewer.DirectManipulationStarted -= OnElementScrollViewerManipulationStarted;
// Hide the indicator until the user starts touching the scrollviewer for the first time.
PullToRefreshIndicator.Opacity = 1;
}
/// <summary>
/// Called when the user releases their finger from the screen after having overpanned the scrollviewer
/// </summary>
void OnElementScrollViewerManipulationCompleted(object sender, object args)
{
ElementScrollViewer.DirectManipulationCompleted -= OnElementScrollViewerManipulationCompleted;
OnRefreshed();
}
private void OnRefreshed()
{
ElementScrollViewer.ChangeView(null, 0, null, true);
IsPulling = false;
ChangeVisualState(true);
PullRefresh?.Invoke(this, EventArgs.Empty);
if (PullRefreshCommand?.CanExecute(null) == true)
PullRefreshCommand.Execute(null);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
ElementScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
PullToRefreshIndicator = GetTemplateChild("PullToRefreshIndicator") as FrameworkElement;
PullToRefreshInnerPanel = GetTemplateChild("InnerPanel") as PullToRefreshInnerPanel;
PullToRefreshOuterPanel = GetTemplateChild("OuterPanel") as PullToRefreshOuterPanel;
ChangeVisualState(false);
}
private void ChangeVisualState(bool useTransitions)
{
if (IsPulling)
{
GoToState(useTransitions, "Pulling");
}
else
{
GoToState(useTransitions, "NotPulling");
}
}
private bool GoToState(bool useTransitions, string stateName)
{
return VisualStateManager.GoToState(this, stateName, useTransitions);
}
/// <summary>
/// Gets or sets the refresh text. Ie "Pull down to refresh".
/// </summary>
public string RefreshText
{
get { return (string)GetValue(RefreshTextProperty); }
set { SetValue(RefreshTextProperty, value); }
}
/// <summary>
/// Identifies the <see cref="RefreshText"/> property
/// </summary>
public static readonly DependencyProperty RefreshTextProperty =
DependencyProperty.Register("RefreshText", typeof(string), typeof(PullToRefreshListView), new PropertyMetadata("Pull down to refresh…"));
/// <summary>
/// Gets or sets the release text. Ie "Release to refresh".
/// </summary>
public string ReleaseText
{
get { return (string)GetValue(ReleaseTextProperty); }
set { SetValue(ReleaseTextProperty, value); }
}
/// <summary>
/// Identifies the <see cref="ReleaseText"/> property
/// </summary>
public static readonly DependencyProperty ReleaseTextProperty =
DependencyProperty.Register("ReleaseText", typeof(string), typeof(PullToRefreshListView), new PropertyMetadata("Release to refresh…"));
/// <summary>
/// Sub text below Release/Refresh text. For example: Updated last: 12:34pm
/// </summary>
public string PullSubtext
{
get { return (string)GetValue(PullSubtextProperty); }
set { SetValue(PullSubtextProperty, value); }
}
/// <summary>
/// Identifies the <see cref="PullSubtext"/> property
/// </summary>
public static readonly DependencyProperty PullSubtextProperty =
DependencyProperty.Register("PullSubtext", typeof(string), typeof(PullToRefreshListView), null);
/// <summary>
/// Identifies the <see cref="PullIndicatorForeground"/> property
/// </summary>
public Brush PullIndicatorForeground
{
get { return (Brush)GetValue(PullIndicatorForegroundProperty); }
set { SetValue(PullIndicatorForegroundProperty, value); }
}
/// <summary>
/// Identifies the <see cref="PullIndicatorForeground"/> property
/// </summary>
public static readonly DependencyProperty PullIndicatorForegroundProperty =
DependencyProperty.Register("PullIndicatorForeground", typeof(Brush), typeof(PullToRefreshListView), null);
/// <summary>
/// Identifies the <see cref="PullRefreshCommand"/> property
/// </summary>
public ICommand PullRefreshCommand
{
get { return (ICommand)GetValue(PullRefreshCommandProperty); }
set { SetValue(PullRefreshCommandProperty, value); }
}
/// <summary>
/// Identifies the <see cref="PullRefreshCommand"/> property
/// </summary>
public static readonly DependencyProperty PullRefreshCommandProperty =
DependencyProperty.Register("PullRefreshCommand", typeof(ICommand), typeof(PullToRefreshListView), null);
/// <summary>
/// Triggered when the user requested a refresh.
/// </summary>
public event EventHandler PullRefresh;
}
/// <summary>
/// The PullToRefreshOuterPanel works together with the <see cref="PullToRefreshInnerPanel"/>
/// to workaround the problem where List Virtualization is disabled when ListView is hosted
/// inside a ScrollViewer.
/// Specifically the InnerPanel is able to report to the ListView as available height the OuterPanel's height
/// instead of infinity which would be the case when a ScrollViewer is hosted inside another ScrollViewer.
/// </summary>
internal class PullToRefreshOuterPanel : Panel
{
public Size AvailableSize { get; private set; }
public Size FinalSize { get; private set; }
protected override Size MeasureOverride(Size availableSize)
{
AvailableSize = availableSize;
// Children[0] is the outer ScrollViewer
this.Children[0].Measure(availableSize);
return this.Children[0].DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
FinalSize = finalSize;
// Children[0] is the outer ScrollViewer
this.Children[0].Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
return finalSize;
}
}
/// <summary>
/// The PullToRefreshOuterPanel works together with the <see cref="PullToRefreshInnerPanel"/>
/// to workaround the problem where List Virtualization is disabled when ListView is hosted
/// inside a ScrollViewer.
/// Specifically the InnerPanel is able to report to the ListView as available height the OuterPanel's height
/// instead of infinity which would be the case when a ScrollViewer is hosted inside another ScrollViewer.
/// </summary>
internal class PullToRefreshInnerPanel : Panel, IScrollSnapPointsInfo
{
EventRegistrationTokenTable<EventHandler<object>> _verticaltable = new EventRegistrationTokenTable<EventHandler<object>>();
EventRegistrationTokenTable<EventHandler<object>> _horizontaltable = new EventRegistrationTokenTable<EventHandler<object>>();
protected override Size MeasureOverride(Size availableSize)
{
// need to get away from infinity
var parent = this.Parent as FrameworkElement;
while (!(parent is PullToRefreshOuterPanel))
{
parent = parent.Parent as FrameworkElement;
}
var pullToRefreshOuterPanel = parent as PullToRefreshOuterPanel;
// Children[0] is the Border that comprises the refresh UI
this.Children[0].Measure(pullToRefreshOuterPanel.AvailableSize);
// Children[1] is the ListView
this.Children[1].Measure(new Size(pullToRefreshOuterPanel.AvailableSize.Width, pullToRefreshOuterPanel.AvailableSize.Height));
return new Size(this.Children[1].DesiredSize.Width, this.Children[0].DesiredSize.Height + pullToRefreshOuterPanel.AvailableSize.Height);
}
protected override Size ArrangeOverride(Size finalSize)
{
// need to get away from infinity
var parent = this.Parent as FrameworkElement;
while (!(parent is PullToRefreshOuterPanel))
{
parent = parent.Parent as FrameworkElement;
}
var pullToRefreshOuterPanel = parent as PullToRefreshOuterPanel;
// Children[0] is the PullToRefreshIndicator
this.Children[0].Arrange(new Rect(0, 0, this.Children[0].DesiredSize.Width, this.Children[0].DesiredSize.Height));
// Children[1] is the ItemsScrollViewer
this.Children[1].Arrange(new Rect(0, this.Children[0].DesiredSize.Height, pullToRefreshOuterPanel.FinalSize.Width, pullToRefreshOuterPanel.FinalSize.Height));
return finalSize;
}
bool IScrollSnapPointsInfo.AreHorizontalSnapPointsRegular => false;
bool IScrollSnapPointsInfo.AreVerticalSnapPointsRegular => false;
IReadOnlyList<float> IScrollSnapPointsInfo.GetIrregularSnapPoints(Orientation orientation, SnapPointsAlignment alignment)
{
if (orientation == Orientation.Vertical)
{
var l = new List<float>();
l.Add((float)this.Children[0].DesiredSize.Height);
return l;
}
else
{
return new List<float>();
}
}
float IScrollSnapPointsInfo.GetRegularSnapPoints(Orientation orientation, SnapPointsAlignment alignment, out float offset)
{
throw new NotImplementedException();
}
event EventHandler<object> IScrollSnapPointsInfo.HorizontalSnapPointsChanged
{
add
{
var table = EventRegistrationTokenTable<EventHandler<object>>
.GetOrCreateEventRegistrationTokenTable(ref this._horizontaltable);
return table.AddEventHandler(value);
}
remove
{
EventRegistrationTokenTable<EventHandler<object>>
.GetOrCreateEventRegistrationTokenTable(ref this._horizontaltable)
.RemoveEventHandler(value);
}
}
event EventHandler<object> IScrollSnapPointsInfo.VerticalSnapPointsChanged
{
add
{
var table = EventRegistrationTokenTable<EventHandler<object>>
.GetOrCreateEventRegistrationTokenTable(ref this._verticaltable);
return table.AddEventHandler(value);
}
remove
{
EventRegistrationTokenTable<EventHandler<object>>
.GetOrCreateEventRegistrationTokenTable(ref this._verticaltable)
.RemoveEventHandler(value);
}
}
}
}


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:controls="using:PullToRefreshListView">
<Style TargetType="controls:PullToRefreshListView">
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="TabNavigation"
Value="Once" />
<Setter Property="IsSwipeEnabled"
Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode"
Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled"
Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode"
Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled"
Value="True" />
<Setter Property="ScrollViewer.ZoomMode"
Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled"
Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange"
Value="True" />
<Setter Property="PullIndicatorForeground"
Value="Gray"/>
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:PullToRefreshListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PullStates">
<VisualState x:Name="Pulling">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pulling"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="notPulling"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="rotArrow"
Storyboard.TargetProperty="Angle"
To="0"
Duration="0:0:.25" />
</Storyboard>
</VisualState>
<VisualState x:Name="NotPulling">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="rotArrow"
Storyboard.TargetProperty="Angle"
To="-180"
Duration="0:0:.25" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<controls:PullToRefreshOuterPanel x:Name="OuterPanel"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="Enabled"
VerticalScrollBarVisibility="Hidden"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
VerticalSnapPointsType="MandatorySingle"
VerticalSnapPointsAlignment="Near"
AutomationProperties.AccessibilityView="Raw">
<controls:PullToRefreshInnerPanel x:Name="InnerPanel"
VerticalAlignment="Stretch">
<Border x:Name="PullToRefreshIndicator"
Height="60"
HorizontalAlignment="Stretch"
Opacity="0">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Bottom">
<FontIcon RenderTransformOrigin=".5,.5"
Margin="0,0,4,0"
FontSize="18"
Glyph="&#xE110;"
VerticalAlignment="Center"
Foreground="{TemplateBinding PullIndicatorForeground}"
UseLayoutRounding="False">
<FontIcon.RenderTransform>
<RotateTransform x:Name="rotArrow" />
</FontIcon.RenderTransform>
</FontIcon>
<StackPanel Orientation="Vertical">
<Grid>
<TextBlock Text="{TemplateBinding RefreshText}"
x:Name="notPulling"
FontSize="16"
HorizontalAlignment="Center"
Foreground="{TemplateBinding PullIndicatorForeground}" />
<TextBlock Text="{TemplateBinding ReleaseText}"
Visibility="Collapsed"
x:Name="pulling"
FontSize="16"
HorizontalAlignment="Center"
Foreground="{TemplateBinding PullIndicatorForeground}" />
</Grid>
<TextBlock Text="{TemplateBinding PullSubtext}"
HorizontalAlignment="Center"
Visibility="Collapsed"
FontSize="12"
Foreground="{TemplateBinding PullIndicatorForeground}" />
</StackPanel>
</StackPanel>
</Border>
<ScrollViewer x:Name="ItemsScrollViewer"
VerticalScrollMode="Enabled"
VerticalScrollBarVisibility="Hidden">
<ItemsPresenter Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}" />
</ScrollViewer>
</controls:PullToRefreshInnerPanel>
</ScrollViewer>
</controls:PullToRefreshOuterPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

<Geeking>

The first most interesting and important detail of this solution is the trick the Microsoft sample used to bypass the classic problem of putting a ListView inside a ScollViewer which effectively kills Virtualization. Since the PullToRefresh indicator needs to be inside and above the ScrollViewer that hosts the list the same problem applies here. To overcome this
the Microsoft sample used two custom panels (an Inner and Outer) that know each other and can “talk” to each other. The reason virtualization is killed is because when a ListView is hosted inside a ScrollViewer then it thinks it has infinite vertical space because that’s what the ScrollViewer is telling it. The solution above works around this problem by allowing the Inner panel to figure out the Outer panel’s actual visible height and thus correctly calculate it’s own actual visible height which it can get by simply subtracting the size of the PullToRefresh indicator from the Outer panel’s height.
Cool trick.

The second most interesting and important detail was figuring out if the user released their finger when the ScrollViewer was over-stretched.  The limitation of the sample is that the Refresh is scheduled to be triggered once the user overstretches even if the user decides to cancel by slowly unstretching the ScrollViewer back to the original position.
The original attempts to solve this failed because ScrollViewer only exposes the DirectManipulationStarted and DirectManipulationCompleted events and the second one fires too late. Loong after the user has released their finger. The DirectManipulationPointerReleased  event was not available unfortunately.
The trick to solve this turned out to be the use of the ViewChanging event (see line 114) and figuring out the exact moment the user released his/her finger from the screen.
When this occurs at the desired time then :

  1. The event would be Inertial.
  2. The ScrollViewer would be overstretched so VerticalOffset should be zero and
  3. The direction of the inertial movement should be upwards towards the non-stretched state.

This allowed users to change their minds if they didn’t really want to refresh.

</Geeking>

Update: Microsoft has released an updated PullToRefresh  sample that you might also want to evaluate here. There’s also the UWP Community Toolkit here.

WP7 vs WP8 Checkbox Style Changes

Hey. I had a small problem yesterday. Minor annoyance to be exact.

What were you trying to do?
Copy paste a WP7 control’s XAML, as is, to be used in a WP8 app.

What happened?
It didn’t work as is. A built-in control’s default style changed in WP8

Really? Which one?
The Checkbox. Well here, see for yourself :

WP7_WP8_Checkbox_Diff

What the what?? How’d that happen?
I think they just changed the controls style. Just like that.
Wait, i have a WinMerge report of the style differences as well here (D/L the html file and open it)

Tip: Open up Blend and click “Edit Template” on the Checkbox to get a copy of its style. Be sure
to do it inside the correct project each time.

Oh i see. How’d that make you feel?
I was a little bummed out..

I was bummed out

Why?
Why? Cause i am lazy and that’s extra work i didn’t want to do.

Relax. It happens. Why would Microsoft do that anyways?
Hell if i know..

Hell if i know

So what are you planning on doing?
Well, I don’t care for the new style. I want to keep the old one. At first i thought I’d make an App-wide StaticResource out of the old style.
Turns out that won’t seem to work easily. Here’s the exception i got by using the old style

CheckBoxStyleException

The new Checkbox doesn’t seem to like it’s old style anymore… Stay tuned for the solution.

-START UPDATE (WITH FIX AND BONUS TOOL) :
The fact that the old style didn’t immediately work on WP8 had to do obviously with a bunch
of missing or changed styles. Well after some dirty work here is the WP7-Like CheckBox Style for WP8 that makes it look like it did on WP7 WP7CheckBoxStyle.xaml.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Color x:Key="WP7PhoneRadioCheckBoxCheckDisabledColor">#66000000</Color>
    <Color x:Key="WP7PhoneRadioCheckBoxDisabledColor">#66FFFFFF</Color>
    <Color x:Key="WP7PhoneRadioCheckBoxPressedBorderColor">#FFFFFFFF</Color>
    <Color x:Key="WP7PhoneRadioCheckBoxColor">#BFFFFFFF</Color>
    <Color x:Key="WP7PhoneRadioCheckBoxPressedColor">#FFFFFFFF</Color>
    <Color x:Key="WP7PhoneRadioCheckBoxCheckColor">#FF000000</Color>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxBrush" Color="{StaticResource WP7PhoneRadioCheckBoxColor}"/>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxCheckDisabledBrush" Color="{StaticResource WP7PhoneRadioCheckBoxCheckDisabledColor}"/>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxDisabledBrush" Color="{StaticResource WP7PhoneRadioCheckBoxDisabledColor}"/>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxPressedBorderBrush" Color="{StaticResource WP7PhoneRadioCheckBoxPressedBorderColor}"/>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxPressedBrush" Color="{StaticResource WP7PhoneRadioCheckBoxPressedColor}"/>
    <SolidColorBrush x:Key="WP7PhoneRadioCheckBoxCheckBrush" Color="{StaticResource WP7PhoneRadioCheckBoxCheckColor}"/>

    <Style x:Key="WP7PhoneButtonBase" TargetType="ButtonBase">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
        <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
        <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
        <Setter Property="Padding" Value="10,3,10,5"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ButtonBase">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneBackgroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneForegroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
                            <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="WP7PhoneRadioButtonCheckBoxBase" BasedOn="{StaticResource WP7PhoneButtonBase}" TargetType="ToggleButton">
        <Setter Property="Background" Value="{StaticResource WP7PhoneRadioCheckBoxBrush}"/>
        <Setter Property="BorderBrush" Value="{StaticResource WP7PhoneRadioCheckBoxBrush}"/>
        <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMedium}"/>
        <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilyNormal}"/>
        <Setter Property="HorizontalContentAlignment" Value="Left"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Padding" Value="0"/>
    </Style>
    <Style x:Key="WP7CheckBoxStyle" BasedOn="{StaticResource WP7PhoneRadioButtonCheckBoxBase}" TargetType="CheckBox">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <Grid Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="CheckBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxPressedBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="CheckBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxPressedBorderBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="CheckMark">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxCheckBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="IndeterminateMark">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxCheckBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="CheckBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="CheckBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="CheckMark">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxCheckDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Fill" Storyboard.TargetName="IndeterminateMark">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource WP7PhoneRadioCheckBoxCheckDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="CheckStates">
                                <VisualState x:Name="Checked">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="CheckMark">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Unchecked"/>
                                <VisualState x:Name="Indeterminate">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="IndeterminateMark">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid Margin="{StaticResource PhoneTouchTargetLargeOverhang}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="32"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid Grid.Column="0">
                                <Border x:Name="CheckBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{StaticResource PhoneBorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="Left" Height="32" IsHitTestVisible="False" VerticalAlignment="Center" Width="32"/>
                                <Rectangle x:Name="IndeterminateMark" Fill="{StaticResource WP7PhoneRadioCheckBoxCheckBrush}" HorizontalAlignment="Center" Height="16" IsHitTestVisible="False" Visibility="Collapsed" VerticalAlignment="Center" Width="16"/>
                                <Path x:Name="CheckMark" Data="M0,123 L39,93 L124,164 L256,18 L295,49 L124,240 z" Fill="{StaticResource WP7PhoneRadioCheckBoxCheckBrush}" FlowDirection="LeftToRight" HorizontalAlignment="Center" Height="21" IsHitTestVisible="False" Stretch="Fill" StrokeThickness="3" StrokeLineJoin="Round" Visibility="Collapsed" VerticalAlignment="Center" Width="23"/>
                            </Grid>
                            <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="1" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="12,0,0,0" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Here is a screenshot that shows off 1) how to use the Style and 2) how it actually looks/works:

WP7_WP8_FixedStyle_Vertical
I needed some help for that painful process of finding out what style changed and how between WP7 and WP8 thats why i built a small tool :

WPThemeReviewerTool1 WPThemeReviewerTool2

I call it “WinPhone Theme Reviewer” , it targets WP8 only for now and you can download the source or xapfile.
What i did, essentially, is include MS’s Theme xaml files located “C:\Program Files (x86)\Microsoft SDKs\Windows Phone\vXX\Design\ThemeResources.xaml” for both WP71 and WP8 in the App.

Enjoy 🙂


-END UPDATE


Wait. That probably means even more default styles might be messed up..
know…We’ll just have to wait and see..

Btw. You know you are talking to yourself right?
….