Blog?

I'm sure you have something better to do.
posts - 5, comments - 2, trackbacks - 1

Monday, May 12, 2008

Photo Stackr (Silverlight goodness)

So with the release of the Silverlight 2 beta bits I finally decided to see what all the talk was about. I suppose I would consider myself an ASP.NET guy by trade. Although I have been building WPF applications for the past year or so and generally I am enjoying NOT building web applications. However, I can't hide the fact that I spent the last seven odd years building mostly ASP.NET applications.

So when I first heard of Silverlight and it in the same vain as flash I was skeptical to say the least. I would say I was somewhere closer to outright disgust. I spend so much time focusing on a lightweight client that offloads it's work to a controlled and decidedly beefier web server. There is no way I want to push a plug-in to a client and THEN pay the price of shoving all the assest's down the pipe and hoping their machine can handle it. but alas, I will give it a fair shake. I do like WPF after all.

So with that said I wanted to see what the development experience is like and what kind of things I could do with the WPF skills I had amassed.

 

Cue PhotoStackr (note the gratuitous web 2.0 spelling)

 

One of the things I really like with WPF, and the main thing that makes WPF so compelling to me, is that it gives you the ability to focus on user interaction and user experience and less on implementation details. This allows you to build incredible applications with minimal effort (compared to building the equivalent in WinForms). So before I show the gory details of my Silverlight application I will say that I was trying to build an application that models a known collection of data (in this case a set of photos from the flickr website) in a new / interesting way.

To that end Silverlight, really XAML in general, gives you the tools to visual your data quickly. For the photo stackr application I wanted to give a interesting way to view and organize multiple search results on a single page.

What I like about Silverlight is it changes the development paradigm on the web. With the plug-in architecture you have a stateful environment to develop to. You can write fully managed code to do things previously done on the client in JavaScript (like drag and drop)

The basic design idea of PhotoStackr is to have a "Photo Table", which is the main canvas of the application. On the table you will have multiple "Photo Stacks." These stacks are the result of searching the Flickr web service for a specified tag. The application will give you the ability to "un-stack" your search results so you can view all results at once. You can then zoom in on any one of the results to view the photo in more detail.

There is a link at the bottom of this post where you can get the entire project to view the code in more detail so I will just focus on the main points.

The main canvas is pretty simple I put a text block to show the title in the top left and then two custom controls, one for the search box and one for the about window. I am toggling the visibility of these controls on the button click event of the respective buttons. This brings me to my first annoyance with Silverlight. The current build doesn't support triggers. Triggers are fundamental (IMHO) to WPF. Since I am approaching Silverlight with a WPF background the lack of triggers is painfully obvious. However, Sivlerlight is still in beta so some features just aren't there. I don't know if there is a plan to support triggers in Silverlight. Personally, I think they have to be supported if they want Silverlight to follow the "Do it in markup, not in code" mantra.

 

<Canvas Background="Black" x:Name="Table" Loaded="Table_Loaded" SizeChanged="Table_SizeChanged" >
    <TextBlock x:Name="Title" Text="Photo Stackr" FontSize="16" Foreground="#FF686868"  Canvas.Left="100" Canvas.Top="50" Opacity=".4"/>
    <Button x:Name="AddButton" Click="AddButton_Click" Template="{StaticResource AddStackButtonTemplate}"  Canvas.Left="100" Canvas.Top="650"/>
    <Button x:Name="AboutButton" Click="AboutButton_Click" Template="{StaticResource AboutButtonTemplate}"  Canvas.Left="125" Canvas.Top="650"/>
    <local:MessageControl x:Name="About" Canvas.Left="250" Visibility="Collapsed"/>
    <local:SearchPopup x:Name="SearchPopup" Canvas.Left="100" Visibility="Collapsed" />
</Canvas>
 

I like the control architecture that Silverlight drives you towards. I have built the SearchPopup and the MessageControl as separate UserControls in it's own XAML file. This is nothing revolutionary, you could do this in ASP.NET with custom controls, but with the stateful environment it makes managing multiple controls much easier. The controls are more aware of the application domain data and don't need state management code to work with the rest of the application. This gives you a development experience much closer to desktop development. This is a big plus to me.

When the user enters a search tag and clicks the search button I create a PolaroidStack control. This control is a canvas that has the logic to manage multiple Polaroid controls and perform the stack and un-stack behaviors. This brings me to the first programming feature that I really like about Silverlight. On the PolaroidStack control I added a static dependency property called SelectedTag

 
public static readonly DependencyProperty SelectedTagProperty = DependencyProperty.Register("SelectedTag", typeof(string), typeof(UserControl),OnSelectedTagChanged);

 

Dependency properties come from WPF and are a key component to control development. Since I have a dependency property on the PolaroidStack all I have to do in code is set the property and the change handler code in the stack control will take care of the rest. 

 
PolaroidStack s = new PolaroidStack();
s.SetValue(PolaroidStack.SelectedTagProperty, SearchPhrase.Text);

 

Change handler for the PolaroidStack.SelectedTag property:

 
private static void OnSelectedTagChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    //Reload the Polaroid Stack
    ((PolaroidStack)d).LoadStack(e.NewValue.ToString());
}

 

The nice thing about the DependencyProperty is that it can also be set in markup. If I created the PolaroidStack in markup I could set the SelectedTag property in the markup as well which would cause the stack load itself when the control is loaded (and this is exactly what I did to test the LoadStack functionality)

In the LoadStack method I make a call the the Flickr Rest based Webservice passing the tag that the user passed in as the search term. The only thing to note here is that I am processing the result of the web service using LinqToXml which makes processing the XML document leaps and bounds easier then before.

 
XDocument doc = XDocument.Parse(e.Result);
var photos = from results in doc.Descendants("photo")
select new
{
    id = results.Attribute("id").Value.ToString(),
    farm = results.Attribute("farm").Value.ToString(),
    server = results.Attribute("server").Value.ToString(),
    secret = results.Attribute("secret").Value.ToString()
};

 

This snippet of code basically creates a dynamic collection and I am defining the members of the objects in the collection by selecting the attributes from the XML document. So I have a collection of Photos, the items in the collection are of type Photo. The Photo type (which is a dynamic type created by LINQ when I call select new) has string members id,farm,server, and secret. And this ends my LINQToXML lesson :), LinqToXML is really cool, for more info check here

After processing the web service response into a collection of Photo objects, I iterate the collection and build a new Polaroid control. The Polaroid control in a custom user control composed of an Image control, a button to control the zoom, and a border to give the look and feel of a Polaroid.

 
<Canvas Height="110" Width="91" Background="#FFFFFFFF" x:Name="PolariodCanvas">
    <Button x:Name="Zoomer" Canvas.Left="70" Canvas.Top="95.666" Click="Button_Click" />
    <Image x:Name="imageControl"  Height="75" Width="75" Stretch="Fill" Canvas.Top="8" Canvas.Left="8"/>
    <Border Height="75.75" Width="76.006" Canvas.Left="8" Canvas.Top="8" BorderBrush="#FF000000" BorderThickness="1,1,1,1"/>
</Canvas>

 

I am also creating a rotate transform in code to randomly set the angle of the Polaroid control to give it the messy look when stacked on top of each other. Again, the control development approach works nicely here because I can keep the logic to zoom the Polaroid in the Polaroid control.

The last thing to point out here is how I implemented drag and drop logic. I wanted to give the ability to drag and drop the PolaroidStack control so I hooked into the mouse button events of the Polaroid control. The actual drag and drop code is nothing new but the ability to do it all in managed code is the big difference. Previously this type of thing would have to be done in javascript. With Silverlight you can do it all in managed C# code

 

private void PolariodStack_MouseMove(object sender, MouseEventArgs e)
{
    // only execute this code when the user has started dragging
    if (startDrag)
    {
        Point movePos = e.GetPosition(null);
        double currentX = double.Parse(this.GetValue(Canvas.LeftProperty).ToString()) + (movePos.X - beginPos.X);
        double currentY = double.Parse(this.GetValue(Canvas.TopProperty).ToString()) + (movePos.Y - beginPos.Y)
        ...
        this.SetValue(Canvas.LeftProperty, currentX);
        this.SetValue(Canvas.TopProperty, currentY);
        beginPos = movePos;
    }
}

 

<Borat> Very Nice </Borat>

 

You can get the code for this project here. NOTE: you will have to sign up for a Flickr account and get your own API key to get this to work. I purposely forced a compile error to show you were to enter your API key.

So in the end, I like what Silverlight offers so far but it isn't all the way there yet. The lack of support for DataTemplates and Triggers  are glaring issues. I am also still not sold on the plug-in approach. Solving the satefulness issue is huge and I think that is the overriding win with the plug-in approach but I am still concerned with pushing that much on to the client machine. The temptation to abuse the technology is high and pushing really intensive applications down the wire to the client can lead to bloated applications and serious support issues.

 

To see the application in action visit our Silverlight page silverlight.interknowlogy.com

 

Tell me what you think

 

As I wrote this I thought of clever way to use this same data visualization for another purpose....... So stay tuned for an update.

posted @ 8:34 PM | Feedback (1)

Friday, May 09, 2008

WPFPerf and software rendering

The WPFPerf tool that is included with the windows SDK can be a very useful tool in finding areas of your application that is being rendered using the WPF software pipeline.

Recently I was profiling an application looking for areas that could be improved, performance wise, at the WPF level. One thing I thought to do was use the WPFPerf tool and look for elements that were being software rendered and see if I could modify them to use the hardware pipeline and still get desired effect. See here for more information on the two pipelines.

To find the areas that are using software rendering the Perfortator plug-in gives a nice, albeit cheesy, visual cue. By enabling "Draw software rendering with purple tint"

 

Purple

 

Come on guys, really? Is that really what the option is called? I wonder what the property is called in code ShowSoftwareRenderingUsingPurpleShadeBecauseMyKidWasWatchingBarneyWhileIWasWritingThisFuction.

But I digress, The functionality works well.

 

 FullApp

So a discovery I made was that WPF is able to use the hardware rendering pipeline to render bitmap drop shadows (which is typically documented as being software rendered) but only if the drop shadow is rectangular. In my case I had a button template that was using a border element ad applying a drop shadow to the border element to give the drop shadow effect on the button

 

SoftwareRender

<Style x:Key="SoftwareRenderTemplate">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid Height="22">
                            <Border Background="Gray" Height="22" Margin="0,0,0,0">
                                <Border.BitmapEffect>
                                    <DropShadowBitmapEffect Opacity="0.5" ShadowDepth="3" Softness="0.285"/>
                                </Border.BitmapEffect>
                                <ContentPresenter x:Name="ButtonContent" Width="Auto" Content="{TemplateBinding Content}" OpacityMask="{TemplateBinding Foreground}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="Auto" Margin="10,0,10,0"/>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

 

When I built the template this way and ran Perforator it showed me that the button was using software rendering. Which is what I would have expected. However, if I modify the template to use a rectangle shape instead of a border element. Then apply the drop shadow effect to the the rectangle I noticed that Perforator no longer indicated (which means to remove the lovely purple tint) that the button was using software rendering. This is not what I would have expected.

 

<Style x:Key="HardwareRenderTemplate">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid Height="22">
                            <Rectangle Fill="Gray" Height="22" Margin="0,0,0,0" Stroke="{x:Null}">
                                <Rectangle.BitmapEffect>
                                    <DropShadowBitmapEffect Opacity="0.5" ShadowDepth="3" Softness="0.285"/>
                                </Rectangle.BitmapEffect>
                            </Rectangle>
                            <ContentPresenter x:Name="ButtonContent" Width="Auto" Content="{TemplateBinding Content}" OpacityMask="{TemplateBinding Foreground}" HorizontalAlignment="Center" VerticalAlignment="Center" Height="Auto" Margin="10,0,10,0"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

 

HardwareRender

 

I searched around a bit to try and determine what was going on here and the information is sparse (hence my blog entry) But I did find one source that hints to the fact that the underlying implementation of the drop shadow effect is optimized when sticking to rectangular shapes.

So there it is. If you really like drop shadows but don't want to get hammered by the performance this is one way to do it.

posted @ 2:37 PM | Feedback (0)

Thursday, December 20, 2007

XML, XSL, XAML, XPS......XACTLY?

For some reason at every company I have worked for I have been dubbed the "Reporting dude." I don't know how this title was bestowed on me but so be it. Unfortunately reports aren't particularly sexy, you never hear people saying "Whoa check out that pivot table!"

 

All that aside, recently I came across a reporting challenge that I thought was particularly interesting. Currently in WPF there isn't much in the way of report designers (at least none that I have seen). We do have some very simple integration with the XPS framework, which makes printing XAML very easy. However, all the examples out there of generating an XPS document relies on the fact that your created the XAML objects in code. Ok, but that goes against a major tenet of WPF of separating the design and the implementation.

 

The challenge I was faced with was to create a reporting architecture in WPF that would support changes to report formats without invasive code changes, Support multiple output formats if desired (XPS, HTML, Excel, etc..) and the nice to have feature is to allow reports to be built using a visual designer if possible.

 

Well looking at the requirements and the toolset available first thought we were going to have to make a lot of compromises. I thought some more about it and took a lesson from my web developer toolset. Why not use XML to model the data and XSL to transform the data to the desired format?

 

I knew this would work for HTML and Excel outputs, what I didn't know was how it would work for XAML. So I gave it a go and here is how it went.

 

1. Model some test data in XML:

We need a sample chunk of data to work with in order to test our idea. So I started with this :

<?xml version="1.0" encoding="utf-8" ?>
<EmployeeInfo>
    <Employee>
        <FirstName>Bob</FirstName>
        <LastName>Smith</LastName>
        <City>San Diego</City>
        <State>CA</State>
    </Employee>
</EmployeeInfo>

2. Transform the data in to a XAML document:

Now that we have sample data to work with we need to write and XSLT document that will take the XML data above and turn it into a XAML document (I wasn't sure what I was going to get with this step. I didn't know how nicely XSL would play with XAML.)

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="EmployeeInfo">
        <StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Orientation="Vertical">
            <TextBlock>
                <TextBlock.Text>
                    <xsl:value-of select="Employee/FirstName"/>
                </TextBlock.Text>
            </TextBlock>
            <TextBlock>
                <TextBlock.Text>
                    <xsl:value-of select="Employee/LastName"/>
                </TextBlock.Text>
            </TextBlock>
            <TextBlock>
                <TextBlock.Text>
                    <xsl:value-of select="Employee/City"/>
                </TextBlock.Text>
            </TextBlock>
            <TextBlock>
                <TextBlock.Text>
                    <xsl:value-of select="Employee/State"/>
                </TextBlock.Text>
            </TextBlock>
        </StackPanel>
    </xsl:template>
</xsl:stylesheet>

 

3.  From here the rest is fairly straight forward. You do the actual transformation in code (something like this)

XmlReader xreader = XmlReader.Create("SampleData.xml");

MemoryStream mem = new MemoryStream();
TextWriter twriter = new StreamWriter(mem, System.Text.Encoding.UTF8);

XslCompiledTransform trans = new XslCompiledTransform();
trans.Load("XAMLOutput.xslt");
trans.Transform(xreader, null, twriter);
 

4. From there all we have to do is to take the transformed output and turn it into an XPS document. What I did was take the transformed output and get it in string format and then created a function to create an XPS document

void XamlToXPS(string srcXaml,string destXpsFile)
        {
            
            StringReader stringReader = new StringReader(srcXaml);
            XmlReader xmlReader = XmlReader.Create(stringReader);

            object parsedDocObject = XamlReader.Load(xmlReader);
           
            XpsDocument document = new XpsDocument(destXpsFile, FileAccess.ReadWrite);
            XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document);
            XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false);
            
            serializationMgr.SaveAsXaml(parsedDocObject);
            document.Close();
        }

That's it!

I tested this much more complex chunks of xaml and didn't find any issues. One thing to note is that you have to make sure the XAML namespace is specified for the root element of the document in order for the

XAML reader to understand the XAML elements (see the bolded area of my sample XSLT).

 

This solution separates the data from the implementation, it allows for format changes to the report without invasive code changes, and it adds the nice to have feature of allowing report design through a visual designer. Since XPS is simply loose xaml packaged up, you can design the report using Blend. Then take that xaml and paste it into the XSLT file and manipulate it to show the data from the XML where needed. 

posted @ 11:41 AM | Feedback (0)

Wednesday, November 21, 2007

WPF "Master Page" like functionality

 

UPDATE: I found and issue with placing the default content elements in the Resources for the Page. When you place the elements directly in the resources, without wrapping them in a template, there is only one instance of the elements created. This means that after the MasterPage control is created the first time the elements will never get re-initialized. This causes problems when you have buttons or other interactions that bind to properties specific to current instance of the master page.

For example : If you had a ButtonContentArea defined and you placed a save button in that area with a Command "SaveCommand" that was bound to a command on the consumer of the MasterPage control, that command would be initialized the first time the MasterPage control is created. Then any subsequent consumers of the master page would use the Binding from the first instance of the master page. This means your save command would be routed to the wrong handler. If you make the Dependency Properties of Type DataTemplate and you wrap the elements in a DataTemplate then bind to the ContentTemplate property of the controls you can resolve this issue. The code below has been updated to reflect this change.

Now on with your regularly scheduled programming.

 

Coming from an ASP.NET background one of the things I really like are Master Pages. If you aren't familiar with master pages read this first.

In WPF there are several different ways to share styles and templates across pages. Recently I had a need to share a common layout across multiple pages while still allowing for different content and behaviors on each page. The first thing I thought was Master Page. However this feature doesn't come out of the box with WPF.

However it is easy enough to implement your own version. A quick search provided me with Karin Huber's code project article about this very subject. This approach uses a user control as the master page. After playing around with this example one problem I found is that you are unable to name elements that are inside of the master page user control (others have discovered this as well if you view the comments on the codeplex project or you view Rob Relyea's blog post) This isn't a deal breaker by any means but I wanted to give a go at implementing it myself. It was surprisingly easy when I got down to it.

 

First I created a simple custom control named Master Page and a few dependency properties for the different content zones I am going to expose. Here is what that looks like

    public class MasterPage : Control
    {
        public static DependencyProperty MainContentAreaProperty =
            DependencyProperty.Register("MainContentArea", typeof (DataTemplate), typeof (MasterPage));

        public static DependencyProperty HeaderContentAreaProperty =
            DependencyProperty.Register("HeaderContentArea", typeof(DataTemplate), typeof(MasterPage));

        public static DependencyProperty RightContentAreaProperty =
            DependencyProperty.Register("RightContentArea", typeof(DataTemplate), typeof(MasterPage));
       
        public static DependencyProperty FooterContentAreaProperty =
           DependencyProperty.Register("FooterContentArea", typeof(DataTemplate), typeof(MasterPage));
      
        static MasterPage()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MasterPage), new FrameworkPropertyMetadata(typeof(MasterPage)));
        }

        public DataTemplate MainContentArea
        {
            get { return (DataTemplate)GetValue(MainContentAreaProperty); }
            set { SetValue(MainContentAreaProperty, value); }
        }

        public DataTemplate HeaderContentArea
        {
            get { return (DataTemplate)GetValue(HeaderContentAreaProperty); }
            set { SetValue(HeaderContentAreaProperty, value); }
        }

        public DataTemplate RightContentArea
        {
            get { return (DataTemplate)GetValue(RightContentAreaProperty); }
            set { SetValue(RightContentAreaProperty, value); }
        }

        public DataTemplate FooterContentArea
        {
            get { return (DataTemplate)GetValue(FooterContentAreaProperty); }
            set { SetValue(FooterContentAreaProperty, value); }
        }

    }

 

The generic.xaml is straightforward. I placed ContentControls where I wanted "content zones." I named them accordingly and I bound the ContentTemplate property to the appropriate dependency property

<Style TargetType="{x:Type local:MasterPage}">
        <Setter Property="HeaderContentArea" Value="{StaticResource HeaderContent}"/>
        <Setter Property="MainContentArea" Value="{StaticResource MainContent}"/>
        <Setter Property="RightContentArea" Value="{StaticResource RightContent}"/>
        <Setter Property="FooterContentArea" Value="{StaticResource FooterContent}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MasterPage}">
                    <Grid ShowGridLines="True">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"></ColumnDefinition>
                            <ColumnDefinition Width="225"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="50"></RowDefinition>
                            <RowDefinition Height="*"></RowDefinition>
                            <RowDefinition Height="25"></RowDefinition>
                            <RowDefinition Height="25"></RowDefinition>
                        </Grid.RowDefinitions>
                        <ContentControl Grid.Column="0" Grid.Row="0" x:Name="HeaderContentArea" ContentTemplate="{TemplateBinding HeaderContentArea}"/>
                        <ContentControl Grid.Column="0" Grid.Row="1" x:Name="MainContentArea" ContentTemplate="{TemplateBinding MainContentArea}"/>
                        <ContentControl Grid.Column="0" Grid.Row="2" x:Name="FooterContentArea" ContentTemplate="{TemplateBinding FooterContentArea}"/>
                        <ContentControl Grid.Column="1" Grid.RowSpan="3" x:Name="RightContentArea" ContentTemplate="{TemplateBinding RightContentArea}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

This allows a consumer of this control to bind any piece of content they want to the dependency property of their choice and that content will be displayed in the "content zone" defined in the generic.xaml.

 

For example your Window1.xaml would look something like this 

    <Window.Resources>
        <DataTemplate x:Key="HeaderContent">
            <StackPanel>
                <TextBlock Text="Header Text"/>
                <Rectangle Fill="Black" Width="25" Height="25"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="RightContent">
            <StackPanel Grid.RowSpan="3">
                <Rectangle Width="100" Height="50" Fill="DarkBlue"/>
            </StackPanel>
        </DataTemplate>
        
    </Window.Resources>
   
    <local:MasterPage  HeaderContentArea="{StaticResource HeaderContent}" RightContentArea="{StaticResource RightContent}" />

Pretty simple right?

 

Well what if we don't bind to all the available content zones like in the example above? With the current setup the layout elements (in this case grid rows / columns) will still be rendered but they will be empty. This may or may not be the desired behavior for your situation.

 

Furthermore you don't want to build this nice layout template and still force your consumers to bind content that will be static across multiple consumers. So we want to provide default content for the content zones from within the generic.xaml.

Easy enough we just need to bind the content property of our zone to a static resource defined in generic.xaml right?

Something like this :

<DataTemplate x:Key="FooterContent">
    <StackPanel>
        <TextBlock Text="Copyright 2007. All rights reserved"/>
    </StackPanel>
</DataTemplate>
  
<ContentControl Grid.Column="0" Grid.Row="2" x:Name="FooterContentArea" Content="{StaticResource FooterContent}"/>
 

 

This will work and will give you a distinct behavior. By doing this you are no longer binding to the dependency property defined in the control and therefore you are forcing the content zone to use the content defined in the generic.xaml. Your consumer will not be able override the content for this zone. This may or may not be the desired behavior.

 

For my situation I wanted to provide default content for the different zones but I also wanted to let the consumer override the content if they wanted to. I also wanted to provide specific defaults for zones that give the user a visual cue they have not bound to a "required" content zone.

 

First we defined default content for each of our zones in the generic.xaml file like this.

<DataTemplate x:Key="HeaderContent">
        <StackPanel>
            <TextBlock Text="Header" FontSize="15" FontWeight="Bold"/>           
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="MainContent">
        <StackPanel>
            <TextBlock Opacity="0.25" FontSize="18" Text="CONTENT ERROR! Bind content to the MainContentArea dependency property" TextWrapping="Wrap"/>
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="RightContent">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Right Text"/>
        <Rectangle Fill="AliceBlue" Width="25" Height="25">
            <Rectangle.BitmapEffect>
                <DropShadowBitmapEffect/>
            </Rectangle.BitmapEffect>
        </Rectangle>
        <Ellipse Fill="YellowGreen" Width="25" Height="25"/>
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="FooterContent">
        <StackPanel>
            <TextBlock Text="Copyright 2007. All rights reserved"/>
        </StackPanel>
    </DataTemplate>

 

Then we modified our control template to use Setters to set the dependency properties to the Static Resources in our generic.xaml.

<Style TargetType="{x:Type local:MasterPage}">
        <Setter Property="HeaderContentArea" Value="{StaticResource HeaderContent}"/>
        <Setter Property="MainContentArea" Value="{StaticResource MainContent}"/>
        <Setter Property="RightContentArea" Value="{StaticResource RightContent}"/>
        <Setter Property="FooterContentArea" Value="{StaticResource FooterContent}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:MasterPage}">
                    <Grid ShowGridLines="True">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"></ColumnDefinition>
                            <ColumnDefinition Width="225"></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="50"></RowDefinition>
                            <RowDefinition Height="*"></RowDefinition>
                            <RowDefinition Height="25"></RowDefinition>
                            <RowDefinition Height="25"></RowDefinition>
                        </Grid.RowDefinitions>
                        <ContentControl Grid.Column="0" Grid.Row="0" x:Name="HeaderContentArea" ContentTemplate="{TemplateBinding HeaderContentArea}"/>
                        <ContentControl Grid.Column="0" Grid.Row="1" x:Name="MainContentArea" ContentTemplate="{TemplateBinding MainContentArea}"/>
                        <ContentControl Grid.Column="0" Grid.Row="2" x:Name="FooterContentArea" ContentTemplate="{TemplateBinding FooterContentArea}"/>
                        <ContentControl Grid.Column="1" Grid.RowSpan="3" x:Name="RightContentArea" ContentTemplate="{TemplateBinding RightContentArea}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
 

 

Notice that we can now keep the content properties for each of our zones bound to the dependency properties we defined in our class. We have also set these properties with default values so if the consumer doesn't set them they will get the content we defined. This is nice because it allows us to move static content in to the custom control's generic.xaml and leaves less for the consumer to worry about.

Another nice feature is we are able to give the user a visual error if they do not bind to a zone that we feel they should bind to. In this case I have defined a main content area and defaulted the content to a textblock that shows an error:

<DataTemplate x:Key="MainContent">
        <StackPanel>
            <TextBlock Opacity="0.25" FontSize="18" Text="CONTENT ERROR! Bind content to the MainContentArea dependency property" TextWrapping="Wrap"/>
        </StackPanel>
    </DataTemplate>

 

Right away the consumer will see the message that they forgot to bind to the content area and can quickly fix the error.

 

Overall this is a pretty simple solution that gives you a nice way to share a layout across multiple pages while still allowing the individual pages to control the content. This also makes it easy to change the layout of your pages in one place. You just modify the layout of the "content zones" in the master page and all your consumer using the control will see the change.

 

UPDATE: You can find the sample code here

posted @ 2:46 PM | Feedback (3)

Monday, November 19, 2007

Anonymous Delegates, how do I love thee? Let me count the ways...

This isn't anything new but I came across a situation recently where I was able to re-factor a seemingly trivial piece code to use an anonymous delegate and it reminded me how cool they are.

One great thing about generics is you can create a collection of strongly typed custom objects and iterate that collection to perform any number of operations. Often times you want to locate an item in the collection given a certain criteria.

I put together a simple little example to show the situation pre-generics,post generics without anonymous delegates, and post generics with anonymous delegates.

 

To set the scene let's assume we have a person class defined as follows

public class Person   

{
        private int id;

        private string firstName = string.Empty;
        private string lastName = string.Empty;


        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }

        public string LastName
        {
            get { return lastName; }
            set { lastName = value; }
        }
    }

 

Prior to generics you might load an ArrayList of person objects like so:

        static ArrayList LoadPersonList()
        {
            ArrayList list = new ArrayList();

            Person Bob = new Person();
            Bob.ID = 1;
            Bob.FirstName = "Bob";
            Bob.LastName = "Smith";

            Person Sally = new Person();
            Sally.ID = 2;
            Sally.FirstName = "Sally";
            Sally.LastName = "Jones";

            list.Add(Bob);
            list.Add(Sally);

            return list;
        }

So working from this example if we wanted to find the Person record with the first name Bob we could do something like this.

 

First write a find method that searches the ArrayList for the given first name. Something like this :

        static Person FindPersonByFirstName(string firstName, ArrayList personList)
        {
            foreach (Person p in personList)
            {
                if (p.FirstName == firstName)
                {
                    return p;
                }
            }
            return null;
        }

Then we call the find method and pass the FirstName "Bob":

       static void Main(string[] args)
        {
            ArrayList personList = LoadPersonList();
            Person bob = FindPersonByFirstName("Bob", personList);
        }

Not so bad.

 

With generics we get a little better

 

First our load method now looks like this

      static List<Person> LoadPersonList()
        {
            List<Person> list = new List<Person>();

            Person Bob = new Person();
            Bob.ID = 1;
            Bob.FirstName = "Bob";
            Bob.LastName = "Smith";

            Person Sally = new Person();
            Sally.ID = 2;
            Sally.FirstName = "Sally";
            Sally.LastName = "Jones";

            list.Add(Bob);
            list.Add(Sally);

            return list;
        }

 

And our find method has been simplified to look like this

        private static bool FindPersonByFirstName(Person p)
        {
            if (p.FirstName == "Bob")
                return true;
            else
                return false;
        }

 

And now we use the List<T> Find method like this to locate the item in the list that we want:

        static void Main(string[] args)
        {
            List<Person> personList = LoadPersonList();
            Person found = personList.Find(FindPersonByFirstName);
        }

 

You notice that we are now hard-coding "Bob" in the implementation of the Find method. That obviously isn't ideal. We need a better solution for passing arguments to the find method while still leveraging the generic List's built in find features (namely iterating the collection for you)

 

This is where anonymous delegates come in to play. Using the same person list we loaded above, we can eliminate our find method all together and do this instead:

       static void Main(string[] args)
        {
            List<Person> personList = LoadPersonList();
            Person found = personList.Find( delegate (Person p) { return p.FirstName == "Bob";});
        }


 

Neat huh?

 

To take it a little further if you want to be able to call a generic method passing the List and the value to search for (instead of writing the above for each time you need to find an item). You could do something like this :

        static Person FindPersonByFirstName(string firstName, List<Person> list)
        {
            return list.Find(delegate(Person p) { return p.FirstName == firstName; });
        }

 

With this implementation of the Find method the Main method from above would like this:

        static void Main(string[] args)
        {
            List<Person> personList = LoadPersonList();
            Person found = FindPersonByFirstName("Bob", personList);
        }

 

This works for all of the methods attached List<T> and will simplify the mundane task implementing the guts of the various methods (FindAll,FindLast,Exists,etc..)

 

 

posted @ 6:53 PM | Feedback (0)