Navigation


Markus on Development and Publishing

This is Markus Egger's professional blog, which covers topics such as development, publishing, and business in general. As the publisher of CoDe and CoDe Focus magazines, and as the President and Chief Software Architect of EPS Software Corp., Markus shares his insights and opinions on this blog.

Content Area Footer

Monday, September 27, 2010
Adding Behavior to Styles and other XAML

WPF and Silverlight styling is amazing (and frankly leaps and bounds beyond HTML5 and CSS3, but that is a different story altogether…). You can easily re-style your entire application or individual controls. You can style the layout of entire screens. You can re-brand Silverlight components so they can be reused on different sites. It’s awesome. And it is all done declaratively in XAML.

One of the most common questions I always get when it comes to styling is: “I need to add custom behavior and thus a code-behind file. How do I do that?”.

Well, the simple answer is: You don’t! And you don’t have to. In fact, I consider it a great architectural characteristic of WPF/SL that styles do not have the capability to add custom behavior! Why? Because a style is “content” and attached code-behind programs are “behavior”. And frankly, one of the biggest mistakes the software industry as a whole has made over time is mixing content and behavior. It is the greatest security nightmare ever created! Take Word documents for instance: Why do we have this mess with macro-viruses? Because when you open a Word document, which after all is generally mostly content, you might inadvertently activate some sort of script (behavior) that does things you might not want. This can only happen because content and behavior is mixed. If Word documents (or documents in other apps) didn’t have this facility at all, we would not have any macro viruses. (And we could always have a different kind – a .docm file format perhaps, or something like that – that enabled macros, just so one knew when opening a file what to expect).

There are many other document types that have the same problem. HTML for instance is a classic example of missing content and behavior. To such an extreme in fact, that we really can’t differentiate between content and script hardly at all, which is why we have to deal with things such as cross-site scripting attacks and a lot more. (And don’t even get me started on SQL Injection attacks, which are rooted in the same problem…). But this post is not meant to be about this classic security issue (books like Writing Secure Code spend a lot of time and space on this very issue).

Anyway: Pure XAML styles do not have this problem, because XAML is just content. XAML specifies which objects are to be used and what kinds of things to trigger (through triggers, events, commands, and behaviors). However, it is important to realize that the objects (and other behaviors) that are references in XAML already need to be compiled into the app. So it is easily possible to reference and use all kinds of behavior objects that the application is designed to provide, but even if someone downloads a XAML style file from a web site, there is no inherent security risk, because no unwanted new behavior can come along. It is a design I like very much.

But back to the issue at hand: What if you do indeed need to reference behavior in your XAML file in a style that you would typically use a code-behind file for? Well, here’s the trick: Whatever you have in your XAML file simply refers to objects. For instance, consider the following XAML snippet:

<Window x:Class="WpfApplication.MainWindow"
        xmlns= "
http://schemas.microsoft.com/winfx/ 2006/xaml/presentation"
        xmlns:x= "http://schemas.microsoft.com/winfx/ 2006/xaml">
    <Grid>
        <Button>Hello!</Button>
    </Grid>
</Window>

In this snippet, a Window hosts a Grid, which hosts a Button. All these tags simply refer to classes of the same name. There is nothing special about these classes. You can also add your own classes and instantiate them as part of the XAML markup like this:

<Window x:Class="WpfApplication.MainWindow"
        xmlns= "
http://schemas.microsoft.com/winfx/ 2006/xaml/presentation"
        xmlns:my= "clr-namespace:WpfApplication"
        xmlns:x= "http://schemas.microsoft.com/winfx/ 2006/xaml">
    <Grid>
        <Button x:Name="button1">Hello!</Button>
        <my:GreatBehavior />
    </Grid>
</Window>

Of course you can do whatever you want in this special object. Nobody says an object in XAML needs to have a visual appearance. In this case, it only provides behavior. Let’s say you had originally intended to create an on-mouse-over handler for the button object in a code-behind file. Instead, you can use this special object and add the following code:

public class GreatBehavior : FrameworkElement
{
    public GreatBehavior()
    {
        Loaded += delegate
                        {
                            var button = FindName("button1") as Button;
                            button.MouseEnter += (s, e) => MessageBox.Show("Now!");
                        };
    }

}

This code finds the button and attaches the desired behavior to the button’s event, just like you would in a code-behind file. Except you now have a behavior-less XAML file that can be securely loaded, no matter where it came from. This of course implies that the GreatBehavior class was already compiled into your app (as would a code-behind file). So this is certain to be entirely safe. You can use this same technique with any XAML file. In fact, I often write my views in my MVVM apps where the views are loose XAML files with no code-behind whatsoever. It works great!

Note that some might point out that this is not the greatest solution syntax-wise, as there is no clear indicator that the behavior goes with the button. That is true, and it is probably better to attach the behavior object to the button, which can be done through an attached property. This is a little beyond the scope of this blog post, but check out this article by Josh Smith on how to do that and thus achieve more agreeable syntax. The basic idea remains the same.

Now what does all of this have to do with styles and templates? Well, basically, when creating advanced skins and templates, I quite often run into scenarios where I just can’t do quite everything I want with declarative XAML. And since there is no code-behind file in skins ever, I use this very technique to introduce my own behavior and thus have complete freedom to do whatever I want. These behavior objects could be pure behavior objects as in the above example.

Another variation on the theme is to subclass an existing object. For instance, in my MVVM applications, I let my view model define “actions”, which are then used to populate a toolbar or similar UI element. (Check out this article of mine in CODE Magazine for a detailed discussion of this concept). However, if my view model does not have any actions defined, I do not want the toolbar (or whatever UI element I use) to show up at all. I thus created myself a subclassed Grid that sets itself visible or collapsed depending on whether the current data context object implements the IHaveActions interface.

Once you have this sort of setup, you are back to being able to do whatever you can do in source code, yet you can include it in any XAML you want, and you can do so entirely without introducing security risks. And this freedom can be worth a lot in sophisticated applications!



Posted @ 5:01 PM by Egger, Markus (markus@code-magazine.com) -
Comments (643)


 

 

 

 

 

 

 

Syndication RSS 2.0 RSS 2.0

All My Blogs:
My personal blogs:
Dev and Publishing Dev and Publishing
Travel and Internat. Living Travel and Internat. Living
Other blogs I contribute to:
Milos Blog (US) Milos Blog (US)
VFPConv. Dev Blog (US) VFPConv. Dev Blog (US)
VFPConv. Dev Blog (DE) VFPConv. Dev Blog (DE)

 

Blog Archives
All Blog Posts

2015
    September (1)
2012
    September (1)
    April (1)
    March (1)
2011
    October (1)
    June (3)
    May (1)
    March (2)
    February (2)
    January (2)
2010
    December (3)
    November (2)
    October (2)
    September (1)
    August (2)
    July (1)
    June (1)
    April (3)
    March (1)
    February (5)
    January (1)
2009
    October (4)
    September (2)
    August (1)
    July (1)
    May (4)
    April (6)
    February (1)
    January (1)
2008
    December (3)
    November (11)
    October (8)
    September (1)
    July (1)
    June (3)
    May (3)
    April (6)
    March (6)
    February (4)
2007
    December (1)
    November (1)
    October (5)
    September (1)
    August (1)
    July (6)
    June (3)
    May (3)
    April (1)
    March (2)
    January (2)
2006
    December (3)
    November (4)
    October (1)
    September (2)
    August (2)
    July (4)
    June (1)
    May (2)
    April (10)
    March (2)
    February (3)
    January (1)
2005
    December (6)
    November (7)
    October (6)
    September (8)
    August (10)
    July (6)
    June (9)

 

 

 

This Blog is powered by MilosTM Collaboration Components.