Note to Self: The Windows Store GridView Control, Deconstructed

Tuesday, July 09, 2013 / Posted by Luke Puplett / comments (0)

Here’s a diagram of the GridView control for Windows Store apps, deconstructed into its components. This is intended to show all the templates and styles in various places to help customise this complex control.

Download here.

Labels: , , , ,

Windows Phone Panorama background image cropped when smaller than screen size.

Wednesday, May 16, 2012 / Posted by Luke Puplett / comments (0)

CroppedPerfect

Notice that the background image repeats just before the ‘r’ on the right side of the left image, but its fine on the other image.

This image on the left is 512x384 whereas the image on the right is twice the size, 1024x768. It seems that when the image must be stretched to fill the height of the screen, the width is truncated. When an image must be shrunk to fit, then the width is preserved.

The Sretch property isn’t required for the larger image, but it works when set as Fill or UniformToFill. The smaller image needs UniformToFill to look correct.

Labels: , ,

Combining Anonymous Types with Dynamic

Sunday, September 25, 2011 / Posted by Luke Puplett / comments (0)

This short blog post shows the outcome of a quick experiment. I am researching a way to back a WPF View with a ViewModel whose properties are not known until runtime.


It’s been a long time since I last blogged. I built a house, I’ve had a baby, well, Cassie did the hard work, and also worked on a very large Silverlight project for a large investment bank (a foreign exchange trading platform) and then moved again to a start-up in the West End of London.

The former job was all hush-hush and even though our small team had the pleasure of a couple of exclusive hours with Silverlight evangelist Mike Taulty, we weren’t able to tell him what we were working on. So blogging was out of the question, which is a real shame for me and the community as a whole, in my opinion (although understandable).

The Interesting Bit
Anyway, while playing this afternoon, I discovered that anonymous types can escape the confines of the scope they’re declared in by returning them as dynamic!
Check this out:
1 static void Main(string[] args) 2 { 3 var rows = ReadDynamicRows(); 4 5 foreach (var row in rows) 6 Console.WriteLine(row.Id); 7 8 object o = rows[0]; 9 var properties = o.GetType().GetProperties(); 10 } 11 12 private static List<dynamic> ReadDynamicRows() 13 { 14 dynamic row; 15 List<dynamic> rows = new List<dynamic>(); 16 using (var reader = 17 DataHelper.ExecuteSqlStatementReader( 18 DataHelper.BuildConnectionString(), 19 "SELECT TOP 1000 * FROM SomeTable")) 20 { 21 while (reader.Read()) 22 { 23 row = new 24 { 25 Id = reader.GetString(0), 26 }; 27 28 rows.Add(row); 29 } 30 } 31 32 return rows; 33 }


What’s really interesting to me is that the properties can be reflected upon. In my experiments with the dynamic type, this isn’t true and so databinding won’t work.


I don’t believe its that simple to stick them in an ObservableCollection bound to a DataGrid because the mechanics of it uses ICustomTypeDescriptor


More to come... (have a train to catch)

Labels: , ,

Detecting Windows Phone Screen Size

Monday, November 15, 2010 / Posted by Luke Puplett / comments (0)

Eventually, and probably not long from now, the Windows Phone will have a form factor that isn’t 480 x 800 so here’s how to detect the screen size:

protected PhoneViewModel()
{
    this.ScreenWidth = System.Windows.Application.Current.Host.Content.ActualWidth;
    this.ScreenHeight = System.Windows.Application.Current.Host.Content.ActualHeight;
}

Labels: , , ,

Recent Changes to vuPlan.tv Client

Thursday, October 07, 2010 / Posted by Luke Puplett / comments (0)

Since writing vuPlan.tv I’ve been using a placeholder company name ‘S26’ which I had to refactor to reflect my final company name Evoq Limited. The new codebase is built with new filenames and namespaces.

Also, the Windows 7 implementation of the Media Center API, at the COM interop level, has a memory leak. The new client works around the problem by creating an API shim inside a new AppDomain which gets recycled at intervals.

Finally, after seeing Mike Taulty’s session at UK Tech Days and his showing-off of new Metro-esque Windows 7 WPF applications Zune and MetroTwit, I reverted the vuPlan.tv client back into its Metro pyjamas. Previously I’d not done enough work on the shadow which is why it never really rocked my world; this version is much better I think.

vuPlan.tv Funkier Metro Look

Testing is on-going as it work on the web application/site. And that reminds me, I must update the images on the site.

Luke

Labels: , , , ,

The Shame of CSS (or HTML)

Tuesday, August 17, 2010 / Posted by Luke Puplett / comments (0)

Is the web built on the heroic patience of thousands of creative geniuses, or by a bunch of morons? This is what I'm left wondering after a second go at HTML and CSS.

As a programmer working in programming languages, I'm used to being a dictator of determinism.

That is to say that, what I instruct the computer to do, happens exactly as I dictate. I run the whole show. This affords me as much power as it does responsibility. If I get those instructions wrong, my programme will not compile. If my code runs, it is computationally perfect even if there are logical bugs. My world is mathematically beautiful.

Due to a problem with my web designer deciding to ignore me, I thought I could knock up a very simple two-column, header, content, footer type web-page in no time. I was wrong and the memories of why I hired a web dude in the first place, came flooding back.

For years now I've been reading the UK’s most popular web designer/developer rag .NET Magazine (nothing to do with that .NET, the irony being that the editor almost completely eschews .NET), and not once has the magazine cared to tell me that HTML is utter garbage.

Born out of SGML and XML, HTML differs in that its tags don't need to be closed. From its inception, HTML immediately repudiated it's first opportunity to work properly.

Although, to be honest, I'm not entirely sure whether it's not CSS that's to blame, since I don't know where HTML ends and CSS begins.

If CSS does the layout then CSS is to blame. I would take back my previous comment about HTML but like a couple of naughty children, they're both at fault even if the other one did it.

In retrospect, I see now that there were some clunking great CSS warning clues:

  • After 15 years of mainstream web content creation, no one has made a decent WYSIWYG designer.
  • CSS is often referred to as being "hand crafted" (in the way that chiselling wood is semi-random).
  • "Hacks" are the everyday vernacular of a web designer.

The simplest tasks are nigh on impossible. Like centering. Particularly centering some content in relation to some other thing to the side of it.

Moving something without nudging something else somewhere else you didn't want it or bringing some other thing along for the ride.

Grids. You used to be able to use Tables which worked really nicely but Health & Safety came along and told everyone that they must use a combination of DIVs and strong painkillers.

And fonts are out.

Please stare in amazement at this very simple attempt to put a logo in the corner of a page with a band of grey going along to top.

Designer

Looks alright doesn't it? I mean, the logo doesn’t have it’s alpha background but that’ll be alright in the real browser.

Here’s how to make this amazing page. You'd think to just put a logo image in the corner and make it a link. You can’t just make something a link, of course, so instead the HTML and CSS instructions say: stick a hyperlink in the corner and then set its background picture so it looks like a logo but have no link text and then fuck around a bit to make the link the same size as the background image and then add some margin. Also use someone else's 960 Grid System thing to shortcut the almost impossible process of aligning stuff.

As you go about "crafting" the page, the designer will show you what you want to see because it doesn't like it when you're mad, but really all your hard work looks like this:

IE8

Quite how Internet Explorer 8.0 - the same people that wrote the designer and Microsoft's best attempt to make a working web browser - manages to screw this one up so spectacularly is a mystery. Overlapping images? WTF?!

Okay, Firefox is the web developer's favourite PC browser, let's see what it looks like properly rendered.

Firefox

Yeah, erm. Almost. Firefox very nearly nailed this complex grey box and logo. Good attempt, gold star for effort. I have no idea how adding margin to my logo-cum-hyperlink managed to add a margin to the DIV that is the hyperlink's parent's parent but it's the equivalent to me doing a vasectomy on my granddad before my dad was born.

Of course stuff in the mark-up is never born. Not unless you're using a different language: XAML. I am used to XAML. I like XAML. Extensible Application Markup Language was written by the people that gave us the world's worst browser, however, XAML works perfectly.

In XAML, each tag is actually an instance of an object in memory. Each thing is created (born) when the XAML is processed, and things inside things, really do become the children of their parents. The system is tied to the underlying programming language which is compiled and so must be perfect.

I can create XAML elements in code, and I can create code elements in XAML. It’s a mark-up language for creating object graphs.

The XAML designer is perfect, stuff can be moved on the page without it writing the movements to the wrong place, and it shows reality.

By comparison, designing in HTML and CSS is like writing upside down with your wrong hand while blindfolded with only 4 lying bastards to assist you.

HTML 5 and CSS 3 aren't going to help much. They offer only a few extra commands to allow such extravagances as round corners and Flashless video.

To truly take the web into the future, the whole system needs to be bulldozed. As HTML 5 and CSS 3 have taken years and years to get this far (nowhere) then a new great system will never be the product of the committee. It would have to be a disruptive innovation from a team of just a few.

N.B. If there are unusual breaks throughout this document, it's because Windows Live Writer and Blogger can't decide how to format the HTML. Or maybe its the CSS.

Labels: , , ,

My Gestural Listbox in WPF Wearing Windows Phone 7 Clothes

Wednesday, April 28, 2010 / Posted by Luke Puplett / comments (7)

Here's a dire quality video of a control that I hope to find time to blog about in the coming weeks. It's inspiration is obvious, it's execution was tough. Involved crash-learning Newtonian mechanics, solving equations and a bit of calculus.

Essentially, its a smart scrollviewer, so it can house all manner of controls; indeed I've uploaded one of a large image being panned with flicks and drags.

If enough people are interesed, I'll put the control up on CodePlex.

Labels:
, , , ,

Luke's Ridiculously Simple MVVM Table Grid

Friday, February 26, 2010 / Posted by Luke Puplett / comments (0)

Windows Phone 7 Series extends the panning and scrolling UI metaphors seen in Windows Media Center and apparently in the Zune. This is no surprise as they are consumer entertainment products, or at least two are and the phone has been remarketed.

Anyway, to experiment with this style of interface, which lends itself so well to touch, I realised that I need a kind of table that can slide left and right, scroll up and down, be extended dynamically and do all this from a control-agnostic view model. Welcome to my ridiculously simple custom control.

Reductionism

When thinking about designing a user interface one must adopt a reductionist mentality. Each consituent part of a UI needs breaking down into units of functionality and within each one, there are usually more units of functionality until a reusable control is arrived at. And then there's that control's constituents which are usually a small number of stock WPF controls.

This idea comes quite easily to an object oriented programmer because its pretty much the same thought process as for designing classes of logic.

To be useable from a ViewModel that is not aware of the control, perhaps because the assembly that the view model is in doesn't reference the library of custom controls, it must support data binding using CLR base types.

A table is a bunch of bunches of stuff and at the time of writing, there's only one bunch of stuff class that supports databinding and that's the ObservableCollection<T>.

The Phone 7 interface scrolls sideways between 'pages' of contextual content either by gesture or with a click of a 'column' header - itself in its own slider.

500x_xboxlivephone

Each page scrolls as a normal page, with autohide scrollbars like the iPhone. The simplest logical representation of this logic - remember controls are just logic - is a horizontal StackPanel of ListBox controls.

That harmoniously translates into CLR land as an ObservableCollection of ObservableCollection objects. Nice.

To allow of column headings, I'm going to wedge a KeyValuePair in there, where K is a string and V is the ObservableCollection<object>

Code

Here's the full code the control. I'll explain some of the few remarkable points after. However there is a bug bug in this quick attempt: DataBinding doesn't call the setter and so doesn't set the CollectionChanged handler, which is why it works in my non-bound example.

The solution is to add/remove the handlers for this event in the OnTableSourceChanged handler, but I'm sure most of you can work this out. Is that lazy? Sorry but editting formatted code in the published article is a nightmare.



namespace WpfSimpleTableControl
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Controls;

    /// <summary>
    /// A StackPanel of ListBoxes where each ListBox represents a column of data which can be databound.
    /// </summary>
    /// <remarks>
    /// DataBinding works by mirroring the StackPanel of ListBoxes with an ObservableCollection of ObservableCollections.
    /// </remarks>
    public class SimpleTable : StackPanel
    {                  
        #region Constructors

        static SimpleTable()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SimpleTable), new FrameworkPropertyMetadata(typeof(SimpleTable)));
        }

        #endregion

        #region Properties

        #region Dependency Properties

        public ObservableCollection<KeyValuePair<string, ObservableCollection<object>>> TableSource
        {
            get { return (ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>)GetValue(TableSourceProperty); }
            set
            {
                var oldValue = this.TableSource;
                if (oldValue != null)
                    oldValue.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(table_CollectionChanged);

                value.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(table_CollectionChanged);
                SetValue(TableSourceProperty, value);
            }
        }
        public static readonly DependencyProperty TableSourceProperty = DependencyProperty.Register(
            "TableSource", typeof(ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>), typeof(SimpleTable),
            new PropertyMetadata(
                new PropertyChangedCallback(OnTableSourceChanged)));

        public Style ListBoxStyle
        {
            get { return (Style)GetValue(ListBoxStyleProperty); }
            set { SetValue(ListBoxStyleProperty, value); }
        }        
        public static readonly DependencyProperty ListBoxStyleProperty =
            DependencyProperty.Register("ListBoxStyle", typeof(Style), typeof(SimpleTable), new UIPropertyMetadata(null));
        
        public DataTemplate ItemDataTemplate
        {
            get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }
            set { SetValue(ItemDataTemplateProperty, value); }
        }
        public static readonly DependencyProperty ItemDataTemplateProperty =
            DependencyProperty.Register("ItemDataTemplate", typeof(DataTemplate), typeof(SimpleTable), new UIPropertyMetadata(null));

        #endregion

        #endregion

        #region Methods

        public void ReinitializeTableSource(ObservableCollection<KeyValuePair<string, ObservableCollection<object>>> table)
        {
            // Detach handlers and clear.
            //
            foreach(ListBox l in this.Children)
                l.LostFocus -= new RoutedEventHandler(listBox_LostFocus);
            //
            this.Children.Clear();
            
            if (table == null)
                return;

            foreach (var columnList in table)
            {
                string columnName = columnList.Key;
                
                var listBox = new ListBox();

                listBox.LostFocus += new RoutedEventHandler(listBox_LostFocus);

                if (this.ItemDataTemplate != null)
                    listBox.ItemTemplate = this.ItemDataTemplate;

                if (this.ListBoxStyle != null)
                    listBox.Style = this.ListBoxStyle;

                listBox.ItemsSource = columnList.Value;
                
                this.Children.Add(listBox);
            }
        }

        private void listBox_LostFocus(object sender, RoutedEventArgs e)
        {
            var listBox = (ListBox)sender;
            listBox.SelectedItem = null;
        }

        private void table_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ReinitializeTableSource((ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>)sender);
        }

        /// <summary>
        /// Reads properties and fires off the state changer using VSM.
        /// </summary>
        /// <param name="useTransitions">Whether to trigger animations between the states.</param>
        private void UpdateStates(bool useTransitions)
        {
            //if (this.IsFocused)
            //    VisualStateManager.GoToState(this, Ticker.FocusedStateName, useTransitions);
            //else
            //    VisualStateManager.GoToState(this, Ticker.UnfocusedStateName, useTransitions);
        }

        private static void OnTableSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var newTable = (ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>)args.NewValue;
            var control = (SimpleTable)sender;

            control.ReinitializeTableSource(newTable);
        }
        
        #endregion

        #region Control Overrides

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Grab named ControlTemplate assets and stick em in properties.
            //           
            //this.ScrollViewerElement = GetTemplateChild(SimpleTable.ScrollViewerPartName) as ScrollViewer;
            
            this.UpdateStates(false);
        }
        
        #endregion
    }
}

Starting at the top, it has no parts. This is because it derives from StackPanel and so it is just itself. It's just a panel.

Moving down the code, there are three DependencyProperty members. The first is the DataTable property which is typed as ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>, the second is ListBoxStyle which accepts a Style object which allows such things as removing the default black border around each element and then there's ItemDataTemplate which is a DataTemplate to apply to each of the listboxes.

It's likely that each ListBox will need its own different DataTemplate but it would be quite trivial to add a property that takes a DataTemplateSelector which can use your own logic to work out which DataTemplate to apply to which items.

The next interesting thing is the ReinitializeTableSource method. This kicks out all the old ListBox instances in the panel (itself) and then adds new ones. It copies over the ItemDataTemplate and ListBoxStyle to each new ListBox and also hooks up the data binding to each collection that represents column data.

It also sets an event handler for the lostfocus event which simply removes the selection, otherwise each independant listbox will have its own selection highlight. There might be situations when this is useful, but not today.

Further down there's the handler for the main outer collection CollectionChanged event which can only really occur when columns are added or removed and triggers the whole thing to be reconfigured.

OnTableSourceChanged handles changes to the TableSource dependency property which could itself be bound, this also trigger a complete reconfig.

The rest is left over from the parts and states model and can support the VisualStateManager paradigm.

Basic Model

The most simple use of the control looks like this.

SimpleTable UI

The two buttons manipulate a window level private collection of collections which is communicated via the magic of data binding to the table on screen.

The XAML for the window is here:

<Window x:Class="WpfSimpleTableControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfSimpleTableControl"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<local:SimpleTable x:Name="SimpleTable" Margin="20" Orientation="Horizontal" Height="200">
<local:SimpleTable.ListBoxStyle>
<Style TargetType="{x:Type ListBox}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Width" Value="150" />
</Style>
</local:SimpleTable.ListBoxStyle>
</local:SimpleTable>
<Button Width="150" Click="Button_Click" Margin="2">Add to column 2</Button>
<Button Width="150" Click="Button_Click" Margin="2">Add new column</Button>
</StackPanel>
</Grid>
</Window>

And the codebehind, which does not use MVVM for this simple demo, goes like this.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

namespace WpfSimpleTableControl
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ObservableCollection<KeyValuePair<string, ObservableCollection<object>>> _table 
            = new ObservableCollection<KeyValuePair<string, ObservableCollection<object>>>();

        public MainWindow()
        {
            this.Initialized += new EventHandler(MainWindow_Initialized);

            InitializeComponent();
        }

        void MainWindow_Initialized(object sender, EventArgs e)
        {
            var list = new ObservableCollection<object>();
            list.Add("Hello");
            list.Add("...world!");
            _table.Add(new KeyValuePair<string, ObservableCollection<object>>("Column1", list));

            list = new ObservableCollection<object>();
            list.Add("Latika!");            
            _table.Add(new KeyValuePair<string, ObservableCollection<object>>("Column2", list));


            this.SimpleTable.TableSource = _table;

            //this.SimpleTable.Background = Brushes.Gray;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var b = (Button)sender;

            switch (b.Content.ToString())
            {
                case "Add to column 2":
                    _table[1].Value.Add(DateTime.Now.Millisecond + b.Content.ToString());
                    break;

                case "Add new column":
                    _table.Add(new KeyValuePair<string,ObservableCollection<object>>("New Column"new ObservableCollection<object>()));
                    _table.Last().Value.Add("Default item.");
                    break;
            }
        }
    }
}

And with MVVM would really just be a binding in the XAML to a ObservableCollection<KeyValuePair<string, ObservableCollection<object>>> typed property on the ViewModel class.

Because this was designed with MVVM in mind, the clicking of items is not considered in the panel. Usually, the DataTemplate for the item binds a Command to a property on the item's ViewModel and so actions go direct.

I will return when I have made it look nice, added a DataTemplate and probably after discovering some minor nuance which completely trounces the whole thing's feasiblity and makes me look a tit.

Hopefully not though, maybe sometimes WPF can be simple. Maybe.

Labels: , , , , ,

Flowing Custom Control Values to ViewModel/Bindings

Friday, January 15, 2010 / Posted by Luke Puplett / comments (0)

This article discusses an issue I had when making a custom login box control, particularly the issue of getting the values in the username and password boxes to flow back down to the view-model. The issue might be my design, which could be fundamentally wrong, if only I knew of another way to do it.

The problem with Windows Presentation Foundation and its more promiscuous sister, Silverlight, is that its as good as it is bad. That is, its hard to deny its brilliance when you're carving out complex drawings and alpha gradients, adding subtleties to button animations and then there's the whole hardware accelerated 3D bit. But its bloody complicated because its mandate is to be everything to everyone and when so much is done for you all that stuff has to be massively multifaceted.

My LoginControl uses the parts and states model and the WPFToolkit to get at the VisualStateManager. I won't go into these things as they're documented enough and Karen Corby has a great MIX session video on it, too.

So I have a few parts, two TextBox controls and a Button. You can guess that these are username, password and the logon button.

Firstly, and I'll get this one over and done with quickly: I don't use the PasswordBox control. It does not support data binding because it takes a cautious approach and does things securely. Instead I apply a strikethrough formatting to a regular TextBox within its default style in generic.xaml.

You can see how this is done by reading my comments at the bottom of the MSDN article on the PasswordBox.

Welcome to my LoginControl

Cutting straight to the meat of the implementation, my control has the following properties:

  • private TextBox UsernameTextBox // the property which stores the TextBox UI control part/element for the username
  • private TextBox PasswordTextBox // the property which stores the TextBox UI control part/element for the password
  • public string Username // the dependency property which stores the username that can be bound to from a view model
  • public string Password // ditto but for the password

Because I can't bind my view model to a property of a the LoginControl's username TextBox and I don't want to expose the TextBox controls publicly, I use the following trick to hook up a binding between the TextBox.Text dependency property and LoginControl.Username (ditto for the password):

private TextBox UsernameTextBox
{
    get
    {
        return (TextBox)GetValue(UsernameTextBoxProperty);
    }
    set
    {
        // Detach observer from outgoing value.
        //
        var oldValue = this.UsernameTextBox;
        if (oldValue != null)
        {
            BindingOperations.ClearAllBindings(oldValue);
            oldValue.GotKeyboardFocus -= new KeyboardFocusChangedEventHandler(TextBoxes_GotKeyboardFocus);
            oldValue.KeyUp -= new KeyEventHandler(Username_KeyUp);
        }

        SetValue(UsernameTextBoxProperty, value);

        if (value != null)
        {
            value.SetBinding(TextBox.TextProperty, new Binding("Username")
            {
                Source = this,
                Mode = BindingMode.TwoWay
            });
            value.GotKeyboardFocus += new KeyboardFocusChangedEventHandler(TextBoxes_GotKeyboardFocus);
            value.KeyUp += new KeyEventHandler(Username_KeyUp);
        }
    }
}

And once that's done there's nothing else to consider except that when adding my LoginControl to my XAML page, I need to hook up the binding to the appropriate properties in the view model. That's easy:

Username="{Binding Path=Username, Mode=TwoWay}" Password="{Binding Path=Password, Mode=TwoWay}"

No worries there.

In Practice it Fails

The problem occurs when the login button is clicked and the ICommand is executed. The RelayCommand on the view model kicks in and my Action runs but if I stop the debugger right there I see that my view model does not have up-to-date Username and Password values!

My theory is that the binding system has not done its magic in between my typing of text and clicking of button. And although in theory the LostFocus events upon which data binding updates rely should have been triggered, my daisy-chained setup is somehow ballsing things up. Actually, there is a LostFocus on the username TextBox which should update the LoginControl.Username property, it doesn't, but even if it had, what's the activator to get the value to then jump from the Username property to its source down in my view model? By the way, flows up from source seem to work fine.

Whatever, I don't know. My solution was to grab each of my bindings and manually hit the UpdateSource method from within the login button's click method:

private void LoginButtonElement_Click(object sender, RoutedEventArgs e)
{
    var utbx = this.UsernameTextBox.GetBindingExpression(TextBox.TextProperty);
    utbx.UpdateSource();

    var ptbx = this.PasswordTextBox.GetBindingExpression(TextBox.TextProperty);
    ptbx.UpdateSource();

    var ubx = BindingOperations.GetBindingExpression(this, UsernameProperty);
    var pbx = BindingOperations.GetBindingExpression(this, PasswordProperty);

    ubx.UpdateSource();
    pbx.UpdateSource();

    if (this.PasswordTextBox != null)
    {
        if (!this.PasswordTextBox.IsFocused)
        {
            this.PasswordTextBox.Focus();
            if (!this.TryLoginButton.IsMouseOver)
                return;
        }
    }

    if (this.Command != null)
        this.Command.Execute(new KeyValuePair<stringstring>(this.Username, this.Password));
}

Which brings me right back to the point I made earlier about not doing this the right way. I'm not sure any other way would flow the values in time anyway. But then there's always the question of how everyone else doesn't seem to have this problem...

Labels: , , ,

Note to self: Turning a Path into a Resource in XAML

Wednesday, January 13, 2010 / Posted by Luke Puplett / comments (0)

Simple post mainly as a reminder to myself explaining how to make a complex path reusable because I keep forgetting this easy thing.

This goes in the asset resource dictionary.

<PathGeometry x:Key="ArrowHeadPath" Figures="M140.00002,39.999641 C134.47716,39.999641 129.47714,42.23822 125.85786,45.857513 L124.94602,46.962662 51.908844,119.99984 124.94678,193.03778 125.85786,194.14201 C129.47714,197.76131 134.47716,199.99988 140.00002,199.99988 151.04572,199.99988 160.00005,191.04556 160.00005,179.99985 160.00005,174.47699 157.76146,169.47699 154.14217,165.8577 L153.43225,165.27196 153.55394,165.14679 108.407,119.99986 154.20343,74.203423 154.14198,74.141975 154.14217,74.14183 C157.76146,70.522537 160.00005,65.52253 160.00005,59.999672 160.00005,48.95396 151.04572,39.999641 140.00002,39.999641 z M120,0.5 C185.99803,0.5 239.5,54.001972 239.5,120 239.5,185.99803 185.99803,239.5 120,239.5 54.001972,239.5 0.5,185.99803 0.5,120 0.5,54.001972 54.001972,0.5 120,0.5 z" />

Then this is how to use it (StaticResource probably better, transforms just for demo):

<Path Data="{DynamicResource ArrowHeadPath}" RenderTransformOrigin="0.7,0.5" MouseLeftButtonDown="ActivateWindowDrag" >
<Path.Fill>
<RadialGradientBrush GradientOrigin="0.495,0.55">
<GradientStop Color="#FFE2E2E2" Offset="0"/>
<GradientStop Color="#CC0F0316" Offset="0.3"/>
</RadialGradientBrush>
</Path.Fill>
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="3.6" ScaleY="3.6"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="8"/>
<TranslateTransform X="5" Y="-160"/>
</TransformGroup>
</Path.RenderTransform>
</Path>

Labels: ,

How I Dynamically Load a ViewModel from the Web

Tuesday, January 12, 2010 / Posted by Luke Puplett / comments (0)

For my vuPlan client I wanted to avoid those 'update me' messages that Adobe products are good at annoying me with. My aim was to be able to tweak my application logic, perhaps fix bugs, and do it unobtrusively. I also wanted to maintain a typical execution context of an ordinary locally installed application. To be clear, I've not fully investigated the options open to me with ClickOnce, and recently I became a user of Seesmic Desktop which is the first time I've seen it used in the wild. But even if you have different design goals, here's how I do dynamic MVVM.

Objective

To download a compiled ViewModel class from the web at runtime and inject it into a local client application so that it hooks into the view just like a normal View-ViewModel does. Also to allow the last known good ViewModel to load when the web version is unavailable.

This article is not a complete code breakdown of the method, rather a 10,000ft overview with a few details. The concept is straight-forward enough for most OO programmers to get.

Overview

Dunno about you but I'm a visual learner and find graphics and diagrams more palatable than words alone. This schematic should compress most of what would otherwise be a long and arduous blog post into 1080x673 (if you look at the full size one I stuck on flickaarrrr).

Diagram of ViewModel class relationships

Each of the black-rimmed screen looking objects are ViewModel classes. The inheritence tree runs right to left and so rooted with ViewModel.cs.

This class is the base for all and every view model I use thoughtout my application and is very similar to the base VM Josh Smith defines in his excellent MSDN article on the Model-View-ViewModel pattern.

The only addition to Josh's base ViewModel worth mentioning is that I add a property (typed as object) which I used to store a reference to the Dispatcher for the UI control to which the ViewModel will be bound.

The coloured blocks define the code distribution in the sense of partitioning over assemblies. (A) is the actual WPF application executable/project and would reside on the client PC once deployed, (B) is the dynamically loaded assembly containing business logic and (C) is a reusable shared library.

The assemblies reference each other like so:

  • Assembly A references
    • The shared library C
  • Assembly B references
    • The shared library C
  • Assembly C references
    • Nothing other than the usual

The Shared Client .dll (C)

Two classes are defined in here. The abstract base ViewModel, as mentioned above, and an abstract base MainViewModel class. This latter class defines all the properties that the WPF view will bind to, but contains no other logic.

It also defines the ICommand properties into which behaviour and logic will be placed. This is an example of an ICommand to hook up to a Login button (in MainViewModel.cs):

public ICommand LoginCommand
{
    get
    {
        if (_loginCommand == null)
        {
            _loginCommand = new RelayCommand(param => this.Login(param), param => this.CanLogin(param));
            //_loginCommand = new RelayCommand(param => this.Login(param));
        }
        return _loginCommand;
    }
}
RelayCommand _loginCommand;
protected virtual void Login(object obj) { throw new NotImplementedException(); }
protected virtual bool CanLogin(object obj) { throw new NotImplementedException(); }

The RelayCommand comes straight from Josh Smith's article mentioned above. The key to it are the two virtual methods which will be overridden in the implementation assembly.

N.B. It may be possible to skip this class as I think WPF bindings are loosely coupled and not checked at build time. The XAML for my view simply contains lines like {Binding Path=Username} (for the username textbox) and so it stands to reason that any object instance could be set as the DataContext and binding/reflection will just work where it works and fail where it fails - although don't quote me on that.

I also define delegates for some actions such as closing the window or application which allows me to shift the implementation of that logic to where I know how I want it to work (in the application UI itself).

The Implementation Logic .dll (B)

I call this class MainImplViewModel and it contains an InitializeComponent method which in many ways is the main initializer for the application - remember that the UI assembly (A) is supposed to be 'dumb'.

For my app, it assigns some delegates and default property values. It also does things like checking if the internet is connected and altering property values which can affect whether various UI controls are active as well as loading stuff in from IsolatedStorage.

As mentioned above, I have a reference to the Dispatcher for the UI and so I also define utility methods for marshalling work onto that thread and my application also has a synchronisation process, so there's a private BackgroundWorker variable and event handlers there, too.

And of course, I override the methods that my RelayCommand objects rely on (in MainImplViewModel):

protected override bool CanLogin(object obj) { ... }
protected override void Login(object obj) { ... }

Within which is the actual implementation which can trigger UI changes by modifying the properties, starting my BackgroundWorker and all manner of stuff.

The Application UI XAML .exe

The job of the pared-down application code is to i) check we're online or that the local cache is available (Isolated Storage), ii) download the implementation assembly (B) from a URL and iii) load it.

Once loaded, the types defined in the implementation assembly are scanned and the first class of type MainViewModel that is found is instantiated and assigned to a local variable within the application as well as the DataContext of the main window.

I have written a pretty complex background component loader/initializer class but the main thrust of what its doing is simple; download the file from a website using an ordinary http get, then load the assembly from the raw bytes with Assembly.Load(bytes).

After this is done, it fires an event which continues the ViewModel load process and is handled like so (in app.xaml.cs):

void Initializer_AfterComponentLoaded(object sender, TypeLoadEventArgs e)
{
    if (e.Instance.GetType() == typeof(ResourceDictionary))
    {
        Application.Current.Resources = (ResourceDictionary)e.Instance;

        this.MainWindow = (Window)LoadComponent(new Uri(@"MainWindow.xaml", UriKind.Relative));
        this.MainWindow.Show();
    }
    else if (e.Instance.GetType() == typeof(System.Reflection.Assembly))
    {                
        UI.MainViewModel mainVm;
        if (this.Initializer.AssemblyLoader.TryLoadType<UI.MainViewModel>(out mainVm))
        {
            this.MainWindowViewModel = mainVm;
            this.MainWindow.DataContext = mainVm; // DataContext set to view model.
            
            mainVm.ShutdownApplicationAction = new Action<int>(ShutdownVuplan);
            mainVm.ScrollIntoViewAction = new Action(delegate
            {
                //var listBox = ((Window1)this.MainWindow).MainListBox;
                //if (listBox.Items.Count > 0)
                //    listBox.ScrollIntoView(listBox.Items.GetItemAt(listBox.Items.Count - 1));
            });
            mainVm.Dispatcher = this.MainWindow.Dispatcher;
            mainVm.ParentControl = this.MainWindow;

            this.SessionEnding += new SessionEndingCancelEventHandler(App_SessionEnding);

            mainVm.SyncStatusMessages.Add(Client.UI.StringHelper.TryGetWinFxResourceString("Status_Message_Initializing"));
            mainVm.InitializeComponent(this.CloudStatus); // Warning: Go To Definition will jump to base impl.
        }
        else
        {
            if (this.Initializer.AssemblyLoader.TypeLoadException != null)
                throw this.Initializer.AssemblyLoader.TypeLoadException;
        }
    }
}

I like to leave all my code around so you can get an idea of the other stuff I do. For example, my loader also loads a resource dictionary from the web, based on location – this is a subject for a different blog post, but you can see that I roll my own localization this way. The method above checks what type of object was loaded and if its an Assembly (the implementation assembly) it hooks it up.

What’s most important here is that I set MainWindowViewModel to point to the view model and then set the DataContext of my main window. Further down, I call the all important InitializeComponent and so any state changes to the view model made during this method will be reflected in the view.

To load a type from the implementation assembly I use a generic method TryLoadType<T>

public bool TryLoadType<T>(out T instance) where T : class
{
    this.TypeLoadException = null;
    instance = null;
    try
    {
        if (this.Assembly == null)
            throw new InvalidOperationException(UI.StringHelper.TryGetResourceString("ErrorMessages", "ErrorVitalReferenceIsNull"));

        foreach (Type t in this.Assembly.GetTypes()) // Exceptions here probably due to assembly dependencies of wrong version (remote assembly not latest build).
        {
            if (typeof(T).IsAssignableFrom(t))
            {
                instance = (T)this.Assembly.CreateInstance(t.FullName);
                // this.OnLoaded(new TypeLoadEventArgs(instance));}
            }
        }
    }
    catch (Exception e)
    {
        // Could throw any of 6 exceptions.
        //
        this.TypeLoadException = e;
    }
    return (instance != null);
}

The important bit here is the foreach loop that looks for the type specified in T. MainImplViewModel is derived from MainViewModel so the calling code only need know of the MainViewModel type and it will load the class with all the implementation logic.

The caller (in this case, the method above this one) can also set properties on the view model because it knows of the base type – this is why I have a MainViewModel (recall that I mentioned that in theory it could be excluded).

Conclusion

I've not the time to simplify my example code above, I've just ripped out lumps of code from my projects, thus making it more complicated and excluding things that may have made it more readable, sorry, but I think the general idea can be grasped by most programmers au fait with MVVM.

Using this technique, I can keep my business logic in an implementation assembly, fix bugs in it, and then publish the .dll file to a web server and my clients will be updated invisibly.

Which reminds me: remember to add an XCOPY command to the post build actions for the implementation DLL which copies the assembly to your web server. Easily forgotton - you'll wonder why your breakpoints aren't being hit.

Labels: , ,

Dragging a WPF Window Around

Wednesday, December 02, 2009 / Posted by Luke Puplett / comments (0)

This short article shows how to drag a WPF application window around your desktop when you have removed the stock Microsoft Windows chrome.

Applications using WPF and having a particularly stylistic look and feel often don’t sit well with the standard Windows chrome supplied by the operating system. It’s easy to remove this chrome, but once it’s gone your users lose the ability to move your application window around.

Here’s how to hook up a region (in this case, a grid) to serve as a handle so as long as the mouse is held down and dragging around, the window moves with it.

First, hook up a single MouseLeftButtonDown handler in your XAML on the element you wish to use.

<Grid Grid.Row="0" MouseLeftButtonDown="Grid_MouseLeftButtonDown" >

The codebehind file for the Window looks like this – see notes at the bottom.

    public partial class Window1 : Window
    {
        private System.Drawing.Point _windowMoveMouseStart;
        private double _windowMoveStartTop;
        private double _windowMoveStartLeft;

        public Window1()
        {
            this.InitializeComponent();

            // Insert code required on object creation below this point.
        }

        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            UIElement handle = sender as UIElement;
            if (handle == null)
                return;

            _windowMoveMouseStart = System.Windows.Forms.Control.MousePosition;
            _windowMoveStartTop = this.Top;
            _windowMoveStartLeft = this.Left;

            handle.MouseMove += Handle_MouseMove;
            handle.CaptureMouse();
        }

        void Handle_MouseMove(object sender, MouseEventArgs e)
        {
            UIElement handle = sender as UIElement;
            if (handle == null)
                return;

            if (e.LeftButton == MouseButtonState.Released)
            {
                handle.MouseMove -= Handle_MouseMove; // Detach listener on mouse up.
                handle.ReleaseMouseCapture();
            }
            else
            {
                var smp = System.Windows.Forms.Control.MousePosition;
                var distanceX = smp.X - _windowMoveMouseStart.X;
                var distanceY = smp.Y - _windowMoveMouseStart.Y;

                this.Left = _windowMoveStartLeft + distanceX;
                this.Top = _windowMoveStartTop + distanceY;
            } 
        }
    }

At the top is a System.Drawing.Point which means you need to add a reference to System.Drawing as well as System.Windows.Forms to get access to the Control.MousePosition static property – unless you know of a better way to get at non-relative mouse data.

CaptureMouse

Key to this is the UIElement.CaptureMouse() method which sets the eventing system to continue firing even when the mouse is outside the bounds of the element. Without this the mouse can ‘'skid off’ the handle when its moved too fast – too fast being quite slow actually.

If a mouse move is captured and the button isn’t down, we disconnect all the mechanics previously setup. Since the mouse move should always be captured, even when its not over the original handle, the mechanics should always get detached.

Windows 7 Aero Shake

Not sure if this will then trigger the Windows 7 shake feature...

Labels: , , ,

Linq to Visual Tree

Friday, November 20, 2009 / Posted by Luke Puplett / comments (0)

Over on Peter McGrattan’s blog is a simple extension method for a DependencyObject which returns the visual tree descending from a particular visual element in a WPF application. The genius in Peter’s simple extension is that it automatically leverages the power of LINQ because the tree is IEnumerable:

myPanel.GetVisualOfType<TextBox>().Where(t => t.Foreground... etc

But I had a problem: the tree would not descend fully.

To cut a long story short, the issue was caused because VisualTreeHelper.GetChildrenCount doesn’t count the content of a ContentControl as a child.

The following is my version of Peter’s good work – this is just one method cut from the original class, so I implore you to head over to his blog and see the whole thing. I have made the following changes:

  • Added support for ContentControl objects.
  • Modified the type checking to use IsAssignableFrom so types can be collated using their base classes.
public static IEnumerable<T> GetVisualOfType<T>(this DependencyObject element)         

    return GetVisualTree(element).Where(
        t => typeof(T).IsAssignableFrom(t.GetType())).Cast<T>();
}
public static IEnumerable<DependencyObject> GetVisualTree(this DependencyObject element)
{
    int childrenCount = VisualTreeHelper.GetChildrenCount(element);

    if ((childrenCount == 0) && (element is ContentControl))
    { 
        ContentControl cc = element as ContentControl;
        if (cc.Content != null)
        {
            DependencyObject content = cc.Content as DependencyObject;

            if (content != null)
            {
                yield return content;
                foreach (DependencyObject obj in GetVisualTree(content))
                    yield return obj;
            }
        }
    }

    for (int i = 0; i < childrenCount; i++)
    {
        var visualChild = VisualTreeHelper.GetChild(element, i);
        yield return visualChild;
        foreach (var visualChildren in GetVisualTree(visualChild))
        {
            yield return visualChildren;
        }
    }
}

Labels: , ,