Tuesday, 24 April 2007

Silverlight big announcement at MIX07?

I was reading the wpfbox blog about the MIX07 announcement regarding Silverlight (the new name for WPF/E). Well if this is the one you're talking about, we've known for some time that wpf/e would include a derivative of the micro CLR that already ran on the short lived SPOT devices, and a lot of work has gone through enabling CLS compliant languages to run on an embbed version of the CLR. My best bet is that compiled IL will be presented at MIX07, together with a beta. Maybe there will even be full interop between IL code and javascript code, letting you call objects coded in C# using javascript.

Wednesday, 11 April 2007

Multiple default buttons the WPF way

Not from me, but from Neil, my future ex-colleague at Netstore: “Default” buttons in WPF and multiple default buttons per page.

Monday, 9 April 2007

WPF Tips'n'Tricks #4: Another way to declare read-only dependency properties

Sorry for the hiatus this week-end, I spent a lovely time out of London, disconnected from the online world. Back online (in the train, with WIFI, fantastic, check it out).

Read-only dependency properties (attached or not) are declared by a call to RegisterReadOnly, which returns a DependencyPropertyKey type, which you have to use when setting values. From that object you get a reference to the DependencyPropert object that is used to read values. Let's look at a typical dependency property registration.

public class Div : Control

{

private static DependencyProperty LeftProperty;

private static DependencyPropertyKey LeftPropertyKey;

static Div()

{

Div.LeftPropertyKey = DependencyProperty.RegisterReadOnly(

"Left",

typeof(double),

typeof(Div),

new FrameworkPropertyMetadata(0d));

Div.LeftProperty = Div.LeftPropertyKey.DependencyProperty;

}

Traditionally, you would then define a property with only a getter. But one of the not so known new features of C# 2.0 is the ability to declare different access modifiers for the getter and the setter of a property. We'll use it to our advantage to declare a property getter but have a private setter using the Key, keeping our object model clean and nifty.

public double Left

{

get { return (double)GetValue(Div.LeftProperty); }

private set { SetValue(Div.LeftPropertyKey, value); }

}

And voila, a nice and clean way to set your read-only properties without calls to SetValue all over the place.

[Edit: Added the private as I forgot it. Thanks for correcting me! ]

Thursday, 5 April 2007

WPF Bug, TextBlock with empty element

A note for me as much as for everybody else (while waiting for the Connect website to be updated to let us fill RTM bugs).

Having a TextBlock containing an element that has no size ends up with an ArgumentOutOfRange exception. Tsk tsk tsk.

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

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

    <TextBlock>

        <Hyperlink />

    </TextBlock>

</Page>

WPF Tips'n'Tricks #3: Reusing the content of a Popup control

That one is far from obvious, and is what I'd classify as a bug. As soon as Microsoft fix the Connect web-site to let us report RTM bugs, I'll more than happily fill one up.

In WPF, controls can only have one parent at a time. Because rendering is done top down and every control has a sequence of Measure/Arrange calls to define the layout of the windows, it makes perfect sense.

It also makes perfect sense to be able to remove a control from somewhere (let's say a Button from a Panel) and re-add it somewhere else. Quick and dirty example, a button switching between two panels.

<Window x:Class="PopupExample.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:my="clr-namespace:IsDirtyExample"

    Title="IsDirtyExample" SizeToContent="WidthAndHeight">

    <DockPanel LastChildFill="False">

        <DockPanel DockPanel.Dock="Top" Name="FirstGrid" Background="Orange" Width="250" Height="200">

            <Button Name="OneButtonToRuleThemAll">Ding</Button>

        </DockPanel>

        <DockPanel DockPanel.Dock="Top" Name="SecondGrid" Background="OrangeRed" Width="250" Height="200"></DockPanel>

        <Button DockPanel.Dock="Top" Click="HandleMoveClick">Move the Ding!</Button>

    </DockPanel>

</Window>

namespace PopupExample

{

    public partial class Window1 : System.Windows.Window

    {

        public Window1()

        {

            InitializeComponent();

        }

        public void HandleMoveClick(object source, RoutedEventArgs e)

        {

            FirstGrid.Children.Remove(OneButtonToRuleThemAll);

            SecondGrid.Children.Add(OneButtonToRuleThemAll);

        }

    }

}

When you create a Popup control however, it only has a Child property. So you'd assume you would just change the Child property to a new control, and that new control would then be showing next time you open the Popup, and the old control wouldn't be in any visual tree anymore. Well, it doesn't always happen that way at all.

If your Popup is visible, then you'll see the change. If the Popup is not visible anymore, and you set it's Child property to null, and reattach your control to a new Popup, you'll be greeted by a very useful message.

Must disconnect specified child from current parent Visual before attaching to new parent Visual.

But wait, the control is not assigned to the Child property anymore? It is and it is not. Whenever the popup gets created, it keeps a separate object deep within called a PopupRoot. That one gets created once and has as a child the control pointed by the Popup's Child property. But whenever a popup is closed (IsOpened=false), changes to its Child property will not impact the PopupRoot that still has a reference to the previous value. Hence why when you try to add your control somewhere else, you have an exception

The solution? Put as the first child of your popup a neutral panel (Grid for example), and move your controls around only as a child of that grid. Full code sample shown below.

namespace PopupExample

{

    public partial class Window1 : System.Windows.Window

    {

        public Window1()

        {

            InitializeComponent();

        }

        private void button_Click(object sender, RoutedEventArgs e)

        {

            if (popupButton == null)

            {

                popupButton = new Button();

                popupButton.Content = "This button on a popup";

            }

 

            Popup popup = new Popup();

            Grid grid = new Grid();

            grid.Children.Add(popupButton);

            popup.Child = grid;

            popup.PlacementTarget = this;

            popup.PlacementRectangle = new Rect(0, 0, this.ActualWidth, this.ActualHeight - 18);

            popup.Placement = PlacementMode.Bottom;

            popup.StaysOpen = false;

            popup.IsOpen = true// Second call used to trigger an exception

            popup.Closed += new EventHandler(popup_Closed);

 

        }

 

        private void popup_Closed(object sender, EventArgs e)

        {

            Popup popup = (Popup)sender;

            ((Grid)popup.Child).Children.Clear(); // will finally clear the control from being in a visual tree

            popup.Closed -= new EventHandler(popup_Closed);

 

        }

    }

}

 

Wednesday, 4 April 2007

WPF Tips'n'Tricks #2: Use Segoe UI on Vista and Tahoma on XP (and whatever else wherever else)

A question that's often asked is how to make it so that your elements in WPF use the latest greatest fonts on Windows Vista, but fallback nicely on Windows XP.

As with CSS, WPF supports font fallback. That means you can define a font to use if present on the target system, and a second one to use if a first one is not found, as in the following example:

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

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

    <StackPanel>

        <TextBlock FontFamily="Segoe UI, Verdana" FontSize="20">Using Fallback</TextBlock>

    </StackPanel>

</Page>

This will show Segoe UI, followed by Verdana. That said, WPF supports composite fonts, which are essentially virtual fonts that redirect each portion of Unicode (symbols, asian text, Greek text, Latin text, ASCII, you get the drill) to the correct font. It replaces the way font substitution is done in Win32. And you have the following fonts you can use:

  • GlobalMonospace.CompositeFont
    Monospaced, for example used to show snippets of code.
  • GlobalSanSerif.CompositeFont
    SanSerif, so without the heavy bits that decorate a font. Arial, Segoe UI and Tahoma are part of that family.
  • GlobalSerif.CompositeFont
    With, Con, Mit Serif (useless Eddie Izzard Reference), for those that want the little decoration. Times New Roman is the most well known
  • GlobalUserInterface.CompositeFont
    The one used for user-interface elements.

You can go and have a look at these files in your %windir%\fonts folder. You'll see each mapping and each code point.

So to answer the question, if you want Segoe UI and a fallback on Tahoma on XP, you can either use the composite font like this:

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

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

    <StackPanel>

        <TextBlock FontFamily="Global User Interface" FontSize="20">Using Composite font</TextBlock>

    </StackPanel>

</Page>

 Or more simply do nothing and do not specify a font, it will default to the right one. But as WPF is multi-platform, my best advice: Define the fonts you use in a resource, and try your app on both Segoe and Tahoma, just to have a feel of what your app looks like under XP.

[Thanks to Mikhail Leonov for the pointer to composite fonts on the forum]

Tuesday, 3 April 2007

WPF Tips'n'Tricks #1: Have all your dates, times, numbers... in the local culture

This is the first instance of a series where I'll try to publish at least 

Technorati tags: , ,

one trick a day you'll find useful in your .net 3 development life. Some of it I came up with myself, some of it comes from the forums. I'll make sure to give credit where credit is due :)

WPF has a very annoying tendency. All dates are by default in the en-US format. You need to understand first why, and then I'll give a few potential solutions, and one that is now my favorite.

You'll notice that UIElement has a property called Language. This property is supposed to define what language the element has been written in. That way, WPF can know when some content is en-US (American English), or en-GB (British English), etc. It is also bound to the xml:lang property you can set on any xml document.

Now if you read the documentation the way I did, you realize that xml:lang by *default* is set to the empty string, and as such doesn't have any associated culture. But by default, the matching Language property has a default of... en-US! And because of the way bindings work, that's why all your dates will always show up in American English format.

Whenever you bind an element's property to a DateTime object, the binding is going to covert the DateTime to the type of the property you're binding to. Take for example the following binding:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <Grid>

        <TextBlock Text="{Binding Path={x:Static sys:DateTime.Now}}" />

    </Grid>

</Window>

This will convert from DateTime to a String (the type of the Text property). But to do so, it is going to use a default converter. If you check the documentation for bindings, you'll see that you can specify an IValueConverter in the Converter property. And you'll also see a ConverterCulture property that specify which culture you want to use to convert your data, here our DateTime object.

As I said before, without a Converter specified, WPF will find one automatically for you, either built-in ones or using the existing conversion infrastructure that has been around for now three versions. But guess what happens when you don't specify a ConverterCulture?

WPF selects the culture of the Language property of the element on which the binding is applied. In our case, the default: en-US again!

So here's my trick of the day. To ensure your application defaults to the current culture *on the client machine* and *at run time*, you can add one simple line in your App.xaml.cs.

    public partial class App : Application

    {

        static App()

        {

            FrameworkElement.LanguageProperty.OverrideMetadata(

                typeof(FrameworkElement),

                new FrameworkPropertyMetadata(

                    XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));

        }

    }

What this bit of code does is override the default value of the Language property on all the FrameworkElement inherited classes in your application to the CurrentCulture of the computer you run on. In this case, we're interested in overriding this value so that any content is assumed by default to be in the language associated with the way you want your localization to be made. This is quite wrong, as explained by Michael in his Why we have both CurrentCulture and CurrentUICulture post, but it's the solution approaching the most an acceptable outcome.

As for what Microsoft probably should have done? Set by default the Binding.ConverterCulture property to the CultureInfo.CurrentCulture value. But that's just my opinion :)