Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Note: This blog has moved to omervk.wordpress.com.

Omer van Kloeten's Facebook profile

Omer has been professionally developing applications over the past 8 years, both at the IDF’s IT corps and later at the Sela Technology Center, but has had the programming bug ever since he can remember himself.
As a senior developer at NuConomy, a leading web analytics and advertising startup, he leads a wide range of technologies for its flagship products.

Get Firefox


powered by Dapper 

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

December 2007 - Posts

Life With WPF: 10 Things Wrong With WPF Today

[Caveat: This is constructive criticism about XAML and the current version of WPF. They both have their strengths and things I love about them and they both may become better over time, but this is about what we have today.]

After Tomer published  his list of 10 Reasons to Love XAML, I wrote a long rebuttal. After reviewing the points I wrote, I decided to instead counter it with a list of ten of the things I think are wrong with WPF and XAML today. I'm originally a Windows Forms developer, so at times my points may seem like a comparison with it.

  1. Separation of Code From Design?
    Developers shouldn't need to think about styling their data's logical structure. This is very nicely implemented in HTML, where the developer denotes <div> when they want to divide their structure. The developer doesn't need to know where that div's contents will sit at the end of the day, when the page is presented to the end-user. For them, it's a simply <div>.
    In WPF, there's an attempt to embrace this principle, but it just doesn't go far enough. Why should I, as a developer, when faced with the decision of which container to use for my elements, have to decide between a StackPanel, a Canvas, a Grid, a WrapPanel, a DockPanel or a UniformGrid (which are only some of the choices for a panel, not to mention everything else that isn't a panel).

    "Let the designers have the first go then," you'd say. And you'd be wrong. Just try it and you'll see so many WPF 'sins', you'll simply have to do the whole thing over again. Just take a look at Dr. Greenshpan's lecture (last link on the right, when it finally decides to start working again; sorry, Hebrew only) from MSDA2, where after a year's work, they reached the exact same conclusion - the iterations of design-development should start with the developer. Why should a designer care if they have a StackPanel or a VirtualizingStackPanel? Why should a designer care if they have 5000 BitmapEffects? I, on the other hand, would.

    The problem starts and ends with both the designer and the developer having to deal with the same files and object structures, simply because the separation of logic from visualization is incomplete. After all, in a well-defined work environment, no web developer would give their designer their HTML sources much in the same way that no web designer would let a developer mess with their CSS.
  2. No Code = Run-Time Errors Galore!
    There are two distinguishable schools of thought when it comes to infrastructure development: The first being the one of Generators, where the tool you use creates code for you and parses said code when you need to change something using the tool; The second being the Framework, where the tool you use is what gives you services, so you have to write less code.
    Full disclosure: I'm firmly in the Generators camp, even though it's been a while since I wrote one.

    The problem here is not that WPF is a Framework (and a little bit of a generator too - more on this in the next paragraph). After all, the CLR is a Framework and the .NET Framework is, you guessed it, a Framework. I'm all for a Framework, but only when it's nice a the developer. WPF is not nice to .NET developers: It throws exceptions that sometimes have nothing to do with the error encountered. It writes a lot of stuff to the Trace output and quietly fails. If it wasn't for tools like Mole, which incidentally was not released by Microsoft but rather by a few good developers from the community, the average developer could spend several hours a day just trying to find out what the heck went wrong with what they've done.

    A 'funny' anecdote is whenever you get a compile-time error from a file generated by the compiler, like Window1.g.cs, when it says that a namespace was not found or that you can't inherit from one class or the other. Good luck tracking that one down in one of your XAML files (or maybe it's not even there! Yay, Terrance, let's look for treasure!)
  3. Strong Typing? Generics? Never Heard of Those
    WPF sits on top of CLR 2.0, but fails to use its characteristics:
    • I want my Dependency Properties to be typed, rather than checked at run-time.
    • I don't want the run-time to yell at me when I send the wrong type of parameter or the wrong parameter order to a converter. I want the compiler to check that for me.
    • The CLR team invented CLR Generics for a reason - why can't I use my generic classes with WPF? Is it simply because angle brackets don't translate well to XML?
    I want the most possible compile time errors to occur, so that I don't have to deal with them at run-time.
  4. XAML Adds Complexity and Logic/Data Cohesion
    Let's face it - XAML is a form of DSL. It includes new syntax for binding, resource references, property references and so on, a different parser and compiler, three more mediating layers (which comes down to six layers already: XAML->WPF->.NET Framework->BAML->IL->ASM) and overall - more complexity.
    Wait, no, it's actually data - it's a hierarchical structure of your user interface. You could just as easily store it in a database with a hierarchical structure and load the appropriate pieces at runtime. Just think of how LocBaml (and we'll come to that too) treats BAML.

    So which is it? Logic or Data? It's both.
  5. Don't Like the Control's Style? Do Over!
    The WPF mindset says you have to style your control. That's all well and good, but what if my control is made up of other controls and I want to style those? No can do, bub, unless the control's original developer explicitly let you do that. So what do you do instead? You redesign the control - copy/paste the original design and do what you like with it.
    This is horrible news for those of us who like code (and design) re-use. You simply unlink the new design form the old one and do it all over again, even if what you wanted to change was a sub-sub-sub-sub-sub control's background color. And what happens when the original control gets updated to a newer, cooler design and you simply would love to get that one? You guessed it - you copy/paste and Redo From Start.
  6. XAML is Code-Behind-Language Dependant
    And if you didn't know that, here's an example of why.
  7. Converters Seem Like a Patch
    Converters are really useful - you want to take a piece of data and convert it to a different representation, so you write a piece of code in your code behind and reference your binding to it. But then you realize that the feature is a very simple, stripped down version of the idea:
    • You only get to pass one static parameter. Want to pass on more? You'd have to create your own data structure. And did we mention it wasn't statically typed? (see #4)
    • You can't pipe converters for re-use (instead of converting from Type1 to Type2 with a preexisting Type1toType2Converter and then to Type3 with a preexisting Type2toType3Converter, you have to write a new piece of code for a Type1toType3Converter), unless you go ahead and create your own framework for it.
    • Values aren't named, so you have to rely on the - you guessed it - untyped order.
    • Conversion, when there's no converter specified, is done using Type Converters, a framework you can not extend.
    • There are bugs and missing features.
  8. Lack of a Good Tool for Developers
    1. Cider (Designer View): No one I know uses Cider. We all write XAML by hand. I've never seen Tamir open a Cider window. Tomer went as far as to make the XML editor his default editor for XAML files.
      Do you know how to attach to events using Cider? I don't, because as far as I know except for the default event - you can't.
      Have you noticed that half of the properties in the grid are type names, so you can't edit them?
    2. XAML View: How many times have you received weird warnings from your XAML editor? How many times did it show you a compilation error as long as you didn't actually go and compile it?
      Also, a good IDE allows the user to write code without using its auto-completion features, but the XAML editor interferes with your intuitive writing flow and you have to get used to it, instead of it being used to you.
  9. Lack of a Complete Tool for Designers
    I have to say that as far as this item is concerned - I have very little personal experience.
    For as long as I have worked with the designers on the current project, I've heard a whole range of statements from "Blend can't do this... well, at least not in the current version" to "Blend knows how to do that, but it simply doesn't work that well with it." These problems were so big they have resorted to working with a CTP of version 2.0.
  10. Localization as a Second Class Citizen
    I have no idea why the people behind WPF took a leap backwards from Windows Forms when they made LocBaml. With absolutely no internal Visual Studio and/or Blend support, you have to resort to translating plain text files and using command line tools to compile them.
    My current client decided to completely stop using it (also because it needed features that just didn't and couldn't exist in LocBaml, but did in Windows Forms, such as replacing the language at runtime) and asked for a whole new mechanism.

...and I haven't even talked about the incompleteness of the WPF framework (for instance - it doesn't have any color-oriented transformations (I had to resort to GDI+ for that one)) or some other little annoyances (see footnote here)...

I don't want anyone to think I dislike WPF. I don't dislike it. It has it's place in the world and has already proven itself to be a very useful skill in my arsenal. All I hope for is that some, if not all of the above problems are tended to. Version 2.0 - I'm waiting for you with open arms! :D

 

Wow, that was long. I hope you got through it. I don't think I would have been able to myself. ;)

WPF Performance and Another Useful Document

Here's a collection of performance related information I find useful:

  1. Optimizing WPF Application Performance at MSDN.
  2. WPF Performance Whitepaper at Kiran Kumar's blog. Much of the content is similar in both links.
  3. WPF Performance Blog.

And here's another document I loved when I first got started with WPF: WPF for those who know Windows Forms by Jessica Folser (aka JFo).

Posted: Dec 18 2007, 09:05 AM by Omer van Kloeten | with 1 comment(s)
Filed under:
Did You Know? Type Member Lookup by Prefix

You can look up for prefixed members, using reflection, by placing an asterisk after the prefix:

MemberInfo[] members = typeof(A).GetMember("hidden*", 
    BindingFlags.NonPublic | BindingFlags.Instance);

// members now contains three members: hiddenFlag1, hiddenFlag2 and hiddenMethod

// ...

public class A
{
    private bool hiddenFlag1;
    private bool hiddenFlag2;

    private void hiddenMethod()
    {
    }

    private void reallyHiddenMethod()
    {
    }
}
Releasing My Code Snippet Repository

Whenever I teach classes or do demos, I get asked about the C# code snippets I use in Visual Studio, so I decided to release them, if anyone would like to use them.
I've created a project on CodePlex and I'll try to update it whenever I write a new one or update the old ones. Nothing to release as such, so visit the Source Code tab to download the latest version.

Here's a list of what I've uploaded so far:

C# Snippets

  • Constructor
  • Event as EventHandler
  • Event as EventHandler<T>
  • Get Property
  • List<T> Property
  • Get+Set Property
  • Get+Set Property with Changed Event
  • Get+Set Property with Changed and Changing Events
  • Get+Set Property with Changed and Cancelable Changing Events
  • Immutable Type with One Field
  • Immutable Type with Two Fields
  • Immutable Type with Three Fields
  • Throw on Null Value
  • Throw on Null or Empty String

C# Snippets for WPF

  • Dependency Property
  • Dependency Property with Change Event
  • Attached Dependency Property
  • Attached Dependency Property with Change Event
  • Single Value Converter
  • Multiple Value Converter
Two Birds with One Stone: Introducing The MultiBindingFoldingConverter

After realizing the project I'm working on is in need of two features that do not exist in WPF - one being a missing feature, the other being a bug - I decided to take action and write something that would allow one to write code that is currently an unsupported scenario.

First, let's take a look at what we want accomplish. We have the following Window, that shows the speed we're walking at. It has three combo boxes, signifying (from left to right) the language of the form (English or Dutch), the distance measurement units (Meters or Kilometres) and the time measurement units (Seconds or Hours), along with a text box that indicates the speed we're walking (as measured in KM/H). What we would like to do is to translate this information to a single string. This is how we would have done this, if the above mentioned features were not missing:

The above illustration shows a single converter (a call to string.Format) which would receive 5 values - the format (language specific, so it has to be converted using a converter), the text for "Speed" (same as the format - must be converted to the wanted language), the speed itself (after being converted from its original value to the requested distance and time unit types) and the distance and time unit types' names (converted to the language of our choice).

However, this scenario is impossible in today's WPF, so in its stead, I have created the MultiBindingFoldingConverter, which allows you to create the above tree with the following XAML:

<MultiBinding Mode="OneWay">
    <MultiBinding.Converter>
        <f:MultiBindingFoldingConverter>
            <f:Fold Converter="{StaticResource stringFormatConverter}">
                <!-- Format -->
                <f:BindingReference Index="0"
                    Converter="{StaticResource translationConverter}"
                    ConverterParameter="Format" />
                <!-- "Speed" -->
                <f:BindingReference Index="0"
                    Converter="{StaticResource translationConverter}"
                    ConverterParameter="Speed" />
                <!-- Speed value -->
                <f:Fold
                    Converter="{StaticResource speedConverter}">
                    <!-- Speed -->
                    <f:BindingReference Index="1" />
                    <!-- Distance -->
                    <f:BindingReference Index="2" />
                    <!-- Time -->
                    <f:BindingReference Index="3" />
                </f:Fold>
                <!-- Distance -->
                <f:Fold
                    Converter="{StaticResource translationConverter}">
                    <f:BindingReference Index="0" />
                    <f:BindingReference Index="2" />
                </f:Fold>
                <!-- Time -->
                <f:Fold
                    Converter="{StaticResource translationConverter}">
                    <f:BindingReference Index="0" />
                    <f:BindingReference Index="3" />
                </f:Fold>
            </f:Fold>
        </f:MultiBindingFoldingConverter>
    </MultiBinding.Converter>
    <!-- Language -->
    <Binding ElementName="language" Path="SelectedItem" />
    <!-- Speed -->
    <Binding ElementName="speed" Path="Text" />
    <!-- Distance -->
    <Binding ElementName="distance" Path="SelectedItem" />
    <!-- Time -->
    <Binding ElementName="time" Path="SelectedItem" />
</MultiBinding>

Instead of writing the Binding elements hierarchically, you simply write the logic of the conversion hierarchy using Fold (instead of MultiBinding) and BindingReference (instead of Binding) elements.

BindingReference elements are exactly what you'd guess they were - references to individual binding tags inside the original MultiBinding, using their zero-based index. For instance, the first BindingReference receives its value from the zeroth Binding element, i.e. the language binding.
Fold elements take their child BindingReference and Fold elements and fold them into a single value, much like a MultiBinding element folds many Binding elements.
Both BindingReference and Fold elements are Evaluators, which means that they can use a value converter (IValueConverter and IMultiValueConverter respectively), along with its parameter and a culture. A Mode attribute can also be placed on them, much like one would be placed on a Binding (updating the source is not fully supported and tested, simply because I have yet to find a case in which I would need it).

An additional upside to this solution is the fact that each Binding happens only once per calculation, so in our example, rather than have 9 Binding elements along with 4 more MultiBinding elements, we only have 4 Binding elements and one MultiBinding.

The code and sample are both available on a new project I opened on CodePlex, under the DotNetZen.Presentation.Converters and Converters projects respectively. I intend to keep adding new ideas and code to that repository as I go along. There's no release, but you can browse the source code itself and download a zip file of the entire code base.

Multi-Value Converters, Value Converters and the Case of the Wrong Target Type
<TextBlock>
    <TextBlock.Text>
        <MultiBinding>
            <MultiBinding.Converter>
                <local:UnwrapperConverter />
            </MultiBinding.Converter>
            <Binding ...>
                <Binding.Converter>
                    <local:WrapperConverter />
                </Binding.Converter>
            </Binding>
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

The above XAML places a MultiBinding expression on the Text dependency property of TextBlock. This MultiBinding in turn takes its value from a Binding expression and passes it through a custom converter. The Binding expression itself has a converter of its own.

Here is the code for the converters. It is pretty straightforward:

public class Wrapper
{
    public readonly object Wrapped;

    public Wrapper(object wrapped)
    {
        this.Wrapped = wrapped;
    }
}

public class WrapperConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
object parameter, CultureInfo culture) { return new Wrapper(value); } public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture) { return Binding.DoNothing; } } public class UnwrapperConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType,
object parameter, CultureInfo culture) { Debug.Assert(values[0] is Wrapper, "value received is not a Wrapper"); return ((Wrapper)values[0]).Wrapped; } public object[] ConvertBack(object value, Type[] targetTypes,

object parameter, CultureInfo culture) { throw new NotSupportedException(); } }

The WrapperConverter wraps the item returned from the simple Binding and the UnwrapperConverter in the MultiBinding unwraps it and sends it to the TextBlock's Text property.
Pretty simple up until now, right? Right. If only it would work.

You see, the assertion in UnwrapperConverter throws an exception because the value it receives is not a Wrapper, but rather a string.
Why does this happen, you ask. I'm sure you could guess by yourself if I told you the string is "MyNamespace.Wrapper".
Yes - Wrapper.ToString is called on the object returned from the WrapperConverter. Buy why?!

To understand this, we must first look at the Convert method's targetType parameter in IValueConverter and IMultiValueConverter. The value we get in this parameter is the type the property we set the Binding or MultiBinding on must be of. This is very helpful when writing a multi-purpose converter, where we can test the value of this parameter to see what type we should return.

But what value is the parameter set to when the converter works on a Binding inside a MultiBinding? It doesn't have any property to set, so you'd think the type would be the generic object.

You'd be wrong.

The target type sent to the converter, and therefore the target type of the binding itself, is taken from the containing MultiBinding, so in our case, when you return a value from the converter to the Binding, it is then converted to a string - the type of the TextBlock.Text dependency property - and only then passed on to the MultiBinding's converter.

Fix? In the next service pack, I hope.

Note: When not sending the value through a converter, the type will not be changed to the target type.

Posted: Dec 11 2007, 04:25 PM by Omer van Kloeten | with 3 comment(s)
Filed under:
DependencyPropertyChangedEventArgs? Think Again.

Delegate Contravariance is a really cool feature in C# that is often overlooked, mostly because it is so implicit and "obvious". Go read the example I linked to if you don't know what it is (it's a short one).

The following code, however, will not compile, even though you'd expect it to:

public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register("MyProperty", typeof(int), typeof(MyControl),
        new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnMyPropertyChanged)));

public int MyProperty
{
    get { return (int)this.GetValue(MyPropertyProperty); }
    set { this.SetValue(MyPropertyProperty, value); }
}

private static void OnMyPropertyChanged(object sender, EventArgs e)
{
    ((MyControl)sender).OnMyPropertyChanged(e);
}

protected virtual void OnMyPropertyChanged(EventArgs e)
{
    if (this.MyPropertyChanged != null)
        this.MyPropertyChanged(this, e);
}

public event EventHandler MyPropertyChanged;

This is due to the fact that the PropertyChangedCallback delegate, to which all property-changed methods must adhere, has two parameters: DependencyObject and DependencyPropertyChangedEventArgs. The problem lies in the DependencyPropertyChangedEventArgs value, since it does not inherit from EventArgs. It's even a struct!
Bummer. Now we have to start writing plumbing code.

Incidentally, this is my 200th post... and it only took me a bit over four years :)

Posted: Dec 10 2007, 11:39 AM by Omer van Kloeten
Filed under: ,
ContentPresenter Is Not Set When Using a ControlTemplate for a ContentControl

I encountered a problem today, the solution to which some of you may find worth keeping note of.

One of the user controls in my Window had to sit inside a fancy container. Obviously, I didn't want to mix plainly visual elements with my logical ones (and also allow the graphic designers to work in peace), so instead I wrapped my element with a ContentControl and gave it a style:

<ContentControl Style="{StaticResource userControlDecorationStyle}">
    <MyAwesomeUserControl />
</ContentControl>

I then went to my ResourceDictionary and gave the style a ControlTemplate which contained the fancy container and a ContentPresenter where my user control was meant to be:

<Style x:Key="userControlDecorationStyle" TargetType="ContentControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <FluffyBunniesBorder ...>
                    <ContentPresenter />
                </FluffyBunniesBorder>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Running it didn't produce the result I wanted - the visual tree showed the ContentPresenter as a leaf in the tree, instead of replacing it with my user control.

After a bit of work, I found the solution to this. All I had to do was to let the ControlTemplate know it's being used on a ContentControl:

<!-- ... -->
<Setter.Value>
    <ControlTemplate TargetType="ContentControl">
        <FluffyBunniesBorder ...>
            <!-- ... -->

And presto, my user control appears.

Posted: Dec 03 2007, 11:53 AM by Omer van Kloeten | with 2 comment(s)
Filed under:
More Posts