Monday, 3 September 2007

WPF Tips'n'Tricks #6: Preventing ScrollViewer from handling the mouse wheel

In the category of the pot talking to the pan, I present you ScrollViewer. It's the main control to implement scrolling in your templates, but it's also the one not respecting a very  fundamental rule of scrolling: if you're done scrolling, let your parent scroll!

Not only does ScrollViewer handles the mouse scrolling even when no more scrolling is needed, but it also does so when there's nothing to scroll, or worse when it is told not to scroll! Let's take an example XAML file. 

<Window x:Class="CaffeineIT.Blog.ScrollViewerExample.Window1"

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

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

   Title="Window1" Height="423" Width="596">

    <Grid>

        <ScrollViewer>

            <StackPanel>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <ScrollViewer Name="NoScrollingScrollViewer">

                    <TextBlock>Content that doesn't need scrolling</TextBlock>

                </ScrollViewer>

                <ScrollViewer Height="235" Name="ScrollingNeededScrollViewer">

                    <StackPanel>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <ListView>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                            <TextBlock>Inside Thrid ListView</TextBlock>

                        </ListView>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                        <TextBlock>Inside Second ScrollViewer</TextBlock>

                    </StackPanel>

                </ScrollViewer>

 

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

                <TextBlock>Inside First ScrollViewer</TextBlock>

            </StackPanel>

        </ScrollViewer>

    </Grid>

</Window>

How can we change the ScrollViewer to behave more like it's supposed to? The most direct approach is to leverage the tunneling and bubbling events and use them against the buggy control.

The idea is that if the PreviewMouseWheel is handled, WPF will not generate the MouseWheel event, and in turn the ScrollViewer will not scroll.

Let's add a handler for the PreviewMouseWheel event on one of our ScrollViewers.

<ScrollViewer Height="235" Name="ScrollingNeededScrollViewer" PreviewMouseWheel="HandlePreviewMouseWheel">

 

        private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)

        {

            if (sender is ScrollViewer && !e.Handled)

            {

                e.Handled = true;

                var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);

                eventArg.RoutedEvent = UIElement.MouseWheelEvent;

                eventArg.Source = sender;

                var parent = ((Control)sender).Parent as UIElement;

                parent.RaiseEvent(eventArg);

            }

        }

This does exactly what we want. It marks the tunneling PreviewMouseWheel event as handled, so as to prevent WPF from raising the bubbling MouseWheel event, which is the one causing the actual scrolling. This is fine in case you don't want a ScrollViewer to scroll at all and let its parent do the scrolling, but what if you only want your ScrollViewer to scroll until it cannot anymore, and then let the parent scroll (this is the behavior in Internet Explorer)? Let's tweak the code a bit.

        private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)

        {

            var scrollControl = sender as ScrollViewer;

            if (!e.Handled && sender != null)

            {

 

                bool cancelScrolling = false;

 

                if ((e.Delta > 0 && scrollControl.VerticalOffset == 0)

                    || (e.Delta <= 0 && scrollControl.VerticalOffset >= scrollControl.ExtentHeight - scrollControl.ViewportHeight))

                {

                    e.Handled = true;

                    var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);

                    eventArg.RoutedEvent = UIElement.MouseWheelEvent;

                    eventArg.Source = sender;

                    var parent = ((Control)sender).Parent as UIElement;

                    parent.RaiseEvent(eventArg);

                }

            }

        }

Now, we check on every mouse wheel scroll if any content needs scrolling in the direction the wheel was scrolled. We check the VerticalOffset property, as it is 0 when you can't scroll up anymore and ExtentHeight-ViewportHeight when you can't scroll down anymore. If there's nothing to scroll, we cancel the event and re-raise it just like we did before.

That's all well so far, but what if I have another child ScrollViewer, like the ListView in our example? The ListView will not receive any notifications if the parent ScrollViewer is scrolled to the max in either direction, because we stop the PreviewMouseWheel before it can reach the ListView. We need to change the code a bit more and do the work the framework would've done.

        private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)

        {

            var scrollControl = sender as ScrollViewer;

            if (!e.Handled && sender != null && !_reentrantList.Contains(e))

            {

                var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)

                {

                    RoutedEvent = UIElement.PreviewMouseWheelEvent,

                    Source = sender

                };

                var originalSource = e.OriginalSource as UIElement;

                _reentrantList.Add(previewEventArg);

                originalSource.RaiseEvent(previewEventArg);

                _reentrantList.Remove(previewEventArg);

                // at this point if no one else handled the event in our children, we do our job

 

 

                if (!previewEventArg.Handled && ((e.Delta > 0 && scrollControl.VerticalOffset == 0)

                    || (e.Delta <= 0 && scrollControl.VerticalOffset >= scrollControl.ExtentHeight - scrollControl.ViewportHeight)))

                {

                    e.Handled = true;

                    var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);

                    eventArg.RoutedEvent = UIElement.MouseWheelEvent;

                    eventArg.Source = sender;

                    var parent = ((Control)sender).Parent as UIElement;

                    parent.RaiseEvent(eventArg);

                }

            }

        }

The main difference is that before we try to cancel the PreviewMouseWheel event by marking it Handled, we check if any child of the control would mark it Handled before us, which by WPF design would mean we shouldn't handle the event at all.

If you try this example now, you'll notice now that our ListView still prevents the scrolling to happen properly. That's because we only changed the behavior of the ScrollViewer we attached an event handler to, and not the one inside the ListView. Using the attached property initialization hack we used before, we can define an attached property that will do all the hookup work whenever attached to a ScrollViewer.

    public class ScrollViewerCorrector

    {

 

 

        public static bool GetFixScrolling(DependencyObject obj)

        {

            return (bool)obj.GetValue(FixScrollingProperty);

        }

 

        public static void SetFixScrolling(DependencyObject obj, bool value)

        {

            obj.SetValue(FixScrollingProperty, value);

        }

 

        public static readonly DependencyProperty FixScrollingProperty =

            DependencyProperty.RegisterAttached("FixScrolling", typeof(bool), typeof(ScrollViewerCorrector), new FrameworkPropertyMetadata(false,ScrollViewerCorrector.OnFixScrollingPropertyChanged));

 

        public static void OnFixScrollingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)

        {

            ScrollViewer viewer = sender as ScrollViewer;

            if (viewer == null)

                throw new ArgumentException("The dependency property can only be attached to a ScrollViewer", "sender");

 

            if ((bool)e.NewValue == true)

                viewer.PreviewMouseWheel += HandlePreviewMouseWheel;

            else if ((bool)e.NewValue == false)

                viewer.PreviewMouseWheel -= HandlePreviewMouseWheel;

        }

        private static List<MouseWheelEventArgs> _reentrantList = new List<MouseWheelEventArgs>();

        private static void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)

        {

            var scrollControl = sender as ScrollViewer;

            if (!e.Handled && sender != null && !_reentrantList.Contains(e))

            {

                var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)

                {

                    RoutedEvent = UIElement.PreviewMouseWheelEvent,

                    Source = sender

                };

                var originalSource = e.OriginalSource as UIElement;

                _reentrantList.Add(previewEventArg);

                originalSource.RaiseEvent(previewEventArg);

                _reentrantList.Remove(previewEventArg);

                // at this point if no one else handled the event in our children, we do our job

 

 

                if (!previewEventArg.Handled && ((e.Delta > 0 && scrollControl.VerticalOffset == 0)

                    || (e.Delta <= 0 && scrollControl.VerticalOffset >= scrollControl.ExtentHeight - scrollControl.ViewportHeight)))

                {

                    e.Handled = true;

                    var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);

                    eventArg.RoutedEvent = UIElement.MouseWheelEvent;

                    eventArg.Source = sender;

                    var parent = (UIElement)((FrameworkElement)sender).Parent;

                    parent.RaiseEvent(eventArg);

                }

            }

        }

    }

And the only thing left to do is to change the template for ScrollViewer to always define the attached property by adding the Style to the resources on the Window, and pronto, all your ScrollViewers are now behaving properly.

<Window x:Class="CaffeineIT.Blog.ScrollViewerExample.Window1"

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

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

   Title="Window1" Height="423" Width="596" xmlns:my="clr-namespace:CaffeineIT.Blog.ScrollViewerExample">

    <Window.Resources>

        <Style TargetType="{x:Type ScrollViewer}">

            <Style.Setters>

                <Setter Property="my:ScrollViewerCorrector.FixScrolling" Value="True" />

            </Style.Setters>

        </Style>

    </Window.Resources>

As usual, you can download the ScrollViewerFixer.zip code and sample.

Friday, 31 August 2007

Google adSense

After having adSense for months (and a total earning of $0.18), the ads are finally showing up something connected with my blog:

  • DataGrid controls from Infragistics
  • The power of .net 3.0 from Xceed
  • Debugging Visual Studio from Microsoft
  • Automating your page composition from Dakota... Uh?

Three out of four is not bad, and it's three companies any .net 3 developer should visit quite often for the latest news, so the ads are in this instance quite useful.

As for automating my page... This is a blog?!? Oh well.

Technorati Tags: , ,

Thursday, 30 August 2007

Programming WPF 2nd Edition is out

Well through Chris Sell's blog, http://www.sellsbrothers.com/news/showTopic.aspx?ixTopic=2122, the new edition is shipping. I liked the previous version, only real reference at the time I started WPF, together with the Petzold.

So I just placed an order for it, and at the same time ordered Nathan's and the other Chris book, so I can have a go at reading them all.

I'll do a comparison of all of them and report here. It's been done before but now that I've been working professionally for what seems like years but is in fact only a year (between the RTM and beta 2), I think I may see things differently.

It will also be fun to compare the first and the 2nd edition of Programming WPF :)

Question is, should I order the 3D Petzold? We'll see after I'm done with the rest.

Anyone wants to commission me to write "Writing controls for WPF: Put some sparkle in your cider!"?

Technorati Tags: , , , ,

Wednesday, 22 August 2007

Funny comment of the day

While coding, found a snippet one of my predecessors left me:

//  Media Player gets its

// knickers in a twist.

Thought it was hilarious!

Friday, 17 August 2007

Using Visual Studio 2008 and Scrum for Team System

If you're using Scrum for Team System and Visual Studio 2008, you may notice that when connecting to a newly created Scrum Team Project, the Work Items node gets a desperate looking red cross.

The solution is to simply connect to TFS using the 2005 Team Explorer. From there, create a new Release. Restart Visual Studio 2008 and your Team Queries will finally show up!

Thursday, 16 August 2007

Xceed DataGrid for WPF v1.2

 We've used the WPF DataGrid successfully on the National Express project, and the guys at Xceed are very prompt to answer questions and very quick to fix bugs we encountered with the first version of their products.

I'm being told it's now top quality and probably the best DataGrid available. And it's just been updated! Check their News Headlines page for the new features.

Technorati Tags: , , , ,

Wednesday, 15 August 2007

Some TFS links

Not the freshest of links but useful if you use TFS.

TFS Bug Snapper v1.0 Released

and

www.scrumforteamsystem.com that provides similar functionality to the eScrum tool I talked about before.

Tuesday, 14 August 2007

Quick note for testers

A quick link to Anutthara's blog because it's an invaluable resource for any tester. I'll probably enforce reading it to any tester I have to work with.

Install windows components on a locked-down machine

More and more companies set their users as non admin, even with XP. The so-called locking down tries to protect the user and the network against anything only an administrator should do.

But there's also group policy that directs what gets shown in windows, which software gets activated, etc. In my case, the policy doesn't let you access the Add/Remove Windows Components section.

Being an administrator on the machine, I could go and change the registry to deny read permission to whichever account is being used for policies. That's a bit drastic and I wouldn't recommend it.

But thanks to the complexity and myriad of options available to group policy, sometimes you can get away with easier things.

To add or remove windows components, open PowerShell and type the following.

PS C:\WINDOWS> sysocmgr /i:$env:windir\inf\sysoc.inf

Or for people still using cmd

C:\WINDOWS> sysocmgr /i:%WINDIR%\inf\sysoc.inf

I'd rather we didn't have to play catch and seek with network administrators, but you have to do what you have to do to get the job done sometimes.

Technorati Tags: , ,

Thursday, 9 August 2007

Mix:UK 07 - I'll be there!

Well, it's now official, I'll be at Mix:Uk 07!

Hope to get chatting with many of you about all things WPF / Silverlight. So much cross-over and so many differences!

DirectShow filters from MediaElement

I previously pointed to Jeremiah blog about his and Leslie's amazing win32 integration work.

Little did I realize that the trick I read a while ago to build your own DirectShow filter for a MediaElement control was also his!

Respect.

Wednesday, 8 August 2007

On why you shouldn't really subclass through XAML

As I am reviewing some code at the moment, I have been looking for this blog entry from Rob for a while, and just found it again: Building a control which holds content: CustomControl vs. MarkupSubclassing vs. UserControl. Included in my specification references, but posting it so I can reuse it as a reference.

And the ever so useful Neil (when are you getting a blog back?) points me to an article by Kevin on the same subject: Don't subclass a Panel, unless you're making a Panel.

It always amazes me that people, after x years of OOD, still don't see a problem in using inheritance instead of combination / encapsulation.

Then again these guidelines postdate most of the uses I've seen so far so you can't really blame anyone.

Technorati Tags: , , ,

Tuesday, 7 August 2007

Documentation != Help, or why visual studio sucks

Visual Studio 2005 has the terrible habit of opening the full msdn library whenever you press F1, which in my case is mostly accidental. Not that I don't need help, but the F1 is a reflex I use when I don't understand the meaning of an option on the UI. I couldn't care less for msdn at that point.

Visual Studio 2008 seems to have a faster <cough> <cough> documentation explorer, but goes into what I'd consider to be pure vice: The little question mark box next to the close button is for bloody contextual help. Guess what, opening your big document explorer in my face when I'm already trying to understand what in the name of god you meant by allow checked-in items to be edited is not contextual. I loose my patience, and definitely loose my respect for whichever manager decided that after all, contextual help could be in the big documentation.

Can I have my tooltips back? Please?

Thursday, 2 August 2007

WPF Tips'n'Tricks #5: Receive notifications for dependency properties

Receiving notifications for dependency property changes on an existing object is a very common scenario. The way to do it properly is not very obvious. So much so that while reviewing some code, I found the following snippet.

// Believe it or not, this seems to be the only way to get change
// notifications for DPs unless you derive from the relevant
// class and override OnPropertyChanged.

PropertyDescriptor prop = TypeDescriptor.GetProperties(obj)["Prop"];
prop.AddValueChanged(obj, delegate { viewModel.RaisePropertyChanged("Prop"); });

There's a few issues with this code. The first one is that you reflect on the CLR property anchoring the dependency property, and not the dependency property itself. For example, the following code wouldn't work.

public Dock Dock { get { return DockPanel.GetDock(this); } }
public void TestDockProperty()
{
    PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)["Dock"];
    descriptor.AddValueChanged(this, delegate(object sender, EventArgs args) { MessageBox.Show("ValueChanged!"); });

    DockPanel.SetDock(this, Dock.Top);
}

The reason is that the PropertyDescriptor points to the CLR property, not to the dependency property.

The second issue is a problem of performance. TypeDescriptor.GetProperties reflects on every call and doesn't cache the result, so its cost is O(n). Here's the result of iterating several times on the code using TypeDescriptor.

  • 1,000 iterations : 00:00:00.2811402
  • 10,000 iterations : 00:00:01.4369388
  • 100,000 iterations : 00:00:14.1194856

So what is the correct way to do it? Say hi to DependencyTypeDescriptor. Here's the code rewritten to use DependencyProperties.

DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ParentObject.PropProperty, obj.GetType());
            prop.AddValueChanged(obj, delegate { viewModel.RaisePropertyChanged("Prop"); });

If you execute the code in the small benchmark application we used previously, the results are completely different.

  • 1,000 iterations : 00:00:00.00
  • 10,000 iterations : 00:00:00.00
  • 100,000 iterations : 00:00:00.0312378

As you can see in the source code, I simply use a DateTime before and after the call, and everything runs on the UI thread.

And the source is stored on box.net for those that want a peek. Be aware it's a visual Studio 2008 solution and project.

Download the source.

Tuesday, 31 July 2007

More new WPF 3.5 things

Neil passed me some of these URLs in my comments and i'm all too happy to republish them.

Technorati Tags: , , , ,

Monday, 30 July 2007

WPF / Control interop, the blended way...

More of a bookmark for later, but seems Leslie and Jeremiah are doing fancy stuff with win32 controls and getting rid of the Hwnd limitation. Well done!

Edit: Leslie gave the link to the source code in the comments, so here it is. Thanks Leslie! http://www.codeplex.com/WPFWin32Renderer/Release

Technorati Tags: , , , , ,

A Bit of WPF love. No, Bits of it!

Tim Sneath posted an answer to the question I asked on friday (well, posted something that answered my question, probably without him being aware of it in the first place):

Most of the performance improvements and some of the feature improvements will also be included in a forthcoming service pack for .NET Framework 3.0 - I don't think we've talked externally about delivery mechanisms for this at this stage, however.

Good news! So I was right, there is a service pack in the pipeline!

Friday, 27 July 2007

So there *are* new things for WPF in 3.5

Visual Studio 2008 beta 2 has just been released, and with it an announcement of new features and bug fixes for WPF. And the best bit for me:

Data binding and journaling by URI work together.

For the WPF application we released to National Express with Netstore, this bug hit us very hard, as we designed a whole navigation system based on passing context / data objects around while keeping navigation by URL (memory footprint being the main reason why you want to navigate by URL.) What used to happen was that any Binding associated with a dependency property that supported Journaling would not be re-established. My good friend Neil Mosafi (seems to not have a blog anymore) was the one that found the bug.

Question for Microsoft though, is there going to be a HotFix for the issue? Some applications in the wild would definitely benefit from having a fix, rather than rely on inheriting every control and overriding each journaled dependency property manually.

As I'm writing this entry, my mind wanders and hope that one day WPF applications will work more like the browser, keeping a page as KeepAlive for a known number of pages or memory footprint, and only release a page when the history stack takes up too much memory. Maybe in .net 4 with WPF (which would be wpf 2.0, if 3.5 is WPF 1.5. Interesting arithmetic, WPF = .net - 2).

Oh the thrill. I'll be starting cooking some WPF examples next week as soon as I switch back to booting Vista instead of MacOS X (have a look at VMWare Fusion. Now if they could integrate their Unity feature with the vista DWM, I could get accelerated graphics WPF development in visual studio within macos. Hmmm...)

Technorati Tags: , ,

Thursday, 19 July 2007

The future soon

After a nice time at AKQA, I'm off in a week time to a big media company for some super duper secret WPF project I obviously won't talk about. But that means WPF content is going to re-surface en force. My WPF tips'n'tricks need to be followed on.

And thanks to the comments and emails I've received from some of the people reading me, if I've been of any help then it's all worth it.

Oh, and I bought a new high resolution MacBook Pro, fantastic Vista development machine! This one is insured though.

Wednesday, 11 July 2007

PowerShell arguments and encodings

Well, after fighting for an hour or two, I finally read a post explaining why my quotes were not passed around when invoking a script. Now that I'm using the -EncodedCommand attribute, everything works fine!

As the guys on the powershell team wrote about encoding conversion, I thought I'd provide two quick ones for those needing it, Url Encoding and Base64. Don't hesitate to add to your profile.

Note that I'm using LoadWithPartialName because I'm lazy and it still works on .net 2. Replace with proper LoadFrom or Load as needed.

[System.Reflection.Assembly]::LoadWithPartialName("System.Web") | out-null
function ConvertTo-UrlEncodedString([string]$dataToConvert)
{
    begin {
        function EncodeCore([string]$data) { return [System.Web.HttpUtility]::UrlEncode($data) }
    }
    process { if ($_ -as [string]) { EncodeCore($_) } }
    end { if ($dataToConvert) { EncodeCore($dataToConvert) } }
}
function ConvertFrom-UrlEncodedString([string]$dataToConvert)
{
    begin {
        function DecodeCore([string]$data) { return [System.Web.HttpUtility]::UrlDecode($data) }
    }
    process { if ($_ -as [string]) { DecodeCore($_) } }
    end { if ($dataToConvert) { DecodeCore($dataToConvert) } }
}
function ConvertTo-Base64EncodedString([string]$dataToConvert)
{
    begin {
        function EncodeCore([string]$data) { return [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($data)) }
    }
    process { if ($_ -as [string]) { EncodeCore($_) } }
    end { if ($dataToConvert) { EncodeCore($dataToConvert) } }
}
function ConvertFrom-Base64EncodedString([string]$dataToConvert)
{
    begin {
        function DecodeCore([string]$data) { return [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($data)) }
    }
    process { if ($_ -as [string]) { DecodeCore($_) } }
    end { if ($dataToConvert) { DecodeCore($dataToConvert) } }
}

 

Technorati Tags: , , ,

Friday, 29 June 2007

WPF Attached Events Addendum

Some of you may remember my article on attached events. There's now more clarification from the WPF SDK blog. Considering the extensive email exchange I had with Wolf Schmidt about the topic and its coverage in the SDK, I wouldn't be surprised if he had something to do with that article. An excerpt from our conversation is in order.

Now that I've spent so much time on an email to one individual customer, I think I smell a WPF SDK blog entry coming :-)

The point is, the MSDN people are very accessible and very helpful, and answer with passion and accuracy. And they hang on the WPF forum as well so don't hesitate to drop your questions there.

Technorati Tags: , ,

Service Pack on the way?

A while ago, Rob Relyea mentioned on the forums that they had someone working on community and bugs in the WPF team.

I also remember reading a few days ago a Microsoft blogger talking about a service pack.

And Neil has pointed out that bugs are starting to get closed down on the Connect site.

And now, another proof, in the form of a link list:

Service pack may you come. And if other bugs could be fixed, including the dependency property journaling and persistence counter for user controls, that would be nice Microsoft :)

 

Technorati Tags: , , ,

Wednesday, 27 June 2007

Multiple mice and cursors on WPF applications

For those that want to do WPF development with multiple cursors, Microsoft released a new version of the MultiPoint SDK. 

Download details: Microsoft Windows MultiPoint Software Development Kit (SDK)

Technorati Tags:

Knowing which aliases are defined

[Update: Replaced -eq by -match as suggested on the PowerShell blog. Thanks guys, humbling to have you reading this blog. Tagging really does have a use :) ]

One of the main struggles I have when writing my PowerShell scripts for public consumption is that I never remember which aliases I can use. And eventually I'd like to naturally type gci instead of ls, but old habits die hard.

Here's a little function I have in my profile to get the list of all aliases defined for a command.

function Get-AliasShortcut([string]$CommandName) {
    ls Alias: | ?{ $_.Definition -match $CommandName }
}
Set-Alias gas Get-AliasShortcut

Which you can then execute as such:

6# gas get-childitem

CommandType     Name                                                Definition
-----------     ----                                                ----------
Alias           gci                                                 Get-ChildItem
Alias           ls                                                  Get-ChildItem
Alias           dir                                                 Get-ChildItem

Technorati Tags:

Tuesday, 26 June 2007

Stupid word of the day

Heard in a corridor.

This has great talkability.

Maybe that's why I'll always have trouble with marketing. Of course the simplest form I can think of,

This will make people talk.

doesn't have the same punch. Why remove the people from a marketing slang expression? Probably because marketing people prefer not to think they're manipulating (or is it influencing) humans.

When digital was about digits...

A blog post over on Satisfy me reminded me of the good old Compuserve days. Oh yes I was there, and 100530,3355 was my ID! That was back when the Internet wasn't that big mind you. But we had file transfers with resumable downloads, chat rooms, message boards, and a real sense of community.

But then the Internet arrived and I turned into technoboy@cis.compuserve.com (when alias email addresses were tested, and only the SysOps knew about them (oh the social status it was to be SysOp or even WyzOp! (and I promise to stop nesting braces.)))

And one of the first IM system on the Internet? ICQ of course, where I was 2931484 for years (and I believe that account is still alive).

Isn't it strange how years on (13 years for Compuserve, 11 for ICQ) I remember these numbers but have no memory of any of my passwords or previous email addresses.

Maybe the brain is better at numbers than we think. And maybe I miss the old time when the digital world was about digits.

Monday, 18 June 2007

xsd.exe passed away, svcutil.exe is the way to go

I was reading Darren David's blog entry on code generation from xsd files. Sadly, he references xsd.exe that generates Xml serialization code.

If you're using .net 3, the new kid on the block is svcutil.exe that will generate real DataContracts that will give you a smooth path to Indigo (hmmm, WCF).

Sadly, the documentation focuses mainly on generating the client code rather than the DataContract serialization code. There is one page detailing the process: Importing Schema to Generate Classes. Choose to not generate the client and only generate data contracts from your xsd schema and you'll be done, the v3 way.

Wednesday, 13 June 2007

Quickly know when you're in quirk mode

While debugging an application rendering issue yesterday (yes, I'm back to a bit of asp.net work, with CSS adapters and asp.net AJAX, oh the joy!) I came up with a very quick way to know when you're in quirk mode.

In the address bar, type the following:

javascript: window.alert(document.compatMode);

CSS1Compat means the browser is not in quirk mode.

Simple and efficient, but of course not with frames (which you shouldn't use anyway!)

eScrum

For fans of scrum, Microsoft released eScrum v1. Not had the time yet to investigate it but it looks good!

Tuesday, 12 June 2007

Validation in WPF with Enterprise Library

A very good news for those that require enterprise

Technorati Tags: , ,

quality validation, there's now a project to integrate WPF validation and Enterprise Library Validation Application Block.

Wednesday, 6 June 2007

Silverlight Predictions and WPF Snippets.

On a break from blogging at the moment, service will resume when my new MacBook Pro arrives (new resolution, faster CPU, nvidia chipset and 4gig of memory), and when I finally finish my move in central London. That said, my Silverlight predictions were accurate 100%, except I predicted a beta where both an RC and a CTP were released. And thanks to Brownie Points for enhancing my wpf snippets, although my blog is not notstatic.com and he forgot to give me credits. Hopefully he'll read the trackback and amend his entry. A lot could be said about the addition of UIPropertyMetadata against FrameworkPropertyMetadata. Rule of thumb if you're using WPF, unless you know the difference between the UIElement and the FrameworkElement levels, stick with the Framework layer. In this case, for WPF FrameworkPropertyMetadata is acceptable, for other toolkits only PropertyMetadata would be more accurate.

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 :)

Wednesday, 7 February 2007

Goodbye MacBook Pro, Hello PowerShell

Well, I am mourning the departure of my MacBook Pro to the hands of a thief in All Bar One on a friday evening with my colleagues.

If anyone ever see a MacBook Pro 17inches Core2 Duo with glossy screen and, more specific and probably unique to mine, a big bump next to the opening of the superdrive, please report it immediately to the London City police in Islington.

And of course, thanks to Murphy's law, the insurance I thought was covering it didn't, and am off £2,200, and no more computer to work. Now that's a major blow! I'll wait till the next revision of the macbook before buying a new one, that I will insure to the maximum coverage!

I've also decided to ditch my old backup system using cds and dvds after a few got corrupted and i ended up loosing a huge amount of stuff. From now on it will be a RAID5 1TB windows server running somewhere hidden in the house.

But on a brighter note, my beloved PowerShell is finally released for Windows Vista and you can download it here.