The blog of dlaa.me
Tag: "Development Tips"
  • Invisible pixels are just as clickable as real pixels! [Tip: Use a Transparent brush to make "empty" parts of a XAML element respond to mouse and touch input]
    Friday, August 19th 2011

    Tip

    Use a Transparent brush to make "empty" parts of a XAML element respond to mouse and touch input

    Explanation

    I got a question yesterday and thought the answer would make a good addition to my Development Tips series. As you probably know, WPF, Silverlight, and Windows Phone support a rich, hierarchical way of laying out an application's UI. Elements can be created in XAML or in code and respond to input by firing the relevant events (MouseLeftButtonDown, Click, etc.). Input events bubble from the element "closest" to the user all the way up to the root element (stopping if an event is marked Handled). Every now and then someone finds that an element they expect to be getting input is not (and they've made sure none of its children are "eating" the event). The most common reason is that the element doesn't have any pixels for the user to click on! For example, in a 100x100 panel containing a short message, only the text pixels are considered part of the panel and respond to mouse input - everything else passes "through" the empty area and bubbles up to the parent. This behavior enables the creation of elements with any shape, but sometimes it's not what you want. Fortunately, it's simple to get empty parts of an element to respond to input: just draw some pixels! And while a Brush of any color will do the trick, painting with Transparent pixels is a fantastic way to keep empty space looking empty while also being clickable!

    Good Example

    <Grid
        Background="Transparent"
        MouseLeftButtonDown="Grid_MouseLeftButtonDown">
        <TextBlock
            Text="You can click anywhere in the Grid!"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"/>
    </Grid>

    More information

    Tags: Silverlight WPF Development Tips Windows Phone
  • Breaking up (lines) is (not) hard to do [Tip: Put XAML attributes and elements on multiple lines so the markup is easy to read and work with]
    Thursday, June 24th 2010

    Tip

    Put XAML attributes and elements on multiple lines so the markup is easy to read and work with

    Explanation

    The last few tips have dealt with DependencyProperty issues. Now it's time for something completely different: XAML. While it's nice to pretend XAML editing can all be done in a design tool like Visual Studio or Expression Blend, I've always felt that XAML should be pleasant for people to work with, too. Therefore, it's worthwhile to establish a common approach to XAML formatting. What I find works well is to put attributes and elements on separate lines with a single indent for continuations and nesting. (Aside: I make an exception for elements with a single attribute to keep simple things compact.) I also tend to order properties according to importance, starting with the most significant ones and ending with the least. I consider the x:Name of an element to be the most important, so it typically comes first. Attached properties also rank high in my book and are usually next. After that, I tend to list common properties (ex: ContentControl.Content and TextBlock.Text) first and formatting properties (FrameworkElement.HorizontalAlignment and .Margin) closer to the end. In addition to keeping lines short and easy to read, this approach is very revision control-friendly - by which I mean that edits, adds, and removes all show up unambiguously. Sometimes people forget that markup is code, too - please treat it with the same respect and discipline. :)

    Good Example

    <Button
        x:Name="TheButton"
        Grid.Row="0"
        Content="Click me"
        Click="TheButton_Click"
        HorizontalAlignment="Center"
        Margin="10">
        <Button.Background>
            <SolidColorBrush Color="Pink"/>
        </Button.Background>
    </Button>

    More information

    Tags: Silverlight WPF Development Tips
  • "I would prefer even to fail with honor than to win by cheating" [Tip: For a truly read-only custom DependencyProperty in Silverlight, use a read-only CLR property instead]
    Friday, April 2nd 2010

    Tip

    For a truly read-only custom DependencyProperty in Silverlight, use a read-only CLR property instead

    Explanation

    My last tip outlined the best way I knew to approximate a WPF-like read-only DependencyProperty on Silverlight. (Aside: It's also a good way to implement coercion.) However, it suffers from the unavoidable problem that the actual DependencyProperty is still writable and can "twitch" if written to. The comments for that post proved to be very interesting - with two suggestions for alternate techniques. I looked into them both (and stumbled across a third myself) to understand their merits. Unfortunately, the technique I stumbled across and the one Morten Nielsen (SharpGIS) suggested both suffer from this same issue: the DependencyProperty is still writable and there are ways to sneak around the protections and write to it anyway. (Aside: Morten's solution is fiendishly clever and well worth having a look at.) However, the technique suggested by Dr. WPF doesn't have that limitation - it produces a guaranteed read-only property. The catch is that it's not actually a DependencyProperty! However, Dr. WPF points out that a read-only DependencyProperty doesn't actually need to be a DependencyProperty as long as the class implements INotifyPropertyChanged. What's great is that there's no loss of functionality with the implementation shown below, and no need to try to subvert the platform! The thing to remember is that you can't use TemplateBinding to get at it - you'll need to use Binding + RelativeSource instead. Overall, this is a very nice solution - even if it does bend the rules just a bit... :)

    Good Example

    public class MyControl : Control, INotifyPropertyChanged
    {
        public int MyReadOnly
        {
            get { return _myReadOnly; }
            protected set
            {
                if (value != _myReadOnly)
                {
                    int oldValue = _myReadOnly;
                    _myReadOnly = value;
                    OnMyReadOnlyChanged(oldValue, _myReadOnly);
                    PropertyChangedEventHandler handler = PropertyChanged;
                    if (null != handler)
                    {
                        handler.Invoke(this, new PropertyChangedEventArgs("MyReadOnly"));
                    }
                }
            }
        }
        private int _myReadOnly;
        protected virtual void OnMyReadOnlyChanged(int oldValue, int newValue)
        {
            // TODO: Handle property change
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    <ControlTemplate TargetType="local:MyControl">
        <ContentControl Content="{Binding MyReadOnly, RelativeSource={RelativeSource TemplatedParent}}"/>
    </ControlTemplate>

    More information

    Tags: Silverlight WPF Development Tips
  • Sometimes you just gotta do the best you can [Tip: Read-only custom DependencyProperties don't exist in Silverlight, but can be closely approximated]
    Tuesday, March 30th 2010

    Tip

    Read-only custom DependencyProperties don't exist in Silverlight, but can be closely approximated

    Explanation

    My last tip discussed a special case of creating a Silverlight/WPF DependencyProperty where it's necessary to create a read-only property. (Aside: Read-only DependencyProperties are read-only outside the owning class, but can be changed by the class itself at any time.) This task is quite simple on WPF where a single call to RegisterReadOnly does it all. However, Silverlight (as of version 4) does not support the RegisterReadOnly method, so if you want a read-only DependencyProperty on that platform, you'll need to do some extra work. Unfortunately, I don't think it's possible to do a perfect job - but you can get fairly close with something like the code below. The basic principle is to catch illegal attempts to change the property's value (i.e., those coming from outside the owning class) and undo those changes as quickly and silently as possible. For convenience, the CLR wrapper's setter hides the details from users of the class (in a notable exception to one of the earlier tips). The code looks a little more complicated than it really is because it tries to be resilient to exceptions and because it uses two state variables to avoid calling the class's virtual OnPropertyChanged method when recovering from a bogus change. For consistency, the exception that's thrown after an invalid attempt to change the property's value is similar to the corresponding exception on WPF. And while this approach can't prevent bound values from seeing the property "twitch" briefly, I also don't know of a way to avoid that (recall that the DependencyProperty itself must be public so other code can create Bindings to it). Like I said above, this isn't perfect - but it's pretty close. :)

    Good Example

    public int MyReadOnly
    {
        get { return (int)GetValue(MyReadOnlyProperty); }
        protected set
        {
            try
            {
                _changingMyReadOnly = true;
                SetValue(MyReadOnlyProperty, value);
            }
            finally
            {
                _changingMyReadOnly = false;
            }
        }
    }
    private bool _changingMyReadOnly;
    private bool _restoringMyReadOnly;
    public static readonly DependencyProperty MyReadOnlyProperty = DependencyProperty.Register(
        "MyReadOnly", typeof(int), typeof(MyControl), new PropertyMetadata(0, OnMyReadOnlyChanged));
    private static void OnMyReadOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MyControl myControl = (MyControl)d;
        if (myControl._changingMyReadOnly)
        {
            if (!myControl._restoringMyReadOnly)
            {
                myControl.OnMyReadOnlyChanged((int)e.OldValue, (int)e.NewValue);
            }
        }
        else
        {
            try
            {
                myControl._restoringMyReadOnly = true;
                myControl.MyReadOnly = (int)e.OldValue;
            }
            finally
            {
                myControl._restoringMyReadOnly = false;
            }
            throw new InvalidOperationException("'MyReadOnly' property is read-only and cannot be modified.");
        }
    }
    protected virtual void OnMyReadOnlyChanged(int oldValue, int newValue)
    {
        // TODO: Handle property change
    }

    More information

    Tags: Silverlight WPF Development Tips
  • That's why it's called the default *value* instead of default *values* [Tip: The default value of a DependencyProperty is shared by all instances of the class that registers it]
    Monday, March 29th 2010

    Tip

    The default value of a DependencyProperty is shared by all instances of the class that registers it

    Explanation

    The last two tips explained how to set the default value of a Silverlight/WPF DependencyProperty. But there's something you need to be aware of when you're using either technique: the default value of a DependencyProperty is shared by all instances of a class. This doesn't tend to matter for value types, immutable reference types, and sharable types (like brushes), but it affects mutable reference types and can lead to unexpected behavior. The most common scenario is creating a collection-type DependencyProperty (for something like Collection(T)) - the intent is for each instance to have its own unique collection, but because the default value is shared, all instances end up sharing the same list! In such cases, there are two things to change: make the DependencyProperty read-only (with RegisterReadOnly) and initialize the property in the class constructor. [Didn't a previous tip say that was bad? Yes, but this scenario is special. :) ] When a class exposes a collection-type DependencyProperty, the intent is typically to use the same collection instance for the life of the object. And that's what makes it okay to set the property in the constructor: it doesn't matter that nobody can override the default value with a Style because they're not supposed to anyway. Next time: Why this can't be done on Silverlight.

    Good Example

    public Collection<string> MyStringCollection
    {
        get { return (Collection<string>)GetValue(MyStringCollectionProperty); }
    }
    protected static readonly DependencyPropertyKey MyStringCollectionPropertyKey =
        DependencyProperty.RegisterReadOnly(
            "MyStringCollection",
            typeof(Collection<string>),
            typeof(MyControl),
            new PropertyMetadata(null));
    public static readonly DependencyProperty MyStringCollectionProperty =
        MyStringCollectionPropertyKey.DependencyProperty;
    
    public MyControl()
    {
        SetValue(MyStringCollectionPropertyKey, new Collection<string>());
    }

    More information

    Tags: Silverlight WPF Development Tips
  • When you have two good options, go with the easier one [Tip: Set DependencyProperty default values in a class's default style if it's more convenient]
    Friday, March 26th 2010

    Tip

    Set DependencyProperty default values in a class's default style if it's more convenient

    Explanation

    In the previous tip, I explained why it's usually wrong to assign a value to a Silverlight/WPF DependencyProperty in the constructor for a class. The preferred way is to pass the default value in the call to Register, but there's another good option: set the property's starting value in the default Style for the control by putting it in generic.xaml. A control's default style is applied when it is first created and the corresponding changes to its DependencyProperty values have very low precedence (though not as low as the default value passed to Register). Therefore, this is a safe place to set default values without the risk of overriding application-level customizations. A nice benefit of this approach is that it allows the value to be specified in XAML - which offers a designer-friendly syntax and can sometimes be easier to understand. In the example below, a rather complicated Brush is constructed in XAML; the matching code to create that same brush would not be as clear. Next time: Something to watch out for when setting default values.

    Good Example

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:DevelopmentTips">
        <Style TargetType="local:MyControl">
            <Setter Property="MyBrush">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Color="Red" Offset="0"/>
                        <GradientStop Color="Green" Offset="0.5"/>
                        <GradientStop Color="Blue" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>

    More information

    Tags: Silverlight WPF Development Tips
  • The platform giveth power, don't taketh it away [Tip: Do not assign DependencyProperty values in a constructor; it prevents users from overriding them]
    Thursday, March 25th 2010

    Tip

    Do not assign DependencyProperty values in a constructor; it prevents users from overriding them

    Explanation

    Initializing variables in an object's constructor is considered a Good Thing. Traditionally, initialization is done with a simple assignment that sets the variable to its initial value: MyProperty = 10;. However, doing that with a Silverlight/WPF DependencyProperty uses the CLR wrapper (more background here and here) and results in a call to SetValue that sets the local value of that property. The precedence order for DependencyProperty values is such that the local value overrides almost any other value the application may have provided with a Style (normal or implicit) Setter or Trigger. But if a property can't be styled, then much of the goodness of being a DependencyProperty goes out the window... Fortunately, there are two good alternatives; the most direct is to pass the default value in the call to Register. Setting the default value that way is nice because it's easy, it's obvious, and it "just works". And since DependencyProperty default values have the lowest precedence of anything, you don't need to worry about overriding any customizations users may have made. Next time: The other option.

    Good Example

    public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
        "MyProperty", typeof(int), typeof(MyControl), new PropertyMetadata(123, OnMyPropertyChanged));

    More information

    Tags: Silverlight WPF Development Tips
  • Freedom isn't free [Tip: When creating a DependencyProperty, follow the handy convention of "wrapper+register+static+virtual"]
    Wednesday, March 24th 2010

    Tip

    When creating a DependencyProperty, follow the handy convention of "wrapper+register+static+virtual"

    Explanation

    The fundamental steps for defining a Silverlight/WPF DependencyProperty are fairly rigid and not open to a great deal of flexibility (as I discuss in this earlier tip about the CLR wrapper). However, there's a bit more freedom once you add a default value or a PropertyChangedCallback delegate to the mix - but don't let it go to your head! :) For convenience and flexibility, I recommend the pattern shown below; the same one used by most of the core Silverlight and WPF controls. Observe that while the PropertyMetadata constructor requires a static delegate for property change notifications, doing instance-specific work in a static method is inconvenient. Therefore, the static method below does the bare minimum before handing execution off to a more appropriate instance method. (Aside: Explicit casts are safe because the DependencyProperty infrastructure is responsible for honoring the contract of the Register call.) The extra level of indirection also provides an opportunity to pass more meaningful parameters to the change handler: the property's old value and its new value. And because the instance method is virtual, subclasses can override it to receive their own notification of property changes easily and efficiently. Working with DependencyProperty can be tricky enough; do yourself a favor and start with a solid foundation.

    Good Example

    public int MyProperty
    {
        get { return (int)GetValue(MyPropertyProperty); }
        set { SetValue(MyPropertyProperty, value); }
    }
    public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
        "MyProperty", typeof(int), typeof(MyControl), new PropertyMetadata(0, OnMyPropertyChanged));
    private static void OnMyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((MyControl)d).OnMyPropertyChanged((int)e.OldValue, (int)e.NewValue);
    }
    protected virtual void OnMyPropertyChanged(int oldValue, int newValue)
    {
        // TODO: Handle property change
    }

    More information

    Tags: Silverlight WPF Development Tips
  • Do one thing, and do it well [Tip: The CLR wrapper for a DependencyProperty should do its job and nothing more]
    Tuesday, March 23rd 2010

    Tip

    The CLR wrapper for a DependencyProperty should do its job and nothing more

    Explanation

    The CLR wrapper for a Silverlight/WPF DependencyProperty exists purely as a convenience to the developer. The "real" value of a DependencyProperty is stored by the system and accessed by the GetValue and SetValue methods. In fact, parts of the system will only access the value in that manner (possible examples: XAML parser, storyboard animations, etc.). And even if that weren't the case, the fact that the DependencyProperty field is public means that other parts of an application might do so as well (and there is no way of knowing when or stopping them). Therefore, it's not possible to ensure that any custom logic added to the CLR property's set or get wrapper implementation will run every time the DependencyProperty is accessed. Unless you're a fan of inconsistent state, hard to find bugs, or the like, it is typically unwise to violate this convention.

    Good Example

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

    More information

    Tags: Silverlight WPF Development Tips
  • Q: How do you eat an elephant? A: One bite at a time... [Announcing a new "Development Tips" series on my blog!]
    Tuesday, March 23rd 2010

    With all that's going on lately, more and more people are moving their development efforts to Silverlight and WPF. What's nice is that there are already a lot of great resources available to help developers learn the basics of Silverlight and WPF programming. Whether you prefer books, videos, blogs, etc., there's no shortage of material out there to help you get started!

    But what about the next stage? What do you do to learn the finer points of the platform? The subtle nuances? The tricks? The traps??

    One of my goals for this blog is to help intermediate and advanced developers shed their inhibitions and get more intimately involved with the platform. Therefore, many of my posts push the boundaries or do things in ways that might not be completely obvious to a newcomer. But there's another facet to becoming a proficient developer - learning best practices and incorporating them into your daily routine. To that end, I'll be doing a new series of posts tagged "Development Tips"!

    The idea is that each tip will include a short, clear directive, a brief, easy to understand explanation, a simple example, and a few links to more information. Some of the tips are bound to be things just about everyone knows, while others will probably be new to some of you. Some can be found in the documentation for the platform, but others will be simple conventions that have been found to make life easier. And though there are exceptions to every rule, I won't be calling them out because I want to keep the recommendations clear and concise.

    I'm going to try to avoid controversial topics, but it would be silly not to expect some discontent every now and then. :) If you disagree with something I've written, please leave a comment explaining why you disagree what you recommend instead. I'll follow up on comments like that and if there are enough people who call me out on something, I'll revisit the topic in a new post highlighting the controversy. Of course, I'm not claiming that anything I recommend is definitively the best technique! Every situation is different and everyone has their own favorite ways of doing things. Rather, I'd like to share some tips that I've found to work well in my experience - and that seem likely to help others in similar situations.

    Okay, enough boring background already - the next post will be the first of the Development Tips!

     

    PS - That link in the previous sentence takes you to my blog's tag filter for "Development Tips". I'll tag every tip like that so it will be easy to see them all in one place.

    Tags: Development Tips