Nerdy tidbits from my life as a software engineer

Thursday, June 26, 2008

When Widths Are NaN's

There must be an easy solution to this. I want to receive an event on a window when every child within that window has had its dimensions set. I cannot seem to figure out how to do this. The Loaded event doesn't work; everything still has its width set to NaN (very helpful, of course). Initialized doesn't work, either (also the NaN problem). Neither does the Activated event. Or the ContentRendered, LayoutUpdated, and SizeChanged events. In short, I cannot, for the life of me, figure out how to get notified once all of a windows' children's sizes have been determined. Is that such a crazy thing to expect?

There must be something small and dumb that I'm missing. But it seems like no matter where I put these events (IE, listen for SizeChanged on some other XAML element), at some point, one or all of the items in the window are not sized. It's starting to look like the only way I can solve this problem is to subclass a Grid, or some whacky solution like that, in order to override ArrangeOverride and get notified when children are rearranged. That just seems so crazy to me, though. Isn't there a better way?

Tuesday, June 17, 2008

Configuration Files and Bindings Made Easy

I ran into a pretty good article on the code project about the mysteries of configuration files in the .NET 2.0 framework the other day. At my previous company, configuration file management was one of the most difficult parts of system deployment. Every system had their own configuration section, and every section had to be copied into a master web.config with just the right parameters or else that particular system would fail. But custom configuration sections make life a lot easier. And, as you'll discover in a minute, a simple implementation of INotifyPropertyChanged allows you to bind configuration values directly to items in XAML and get updated when changes occur, which makes life extremely easy.

So, first, here's an example app.config with a custom section:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="displaySettings"
type="MJB.Examples.Configuration.DisplayConfiguration, MJB.Examples"/>
</configSections>
<displaySettings BorderThickness="2" />
</configuration>

The System.Configuration.ConfigurationManager class parses this configuration file and uses reflection to deserialize an instance of the MJB.Examples.Configuration.DisplayConfiguration class using the xml element called displaySettings. The DisplaySettings class extends System.Configuration.ConfigurationSection. It's public properties can be marked using attributes to tell the configuration API to serialize or deserialize that property in the configuration section. Here's what the configuration section looks like:

/// <summary>
/// A configuration section for display settings.
/// </summary>
public class DisplayConfiguration : ConfigurationSection
{
    /// <summary>
    /// Default constructor for DisplayConfiguration
    /// </summary>
    public DisplayConfiguration()
    {
    }

    [ConfigurationProperty("BorderThickness",
        DefaultValue = 2,
        IsRequired = true)]
    public virtual int BorderThickness
    {
        get
        {
            return Convert.ToInt32(this["BorderThickness"]);
        }
        set
        {
            this["BorderThickness"] = value;
        }
    }
}

So now, the configuration section can be access using a simple xpath-like statement:

DisplayConfiguration displayConfiguration;
string path = "displaySettings";
displayConfiguration = ConfigurationManager.GetSection(path) as DisplayConfiguration;
...

And there we go! We now have strongly-type configuration sections, separated into customizable and logical sections. And, if we implement the INotifyPropertyChanged interface, we can use some fun data binding techniques to bind properties to configuration values directly in XAML code:

/// <summary>
/// A configuration section for display settings.
/// </summary>
public class DisplayConfiguration : ConfigurationSection, INotifyPropertyChanged
{

    /// <summary>
    /// Called when a property on the configuration section changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Default constructor for DisplayConfiguration
    /// </summary>
    public DisplayConfiguration()
    {
    }

    [ConfigurationProperty("BorderThickness",
        DefaultValue = 2,
        IsRequired = true)]
    public virtual int BorderThickness
    {
        get
        {
            return Convert.ToInt32(this["BorderThickness"]);
        }
        set
        {
            this["BorderThickness"] = value;
            FirePropertyChangedEvent("BorderThickness");
        }
    }

    /// <summary>
    /// Fires the property changed event in the event that 
    /// there is a subscriber listening to our changes.
    /// </summary>
    /// <param name="propertyName"></param>
    public void FirePropertyChangedEvent(string propertyName)
    {
        if(this.PropertyChanged != null)
        {
            this.PropertyChanged(this, 
               new PropertyChangedEventArgs(propertyName));
        }
    }

}

Now comes a question: how do we get this configuration section into our XAML as a resource object? The answer is to have static copies of the configuration sections in a repository, with a bunch of 'proxy' instances which reference the static configuration section objects and make them accessible to declarative code. First, lets take a look at the static repository:

/// <summary>
/// This is a singleton class that loads and stores all configuration values at startup.
/// </summary>
public class ConfigurationValuesRepository
{
    /// <summary>
    /// This is the one and only instance of the repository.
    /// </summary>
    private static ConfigurationValuesRepository mInstance;

    /// <summary>
    /// This is the display configuration.
    /// </summary>
    private DisplayConfiguration mDisplayConfiguration;

    /// <summary>
    /// The static constructor creates the repository.
    /// </summary>
    static ConfigurationValuesRepository()
    {
        mInstance = new ConfigurationValuesRepository();
    }

    /// <summary>
    /// Creates the configuration values repository.  If the config file can't be loaded,
    /// then the configuration values will stay with their default values.
    /// </summary>
    private ConfigurationValuesRepository()
    {
          string path = "MJB.Examples\displaySettings";
          mDisplayConfiguration = ConfigurationManager.GetSection(path) 
              as DisplayConfiguration;
    }

    /// <summary>
    /// Returns the display configuration.
    /// </summary>
    public static DisplayConfiguration DisplayConfiguration
    {
        get
        {
            return mInstance.mDisplayConfiguration;
        }
    }
    
}

The reason we can't declare this directly in XAML is because it's a singleton, which means it has a private constructor. Another approach would have been to make the mDisplayConfiguration a static member of the following class, but I felt this solution was a bit more elegant. At least that's my thoughts on it for the moment. Either way will work, however, so pick and choose as you wish:

/// <summary>
/// This dependency object allows configuration values to be bound to controls in XAML.
/// </summary>
public class ConfigurationValuesProxy : DependencyObject
{
    /// <summary>
    /// Defines the NetworkConfigProperty dependency property.
    /// </summary>
    public static readonly DependencyProperty DisplayConfigProperty = 
        DependencyProperty.Register("DisplayConfig",
            typeof(DisplayConfiguration),
            typeof(ConfigurationValuesProxy));        

    /// <summary>
    /// Creates the proxy object, which initializes itself from 
    /// the configuration values repository object.
    /// </summary>
    public ConfigurationValuesProxy()
    {
        this.DisplayConfig = ConfigurationValuesRepository.DisplayConfiguration;
    }

    /// <summary>
    /// Gets / sets the NetworkConfig property.
    /// </summary>
    public virtual DisplayConfiguration DisplayConfig
    {
        get
        {
            return (DisplayConfiguration)GetValue(DisplayConfigProperty);
        }
        set
        {
            SetValue(DisplayConfigProperty, value);
        }
    }

}

Now, we can bind control values directly to the values of sections in the app.config. And, if we make any changes to the configuration sections values, we can get the UI to automatically update itself. See how everything comes together?

<Window x:Class="MJB.Examples.Windows.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:conf="clr-namespace:MJB.Examples.Configuration">

    <Window.Resources>
        <conf:ConfigurationValuesProxy x:Key="mProxy" />
    </Window.Resources>
    
    <Grid>
        ...
        <Border Grid.Column="0"
                Grid.Row="3"
                BorderThickness="{Binding DisplayConfig.BorderThickness
                                  Source={StaticResource mProxy}}">
            ...
        </Border>
        ...
    </Grid>
</Window> 

Life is fun with simple configuration files that support data binding, don't you think?