Friday, 24 August 2012

Need a WrapPanel in WinRT?

I did, as the existing WrapGrid and VirtualizingWrapGrid weren’t up to the job of the panel I had used in my Windows Phone project.

I thought that because it’s a Panel implementation, it probably doesn’t have all the much code, having seen the one in Adam Nathan’s WPF 4 book.

So, I started by getting the implementation from the silverlight toolkit.

I only needed the following files:

NumericExtensions.cs, OrientedSize.cs and WrapPanel.cs.

While compiling a few namespaces were required to be changed and a couple of attributes removed, but after that, it’s now fully functional.

A new pattern for Win8 ApplicationViewState handling

While trying to port our app Bridge Stenographer I was faced with the problem of supporting the new ViewStates in Windows 8.

I didn’t really like the idea of having a bunch of duplicate controls on my UI that I hid.

I didn’t really like the idea of having a frame that I navigated to a completely separate set of controls.

And I really didn’t like the messy syntax for managing view states with the VisualStateManager, particularly in light of my previous experiences of Visual States never doing what you expect.

So here’s my idea:

First, in the Common folder, modify LayoutAwarePage.cs to make the ObservableDictionary available to the rest of the classes by making it public (or protected).

Set up bindings in your DefaultView model, labelled with keys that define the states you require.  So for instance, I need to move some items from one grid location to another based on my state changes.

DefaultViewModel["keyboardRow"] = 2;
DefaultViewModel["keyboardColumn"] = 1;
DefaultViewModel["keyboardMaxWdith"] = 465;
_fullpage = DefaultViewModel;



Where _fullpage is defined as (sorry about Wdith typo above.  How to edit in LiveWriter???)


private IObservableMap<string, object> _fullpage;
private IObservableMap<string, object> _snapped;



Then in my constructor I also add the _snapped object:


_snapped = new ObservableDictionary<string, object>;
_snapped["keyboardMaxWidth"] = 300;
// etc...


Then in my code, I simply bind to

<somecontrol MaxWidth="{Binding DefaultViewModel[keyboardMaxWidth]}" />


And swap the DefaultViewModel in response to the SizeChanged event on the page:


void BiddingPage_SizeChanged(object sender, SizeChangedEventArgs e)
{
switch (ApplicationView.Value)
{
case ApplicationViewState.Snapped:
DefaultViewModel = _snapped;
break;
default:
DefaultViewModel = _fullpage;
break;
}
InvalidateArrange();
}


Hey Presto!  New layout.
I was looking for a way to do this declaratively in the XAML, by creating the Observable collections there, but alas, haven’t yet figured out how to do that.

Problems subclassing UserControl on WinRT

While trying to figure out how to add a generic mechanism for accelerators on buttons on my Win8 port of Bridge Stenographer I was faced with blank stares when asking about Keyboard Shortcuts

AutomationProperties.AccessKey="Number2" looked like the perfect property to add on my 2 button for instance, but I was disappointed to find out that nothing supported it.

In the end, I needed to have a base class that did some of the handling for my keyboard mapping to the access keys, as I don’t want to write that code over and over again (actually, I don’t want to debug and keep parallel copies up to date, but I digress).

So, what better way than a base class I thought.

So, here’s what I did.  I had my keyboard like control based on a UserControl.  Why not just change the base class to SpecializedUserControl like this:

public class SpecializedUserControl : UserControl
{

public SpecializedUserControl()
{
}

public void ExtraFunctionality()
{
}
}



Then, I can create a new user control, and base it on this new base class like this:


public sealed partial class MySpecializedUserControlInstance : SpecializedUserControl
{
public MySpecializedUserControlInstance()
{
this.InitializeComponent();
}
}


and declare it in XAML like this:

<local:SpecializedUserControl
x:Class="hoverbug.MySpecializedUserControlInstance"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:repro"
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">

<Grid>
<Button>Sorry Charlie!</Button>
</Grid>
</local:SpecializedUserControl>


Interestingly, while writing this, I was disappointed to find that everything worked fine, and I wasn’t getting the error I got in my actual code.


Here’s how I instantiated the control on my page:


<local:MySpecializedUserControlInstance DataContext={Binding }" />


Not to be beaten (as it really didn’t appear to work in my code), I started porting across bits of my implementation, a feature of which was that I needed a property to attach to controls in my implementation that could hold a function to call when the AccessKey was pressed, so I did it like this in SpecializedUserControl:


public RoutedEventHandler KeyboardAction
{
get { return (RoutedEventHandler)GetValue(KeyboardActionProperty); }
set { SetValue(KeyboardActionProperty, value); }
}

// Using a DependencyProperty as the backing store for KeyboardAction. This enables animation, styling, binding, etc...
public static readonly DependencyProperty KeyboardActionProperty =
DependencyProperty.RegisterAttached("KeyboardAction", typeof(RoutedEventHandler), typeof(SpecializedUserControl),
new PropertyMetadata(null));


Now when I run the same program, I get this error:


oops


Anyone have any idea why adding a declaration of an attached property breaks it in this way?

PointerOver visual state on WinRT, it’s over even if it’s not.

I’ve been trying to get my Bridge Stenographer App ported to Windows 8 and in the process have come across a few problems.  The latest being that I click on a button on the welcome page to get somewhere else in the app, and when I navigate back, my button is still in the pointer over state, despite the pointer being elsewhere.

I’ve tracked this down to using  NavigationCacheMode="Required" on the page, in order to keep my page alive.

As a result, when I navigate back to the page, the controls don’t get whatever the equivalent of MouseLeave is, and therefore don’t transition out of the PointerOver state, and this doesn’t just apply to Buttons.  Combo boxes have this behavior as well.

You’ve been warned.

Friday, 27 July 2012

Data-Binding Pitfalls on WPF, WP7, Silverlight and WinRT Part 2

Most of my data binding worries stem from implementing my view model for MVVM.

One of the things I noticed was that it was very difficult to get things right when multiple properties are combined to form another property, so a change to any input would need to raise notifications that the derived property had changed. 

This led to code that was difficult to debug, write and maintain.  In addition we found that in many cases, properties weren’t changed in isolation.  Changing one property on an object often meant others would soon change too.

So, to deal with this issue, we built a class that coalesced property notifications, and also had a representation of dependent properties which would fire when an input changed.  With the addition of a FreezeNotifications and UnfreezeNotifications mechanism, we could queue up all of the changes, and then fire each change only once.

However, we still end up with many properties on the same object potentially firing, and still requiring recalculation of layout multiple times.

In the previous post we were concerned with how data binding worked on a control, and in a control the properties are generally a Dependency Property.  In this example the properties in question simply implement INotifyPropertyChanged, and the objects in question aren’t based on the Control class, so calling Invalidate won’t suffice to defer our work.

In the end, here’s what we did. 

We had several objects that needed to connect to the data to get property notifications.  When a property changed, we marked our object as dirty in such a way we could determine what needed to be updated.

We then fired off a Task, which collected the data from the database, and built up the information required.  In the case where a previous task was already running, we called Cancel on the existing task, and then started a new task to load the newly loaded data.

This worked ok.  The tasks ran on another thread on the multi core machine, but we found that we were cancelling the task about 6 times before finally allowing it to run to completion.

How can I Stop Wasting Cycles?

We thought there had to be a better way.  And that turned out to be our old friend Dispather.BeginInvoke.  Instead of launching the recalculation immediately, we asked for it to happen when we finally got around to processing messages again.

Initially we passed it with the DispatcherPriority.DataBind, to get it into the DataBinding queue.  That indeed worked, and we no longer get any cancellation requests, as change detection all works on the UI thread, and when we finally relinquish control, we then start our task, and mark the object as not dirty.

When subsequent notifications arrive, the work is already done, so no further action is taken.  There’s probably a further improvement to be had by checking that we haven’t already marked the object as dirty, and avoiding queuing the call altogether.

Finally, we wondered if we really needed to drop to that priority level, and the answer was no; Normal priority works just fine.

The Moral of the Story?

Don’t be in a big hurry to do the work, as someone might change their mind, and you’ll just have to start all over again.  Instead, schedule the work to be done using Dispatcher.BeginInvoke. 

But BeginInvoke doesn’t exist on WINRT

It appears that CoreDispatcher.RunAsync gives us 3 priority levels, so changing to this method on WinRT will probably continue to work (if you can manage to get your hands on a CoreDispatcher object in the first place)

Thursday, 26 July 2012

Data-Binding Pitfalls on WPF, WP7, Silverlight and WinRT Part 1

I've been doing a lot of work on all four platforms (WPF, WP7, Silverlight and WinRT), and am puzzled by how data-binding is supposed to be implemented.

For example, in David Anson's Data Visualization toolkit, you can be penalized with an extraordinarily long delay if property changes happen in the wrong order.
http://forums.silverlight.net/t/101287.aspx/1

The problem in that specific instance is that the control sets the default range to 1 year, but the order of changing properties can cause the problem to occur at any point.  For example, if you were to change a date time axis from a range of 1 day to 1 year and back, with intervals of 1 hour and 1 month respectively, on the way to 1 year, you'd need to set the interval first, and the range second.  In the other direction, it's the opposite.

Now with Data Binding, it's not clear how you'd arrange this.  In my case in WPF everything appeared to be working just fine, until I newed up a control for printing.  Printing worked fine, but PrintPreview had a hugely long delay, before the preview window appeared.  The culprit?  The DateTimeAxis.

It turned out the problem was provoked by my code behind setting the Interval and Interval type.  When I changed it to data-binding, the problem disappeared.

Now, I suspect the reason this fixed the problem has to do with the options on Dispatcher.BeginInvoke and the Dispatcher itself.  If you look there (in WPF) the DispatcherPriorities includes Render and DataBind levels in addition to Normal.  Shawn Wildermuth wrote an article a long time ago about the dispatcher http://msdn.microsoft.com/en-us/magazine/cc163328.aspx that provides some insights.

If you examine the code in the DateTimeAxis, you’ll see that on a property change, most of what happens is a simple call to Invalidate().  That pushes the refresh down to the Render level, allowing all of the other Invalidate calls to occur before doing any work for changes resulting from property changes.  I’ll come back to that scheduling trick in a part 2 tomorrow.

Now, my code didn’t use data binding for all of it’s properties.  It all ran on the UI thread in response to the user asking to print.  As a result, (if my analysis is correct) the initial data binding has not yet run, as we haven’t ceded control to the dispatcher.

Eventually, my UI control is asked to Measure and Arrange from my code on the UI thread, and finally UpdateLayout is called by my code.  Because I had set some of the properties in code (the Interval, and Interval Types) but not the Max and Min, when the control is being laid out it does all the work to create the stupid number of ticks for each hour in a year.

Then, inside UIElement.UpdateLayout() I have this call stack:

PresentationFramework.dll!MS.Internal.Data.DataBindEngine.Task.Run(bool lastChance) + 0x31 bytes   
PresentationFramework.dll!MS.Internal.Data.DataBindEngine.Run(object arg) + 0xb6 bytes   
PresentationFramework.dll!MS.Internal.Data.DataBindEngine.OnLayoutUpdated(object sender, System.EventArgs e) + 0x1e bytes   
PresentationCore.dll!System.Windows.ContextLayoutManager.fireLayoutUpdateEvent() + 0x154 bytes   
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() + 0x926 bytes   
PresentationCore.dll!System.Windows.UIElement.UpdateLayout() + 0x16 bytes   

So, it finally data-binds the rest of my values, and lays out the axis correctly with the intended values for Max and Min.

By switching everything to use data-binding instead mixing it with code-behind, the default values in the DateTimeControl work correctly (though the work is still wasted), and then the data-binding sets them to the new values.

It’s this wasted effort that concerns me, so future instalments will examine what can be done about it.

Wednesday, 25 July 2012

Landscape Printing and Preview in WPF: It’s the overload, stupid!

We’re just finishing up a new release for our weather station software, and thought it might be nice to add printing for our users.

The printouts lend themselves to Landscape orientation, but much as we searched, we couldn’t find a way to get the DocumentViewer control to show us the preview in Landscape mode.

In the end, there are a sequence of things that need to be done.

First, when you create your PrintDialog, make sure you set the PageOrientation property to PageOrientation.Landscape.  That in itself isn’t enough though.

So, now store the size of the paper from the successful print dialog, and construct your DocumentPaginator object.

var ms = printDialog.PrintTicket.PageMediaSize;
Size pageSize = (printDialog.PrintTicket.PageOrientation == PageOrientation.Portrait) ?
new Size(ms.Width.Value, ms.Height.Value) :
new Size(ms.Height.Value, ms.Width.Value);

var Paginator = new SamplePaginator(uc, pageSize);


Where uc some control or Framework Element.


And here’s the important bit.  When you return a DocumentPage, make sure you use the overload that takes the PageSize you calculated above.  If you do, then preview works great. 


I had naively assumed that overriding the PageSize abstract property would do this job, but it did not.