Wednesday, 27 June 2007

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.

Friday, 26 January 2007

Very exciting news... A DataGrid!

Xceed just released version 1 of their DataGrid. We're super excited here, as it would mean not having to invest in writting our own for the functionality we need. We'll have to see if it's flexible enough to do what we want. Stay tuned!

Thursday, 25 January 2007

Attached Events By Example - Adding an Activate event to any Selector element (ListView, ListBox, TreeView, etc...)

I couldn't resist using a long title. I've had this article on the back of my mind for a while but only managed to get in the mood for a big writing session tonight, after a glass of Stella and a soapy bath. Go figure.

A common struggle with ListViews, TreeViews and other controls is the lack of an ItemActivate event like the one found in WinForms. Several solutions have been offered, most of them involving inheriting from these controls to add the missing functionality. In the spirit of Windows Presentation Foundation's (oh, Avalon, where art thou...) emphasis on composition, I thought I'd offer a generalized solution for any Selector control, with no need for inheritance, using a nearly unknown little gem called attached events.

Attached events are not much talked about (Nick mentions them in passing), documented (msdn mentions them in one paragraph), or printed about (as far as I can read, neither the Petzold nor Chris & Ian's book talk about them, although Ian mentioned to me it will be in the next version). So what is an attached event, and what is not? Let's start with what it is not.

[Disclaimer: At core, attached events are just routed events used in a different way, and as such are more a pattern than an actual specific piece of technology. And as any pattern, they end up with a fancy name. Naming conventions are based on current msdn documentation, which may (and should) change in the future.]

Qualified Event Names

Often, when people think they use attached events, they in fact use Qualified Event Names. This special syntax lets you attach an event handler for a RoutedEvent anywhere in the tree above the element triggering that event. For example, the following code attaches the Click event triggered by the Button element type, but on its direct parent.

    1 <Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">

    2     <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">

    3         <Button Name="YesButton" Width="Auto" >Yes</Button>

    4         <Button Name="NoButton" Width="Auto" >No</Button>

    5         <Button Name="CancelButton" Width="Auto" >Cancel</Button>

    6     </StackPanel>

    7 </Border>

This syntax is the same you'll use for attached events, so it could be said that to add a listener to an attached event, you use the Qualified Event Name notation.

Attached Events

So what are attached events? Let's see what our friends at msdn have to say.

An attached event allows you to attach a handler for a particular event to some child element rather than to the parent that actually defines the event, even though neither the object potentially raising the event nor the destination handling instance define or otherwise "own" that event in their namespace.

If you're like me, it takes a few readings to understand what it means. Let's take it one bit at a time. The first thing to realize is that we're talking about a normal routed event, declared the same way as usual.

        public static readonly RoutedEvent ItemActivateEvent =

            EventManager.RegisterRoutedEvent("ItemActivate",

                                            RoutingStrategy.Bubble,

                                            typeof(RoutedEventHandler),

                                            typeof(ItemActivation));

Then, it is said attached events are defined on an element. Actually they don't need to be defined on an element that is in your element tree at all, and not even on an element. For example, the Mouse class is a sealed class, and yet it defines all mouse-related attached events. What would be more accurate would be to say that the class defining the event is often neither the user of the event (whoever consumes it by adding a handler on it) nor the source of the event (whatever code raises it). For example, the previous attached event has been defined on my ItemActivation class.

namespace SerialSeb.Windows.Controls

{

    public static class ItemActivation

    {

        ...

What is common to all attached events however, is the absence of an event declaration using the add{} and remove{} accessors to call AddHandler and RemoveHandler on the instance of the object. Instead, and I suppose it is by convention, two static methods are defined to achieve the adding and removing of an handler.

        public static void AddItemActivateHandler(DependencyObject o, RoutedEventHandler handler)

        {

            ((UIElement)o).AddHandler(ItemActivation.ItemActivateEvent, handler);

        }

        public static void RemoveItemActivateHandler(DependencyObject o, RoutedEventHandler handler)

        {

            ((UIElement)o).RemoveHandler(ItemActivation.ItemActivateEvent, handler);

        }

You'll find that all attached events follow the same AddEventNameHandler and RemoveEventNameHandler convention.

The pieces of the puzzle start falling into place slowly. Of course, now that an event has been defined, you want to consume it. To do so, you want to add a handler for that event, but it's not defined on an element present in your tree. The following XAML code shows how it can be done. Note that you can of course add and remove handlers programmatically through the two methods you've defined.

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

    xmlns:my="clr-namespace:SerialSeb.Windows.Controls">

    <Grid my:ItemActivation.ItemActivate="HandleItemActivate">

        ...

    </Grid>

</Window>

I believe it strikes a phenomenal resemblance to the Qualified Event Name notation I mentioned earlier. Now, whenever the routed event ItemActivate we've defined earlier bubbles up from somewhere within the grid, it will be caught just like any normal routed event. After all, as I said before, this is just a normal routed event.

We've seen how you declare an attached event, and how to add a handler for it anywhere on our tree. The big question is now to know who is going to raise it? Like any routed event, any code can raise this event, as long as it knows on which element it wants to start bubbling it.

Raising an Attached Event

Raising our ItemActivate event needs to be done whenever, within a Selector, a user double-click on an item, or presses the Enter key after selecting one. To do so, we need to attach some behavior to that element, and we're going to use Dan Crevier's excellent attached property trick.

Whenever you declare an attached property, you can define a PropertyChangedCallback that will get called whenever the value of the property changes, and that includes the first time it's applied. And you get a reference to the element it's applied to, absolutely perfect to hook our behavior code to the Selector element!

Let's start by defining the attached property in our ItemActivation class.

        public static ActivationMode GetActivationMode(DependencyObject obj)

        {

            return (ActivationMode)obj.GetValue(ActivationModeProperty);

        }

        public static void SetActivationMode(DependencyObject obj, ActivationMode value)

        {

            obj.SetValue(ActivationModeProperty, value);

        }

        public static readonly DependencyProperty ActivationModeProperty =

            DependencyProperty.RegisterAttached("ActivationMode"

                                                typeof(ActivationMode),

                                                typeof(ItemActivation),

                                                new FrameworkPropertyMetadata(ActivationMode.None,

                                                ItemActivation.HandleActivationModeChanged));

Overall a very simple attached property called ActivationMode. The type is a simple enumeration defining what can trigger the raising of our event, Mouse, Keyboard or both. And finally, a call to our HandleActivationModeChanged static method that will provide for the subscription to the events we're interested in.

        private static MouseButtonEventHandler SelectorMouseDoubleClickHandler = new MouseButtonEventHandler(ItemActivation.HandleSelectorMouseDoubleClick);

        private static KeyEventHandler SelectorKeyDownHandler = new KeyEventHandler(ItemActivation.HandleSelectorKeyDown);

        private static void HandleActivationModeChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)

        {

            Selector selector = target as Selector;

            if (target == null) // if trying to attach to something else than a Selector, just ignore

                return;

            ActivationMode newActivation = (ActivationMode)e.NewValue;

            if ((newActivation & ActivationMode.Mouse) == ActivationMode.Mouse)

            {

                selector.MouseDoubleClick += SelectorMouseDoubleClickHandler;

            }

            if ((newActivation & ActivationMode.Keyboard) == ActivationMode.Keyboard)

            {

                selector.KeyDown += SelectorKeyDownHandler;

            }

            else

            {

                selector.KeyDown -= SelectorKeyDownHandler;

                selector.MouseDoubleClick -= SelectorMouseDoubleClickHandler;

            }

        }

I defined two handlers for the KeyDown and the MouseDoubleClick events of our Selector. Only thing left to do is to raise our event when the user double-clicked on something.

        static void HandleSelectorMouseDoubleClick(object o, MouseButtonEventArgs e)

        {

            ItemsControl sender = o as ItemsControl;

            DependencyObject originalSender = e.OriginalSource as DependencyObject;

 

            if (sender == null || originalSender == null) return;

 

            DependencyObject container = ItemsControl.ContainerFromElement(sender as ItemsControl, e.OriginalSource as DependencyObject);

            // just in case, check if the double click doesn't come from somewhere else than something in a container

            if (container == null || container == DependencyProperty.UnsetValue) return;

 

            // found a container, now find the item.

            object activatedItem = sender.ItemContainerGenerator.ItemFromContainer(container);

 

            if (activatedItem != null && activatedItem != DependencyProperty.UnsetValue)

                sender.RaiseEvent(new ItemActivateEventArgs(ItemActivation.ItemActivateEvent, sender, activatedItem, ActivationMode.Mouse));

        }

 We get the container (the one returned by the ItemsControl.ItemTemplate property), from which we can get the Item that is being represented by this fragment of the tree.

And there you have it, we raise our attached event by calling the RaiseEvent method of our target element! Our attached event is now going to bubble from the Selector and you'll be able to catch it wherever you want in your tree.

Conclusion

We now have an ItemActivate routed event that can be used on any selector, by using attached properties and attached events. Through composition we've added behavior and functionality to a type without having to inherit from it.

Next time, we'll learn how to bind a RoutedEvent to a Command anywhere in your XAML. Stay tuned...

P.S. I'll post the complete code, with keyboard support, and tunneling PreviewItemActivate event when I find the time to set-up my other web sites. In the meantime, don't hesitate to copy and paste!

Wednesday, 24 January 2007

You know how it is sometimes...

.. you download stuff and you forget to even install it. I did that today with StyleSnooper. Added to my toolbox!

Thursday, 18 January 2007

Going through my WPF backblog...

I use this post as a bookmark as much for myself as for others.

Mike Hillberg talks about the Loaded and Initialized event, as well as about Trace sources in WPF.

How to show different text based on an enum value?

This question was asked on the MSDN forums, so I thought I'd replciate it here for everybody's benefit.

When you want to bind to a value that's an enumeration, sometimes you want to show specific text. While writting a converter is one way of solving this problem, it requires writting code. And no code is of better quality than the one you don't write.

So here's the sample I posted.

MainWindow.xaml:

    1 <Window x:Class="MsdnForums.TextBlockBoundToEnum.MainWindow"

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

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

    4     xmlns:local="clr-namespace:MsdnForums.TextBlockBoundToEnum"

    5     Title="TextBlockBoundToEnum" Height="300" Width="300"

    6     >

    7     <!-- Forums: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1107413&SiteID=1 -->

    8     <DockPanel>

    9         <StackPanel DockPanel.Dock="Right">

   10             <TextBlock>Current CurState value:</TextBlock>

   11             <Button Click="HandleGoodButtonClick">Good</Button>

   12             <Button Click="HandleBadButtonClick">Bad</Button>

   13             <Button Click="HandleUglyButtonClick">Ugly</Button>

   14         </StackPanel>

   15         <TextBlock>

   16             <TextBlock.Style>

   17                 <Style TargetType="{x:Type TextBlock}">

   18                     <Style.Triggers>

   19                         <DataTrigger Binding="{Binding Path=CurState}" Value="Good">

   20                             <Setter Property="TextBlock.Text" Value="That was a good one!" />

   21                         </DataTrigger>

   22                         <DataTrigger Binding="{Binding Path=CurState}" Value="Bad">

   23                             <Setter Property="TextBlock.Text" Value="That's no good!" />

   24                         </DataTrigger>

   25                         <DataTrigger Binding="{Binding Path=CurState}" Value="Ugly">

   26                             <Setter Property="TextBlock.Text" Value="Ooooooooooh look at hiiiim!" />

   27                         </DataTrigger>

   28                     </Style.Triggers>

   29                 </Style>

   30             </TextBlock.Style>

   31         </TextBlock>

   32     </DockPanel>

   33 </Window>

MainWindow.cs

    1 using System;

    2 using System.Windows;

    3 

    4 namespace MsdnForums.TextBlockBoundToEnum

    5 {

    6     public partial class MainWindow : System.Windows.Window

    7     {

    8         private MyDataContext myDataContext;

    9         public MainWindow()

   10         {

   11             InitializeComponent();

   12             this.DataContext = myDataContext = new MyDataContext();

   13         }

   14         public void HandleGoodButtonClick(object source, RoutedEventArgs e)

   15         {

   16             myDataContext.CurState = APP_STATE.Good;

   17         }

   18         public void HandleBadButtonClick(object source, RoutedEventArgs e)

   19         {

   20             myDataContext.CurState = APP_STATE.Bad;

   21         }

   22         public void HandleUglyButtonClick(object source, RoutedEventArgs e)

   23         {

   24             myDataContext.CurState = APP_STATE.Ugly;

   25         }

   26     }

   27     public class MyDataContext : DependencyObject

   28     {

   29         public APP_STATE CurState

   30         {

   31             get { return (APP_STATE)GetValue(CurStateProperty); }

   32             set { SetValue(CurStateProperty, value); }

   33         }

   34         public static readonly DependencyProperty CurStateProperty =

   35             DependencyProperty.Register("CurState", typeof(APP_STATE), typeof(MyDataContext), new UIPropertyMetadata(APP_STATE.Good));

   36     }

   37     public enum APP_STATE

   38     {

   39         Good,

   40         Bad,

   41         Ugly

   42     }

   43 }

To profile your WPF applications

Tim Cahill talks about profiling your WPF application. Not a new post but one worth knowing, bookmarking and using!

Monday, 15 January 2007

Blog or forum...

I've not been very active updating this blog. First, I'm in the process of setting up another blog that would support some stuff I have pending, and on my own domain name. Plus it would be nice to re-upload my old archives of entries and without controlling the code it's gonna be hard.

Second issue for me, and the major one, is that my time is extremely limited as we're all working very hard on the project we're working on at the moment (WPF of course). And it comes down to a choice to make between my blog and my contributions in the msdn forum.

So for now, you can find me on the msdn wpf forum. I genuinely think my time is better spent there for the next few weeks, until the new blog arrives!

Wednesday, 3 January 2007

Using winmerge as a merge tool in Visual Studio Team Suite

Found in msdn, to change the diff/merge tool used on conflicts with TFS:

Go to Tools > Options > Source Control > Visual Studio Team Foundation Server > Configure User Tools...

Add a Compare pointing to winmergeU.exe and using:

/e /x /s /wl /dl %6 /dr %7 %1 %2

as a command-line argument.

Repeat the operation for merge, this time using:

/e /s /x /ub /dl %6 /dr %7 %1 %2 %4

Enjoy!