Managing application resources when WPF is hosted

Recently, I was working on a project that had WPF controls and windows hosted in Windows Media Player. When using WPF styles correctly (themes, resources, etc), normally one would create a resource library with all the styles.

When WPF is hosted, no System.Windows.Application.Current is available, so no application resources are available. Because of this, every window and control has to load the main theme resource dictionary on each load. This isn’t exactly what one would like to do. After searching for a while, I found this interesting article of Dr. WPF. It’s a long article, but the most interested part is to create the current application object yourself:

public static void EnsureApplicationResources()
{
    if (Application.Current == null)
    {
        // create the Application object
        new Application();

        // merge in your application resources
        Application.Current.Resources.MergedDictionaries.Add(
            Application.LoadComponent(
                new Uri("MyLibrary;component/Resources/MyResourceDictionary.xaml",
                UriKind.Relative)) as ResourceDictionary);
    }
}

At first, this seemed to work great in my scenario. However, when a window was shown the 2nd time, the application (in my case, Windows Media Player) crashed. After some investigation, I noticed that the application was not valid anymore after the the first window was shown. This brought me to the idea that the first window that is created is automatically set to the main window of the application. As soon as that window closes, the application gets destroyed. However, I don’t have a main window, I can only show windows as popups, not as main window of the application.

Hereby, I present to you as a reader of my blog, the solution to this issue. Create the first “main application window” yourself and make sure that it is invisible. This window will stay in scope (since Application.Current.MainWindow has a reference) as long as the hosting application is running. The following code should be used instead of the one that Dr. WPF has posted:

/// 
/// Ensures that an application instance exists and the styles are applied to the application.
/// 
public static void EnsureApplicationResources()
{
	// Do we have an application?
	if (Application.Current == null)
	{
		// Create the Application object
		new Application();

		// merge in your application resources
		Application.Current.Resources.MergedDictionaries.Add(Application.LoadComponent(new Uri("/MyLibrary;component/Themes/Generic.xaml", 
			UriKind.RelativeOrAbsolute)) as ResourceDictionary);

		// Create style forwarders
		CatenaLogic.Windows.StyleHelper.CreateStyleForwardersForDefaultStyles(Application.Current.Resources);

		// Create an invisible dummy window to make sure that this is the main window
		Window dummyMainWindow = new Window();
		dummyMainWindow.Visibility = Visibility.Hidden;
	}
}

As you might have noticed, a StyleHelper is used. This is another article, you can find it here.

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Context sensitive help for Windows Forms – part 2

This is an update to the old version of the CatenaLogic.Help namespace.

How to use

  1. Recompile the library from the provided source
  2. Put the HelpComponent on the main form of your application
  3. Run the application with helpprovider commandline parameter
  4. Use Ctrl+F1 to map help topics to controls. Remember about the hierarchy - you do not have to provide topics for all controls, it is sufficient to provide the topic for a container control and all its child controls inherit the mapping.
  5. Remember to provide a help file name (must be a *.chm file located beside the application main executable)
  6. When the mapping is complete, include the help.mapping file stored beside the application main executable as an embedded resource in one of assemblies
  7. Remove the help.mapping file located beside the application main executable
  8. Rebuild the application

Screenshot

help_editor Use of the Help Editor in a real-world application

History

(*) Help file now correctly handles empty keywords (so it is actually possible to remove topics)
(*) Refactored code

Download

CatenaLogic.Help.zip (30.03 kb) [Downloads: 30]

kick it on DotNetKicks.com

 

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Log4net TraceListener

Until now, I used the static Trace class to log information of applications to a log file. However, recently a collegue recommended log4net since it supports formatting options of the log file and to set the maximum filesize of the logfile (the logfiles we received were very big). Also, it supports a lot of trace helper methods I wrote to trace exceptions with information.

However, the whole application has tracecs, and I didn’t want to change all of the calls just for the log4net change. Therefore, I wrote the Log4netTraceListener class to forward all traces to log4net.

This is how to use the class:

1) Add the file to your application or core library if you prefer that.

2) Add the trace listener to the app.config file:

<!-- Diagnostics settings -->
<system.diagnostics>    
    <!-- All your other options here -->
    <trace autoflush="true">   
      <listeners>    
        <add name="Log4netTraceListener" type="CatenaLogic.Diagnostics.Log4netTraceListener, MyAssembly" />
      </listeners>  
    </trace>
</system.diagnostics>

Log4netTraceListener.cs (6.62 kb) [Downloads: 45]

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

First chance exceptions in C++ UI classes

Recently, I was developing a CPropertySheet based application (the application development started in VS 6, but is now done in VS 2010). I got some "first-chance-exceptions" in the SetActivePage method of the PropertySheet.

The documentation of the SetActivePage method already warns about the first chance exceptions that you might get, but in my case, that didn't help solve the exceptions.

After some digging, I noticed that the font of the property pages was not consistent. Older objects had either "MS Sans Serif" or "MS Shell". However, all pages should have "Microsoft Sans Serif".

After changing the fonts of all dialog resources, all exceptions are now history.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

DataObjectBase -> the new Object class for data objects!

Important update!!!

The code posted with this article is version 2.0.0. To downloaded the latest version, check out my article on The CodeProject.

 

Original article:

One of the problems every developer faces with is data persistence. Most software that is written requires the ability to save objects to disk, or serialize them (binary or XML) to use .NET remoting or web services. The .NET framework supports a lot of interfaces to implement this:

But, you also want to be notified when a property of the data object changes, and now you are writing this piece of software, you decide to implement data validation as well. The following .NET interfaces are required:

What happens is that you need to write a lot of (redundant) to support all these interfaces for all your data classes. Then you haven’t even thought of versioning in binary serialized objects that are really difficult to deal with. Also, when (de)serializing objects, you will find yourself write a lot of custom (repetitive) code which actually does the same and is very hard to maintain.

Table of contents

  1. Introduction
  2. Usage examples
  3. Explanation
  4. History
  5. Download

Introduction

Summary of DataObjectBase

The DataObjectBase class is a generic base class that can be used for all your data classes.

  1. Fully serializable
    It is now really easy to store the objects on disk or serialize them into memory, either binary or in XML. The data object supports this out of the box and automatically handles the (de)serialization.
  2. Support property changed notifications
    The class supports the INotifyPropertyChanged notifications so this class can easily be used in WPF and MVC applications to show error messages to the user.
  3. Backwards compatibility
    When serializating your objects binary, it is hard to maintain the right versions. When you add a new property to a binary class, or change a namespace, the object cannot be loaded any longer. The data object base takes care of this issue and supports backwards compatibility.
  4. Error checking
    The class implements IDataErrorInfo so it is possible to validate the data object and check the errors. This way, no custom validation code needs to be written outside the data class.
  5. Backup & revert
    The class implements the IEditableObject interface which makes it possible to create a state of the object. Then all properties can be edited, and finally the changes can be applied or canceled.

To support all this functionality out of the box, the class implements the following interfaces:

Interface Reason
ISerializable Required to make the object serializable.
INotifyPropertyChanged Required to notify other classes of property changes that occur inside the object.
IDataErrorInfo Required to notify other classes about errors in the data object. Very useful in WPF to show errors automatically in the user interface.
IDataWarningInfo Custom interface, similar to IDataErrorInfo, to support warnings in the data object.
IClonable Required to support cloning of the data object.
IEditableObject Required to create states in the data object. Therefore, it is possible to create a state before the user starts editing the object. Then, the changes can be confirmed or canceled.

Requirements

The requirements depend on whether support for collection changes is required. If so, .NET framework 3.0 is required. If not, .NET framework 2.0 is sufficient to use the class.

How to declare the class

The class requires a special declaration. The declaration seems like a lot of code, but a code snippet ‘dataobject’ is included in the package to easily create new objects.

/// <summary>
/// MyFirstObject Data object class which fully supports serialization, property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
[Serializable]
public class MyFirstObject : DataObjectBase
{
    /// <summary>
    /// Initializes a new object from scratch.
    /// </summary>
    public MyFirstObject()
        : base() { }

    /// <summary>
    /// Initializes a new object based on .
    /// </summary>
    ///  that contains the information.
    /// .
    public MyFirstObject(SerializationInfo info, StreamingContext context)
        : base(info, context) { }
}

How to declare properties

A data class without properties is like a bike without wheels. You can sit on it, but you can never ride the thing. So, it is very important to declare properties. However, since the class uses a special (de)serialization of the properties, a special declaration is required.

To declare a property, use the included code snippet ‘propdata’.

/// <summary>
/// Gets or sets the value of MyFirstProperty.
/// </summary>
public string MyFirstProperty
{
	get { return GetValue(MyFirstPropertyProperty); }
	set { SetValue(MyFirstPropertyProperty, value); }
}

/// <summary>
/// Register the property so it is known in the class.
/// </summary>
public readonly PropertyData MyFirstPropertyProperty = RegisterProperty("MyFirstProperty", typeof(string), string.Empty);

As you can see, a special wrapper for the property is created that uses the GetValue and SetValue methods of the base class. The most important part of the property definition is the call to RegisterProperty. The first argument is the name of the property, the second argument is the type of the property, the third argument is the default value, in case the property is not yet available or cannot be deserialized.

Usage examples

All the examples listed below are available as code inside the downloadable package included in this article. Most of the examples have a real-life example which can be started to show that it actually works :)

Example 01 – Simple object

This example shows how to create a basic object that can be used as a “normal” CLR object:

/// <summary>
/// SimpleObject Data object class which fully supports serialization, property changed notifications,
/// backwards compatibility and error checking.
/// </summary>
[Serializable]
public class SimpleObject : DataObjectBase
{
    #region Variables
    #endregion

    #region Constructor & destructor
    /// <summary>
    /// Initializes a new object from scratch.
    /// </summary>
    public SimpleObject()
        : base() { }

    /// <summary>
    /// Initializes a new object based on .
    /// </summary>
    ///  that contains the information.
    /// .
    public SimpleObject(SerializationInfo info, StreamingContext context)
        : base(info, context) { }
    #endregion

    #region Properties
    /// <summary>
    /// Gets or sets the simple property.
    /// </summary>
    public string SimpleProperty
    {
        get { return GetValue(SimplePropertyProperty); }
        set { SetValue(SimplePropertyProperty, value); }
    }

    /// <summary>
    /// Register the property so it is known in the class.
    /// </summary>
    public readonly PropertyData SimplePropertyProperty = RegisterProperty("SimpleProperty", typeof(string), string.Empty);
    #endregion

    #region Methods
    #endregion
}

Example 02 – Error validation

This example shows how to implement validation into the object. Validation will prevent objects that are not valid to be stored (invalid objects cannot be serialized). This example shows an object using WPF, which can show the current errors in the object real-time to the end-user.

Objects have 2 types of validation results. There can either be errors that prevent the object from being serialized, and warnings, that are just warnings so the object can still be serialized.

To enable validation, you must override at least one of the following methods:

/// <summary>
/// Validates the fields.
/// </summary>
protected override void ValidateFields()
{
    // Validate for warnings
    if (string.IsNullOrEmpty(Recommended)) SetFieldWarning(RecommendedProperty, "The recommended property is missing!");

    // Validate for errors
    if (string.IsNullOrEmpty(Required)) SetFieldError(RequiredProperty, "The required property is missing!");
}

/// <summary>
/// Validates the business rules.
/// </summary>
protected override void ValidateBusinessRules()
{
    // Check for fields
    if (string.IsNullOrEmpty(Recommended) && string.IsNullOrEmpty(Required))
    {
        SetBusinessRuleError("A combination of field data is not valid, make sure to enter at least one field!");
    }
}

The validation in WPF will result in the following output:

Example 03 – Serialization

This example shows how to use the Save and Load methods to serialize/deserialize objects to/from disk. The object itself can also directly serialize/deserialize into memory, but this is not shown in the example.

To save an object, call the Save method on the object itself:

myObject.Save("MyFile.dob", SerializationMode.Binary);

To load an object, call the static Load method on the class to load:

SerializableObject myObject = SerializableObject.Load("MyFile.dob", SerializationMode.Binary);

Example 04 – Nested objects

The class is perfectly capable of nesting. This means that you can use objects, that are based on DataObjectBase, can contain other classes also based on DataObjectBase. This creates the ability to create parent/child objects where the whole tree is fully serializable. Nothing of interest to show here, just look into the example if you need more information on this topic.

Example 05 – Backward compatibility

This example shows how an “old” (standard .NET) data class that uses custom binary serialization can easily be converted to a DataObjectBase to use the DataObjectBase even for all your existing classes.

Declare a new DataObjectBase class (remember the ‘dataobject’ code snippet). If the new class is in a new assembly, or has a new name or namespace, use the RedirectType attribute to let the DataObjectBase know that when it finds the old type, it should deserialize that type into the new type.

Then, by default, the DataObjectBase class will try to deserialize the old object. If it fails to do so, it will fall back on the default values provided by the property declarations. However, it is also possible to override the GetDataFromSerializationInfo method:

/// <summary>
/// Retrieves the actual data from the serialization info.
/// </summary>
/// .
/// <remarks>
/// This method should only be implemented if backwards compatibility should be implemented for
/// a class that did not previously implement the DataObjectBase class.
/// </remarks>
protected override void GetDataFromSerializationInfo(SerializationInfo info)
{
    // Check if deserialization succeeded
    if (DeserializationSucceeded) return;

    // Deserialization did not succeed for any reason, so retrieve the values manually
    // Luckily there is a helper class (SerializationHelper) that eases the deserialization of "old" style objects
    FirstName = SerializationHelper.GetString(info, "FirstName", FirstNameProperty.GetDefaultValue());
    LastName = SerializationHelper.GetString(info, "LastName", LastNameProperty.GetDefaultValue());
}

Example 06 - Revert and apply

This example shows how to create a state for an object. This is extremely useful when an object is used in an edit mode. Just before the window is shown, a new state is created. Then, when the user clicks the OK button, the new state is applied. When the user clicks the Cancel button, the changes are cleared by reverting to the state created when the window just opened.

To create a state, use the following code:

myObject.BeginEdit();

To apply all changes made to the object since the last state, use the following code:

myObject.EndEdit();

To revert all changes made to the object since the last state, use the following code:

myObject.CancelEdit();

Explanation

How does it all work in the background? Well, the idea is pretty basic and simple, the way it is implemented is not because of the support for all the features and interfaces. The DataObjectBase class holds a dictionary of registered properties, and the current values for the properties. Then, the GetValue and SetValue methods can be used to get or set the values inside the dictionary.

The biggest issue (actually, it was a challenge, not an issue) was to deserialize all the data automatically, especially with different version numbers of assemblies or renamed types. When deserializing binary objects, the .NET framework is very, very strict and even the slightest change in the class or assembly will completely ruin the deserialization and thus the data of the user.

The DataObjectClass solves this in 2 ways. First of all, it checks for the RedirectType attribute to see if any types should be redirected to new types. Then, it initializes a custom SerializationBinder to redirect all the types. This solves the renaming of types, and can also take care of versioning of the types.

However, there are still problems left. When deserializing, you never know when your object (and its child objects) are completely deserialized. To solve this problem, the OnDeserialized attribute is implemented as well as the IDeserializationCallback interface. Using a single one of these will not solve the problem, but using both together has solved the problem.

History

2010/04/29 - First good release with real-life examples

Download

DataObjectBase.zip (235.26 kb) [Downloads: 117]

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList