Introduction
Where I worked we had a number of applications that have a lot of projects in common. This includes having a number of forms that are the same. The problem was that the forms were the same, but slightly different (such as icons, titles, etc.). There were also other differences like the storage location of saved files, default file name, and the file extension. Other differences included paths and file extensions.
Normally the Settings file is used to maintain this information, but there are two problems with the settings file: it is an internal class (this can be changed, but somewhat painful), and we did not want to modify the settings file when we were building the different applications. We also would do design using a combined set of projects where we would change the application by changing the startup project. The way we were solving this problem was a lot of custom code that had to figure out the actual application and make the changes. This is not very maintainable.
The solution was to put a static singleton in the common core project, a project shared by all applications, and referenced in all projects that needed access to the file. The trick was to make sure that the location did not cause circular references. Having a good location for this class was initially a problem because the design was not clean. After we fixed this problem, this solution worked like a charm.
The Design
The design centers on a class that contains a number of static properties that have a public Get
and a protected Set
. There is also a static constructor that sets the properties, and the class can be inherited.
public class RuntimeVariables
{
public static string Name { get; protected set; }
static RuntimeVariables()
{
Name = "Test1";
}
}
Here I have only a single static property, Name
, but there can be many more.
Having a static
constructor that sets the properties means that there is a default behavior. This was important for us because we were able minimize the changes for the legacy applications to just referring to the static properties instead of dealing immediately with possibly unknown side effects. The properties have a protected Set
means that these variables cannot be changed except from a derived class.
The way to change these properties is to have a derived class that has a constructor that updates the properties as so:
class RuntimeVariablesDerived : RuntimeVariables
{
public RuntimeVariablesDerived()
{
Name = "Test2";
}
}
To set these application variables, all that is needed for WPF is to have the App
class create an instance of the derived runtime variables class:
public partial class App : Application
{
public App()
{
new RuntimeVariablesDerived();
}
}
Just creating the instance will cause the instance constructor to run, setting all the static properties. Not perfect, but only have to set the values in one place, the derived class. This has reduced the maintenance significantly on our applications, and has made it easier to spin off new versions of the application (of course there were some bad decisions made during the design that made the situation worse than it should have been).
There are a number of variations on how this can be implemented. Since we were working with a legacy application, we used default values, but that may not have been the right choice. I now think that throwing an exception if a value that has not been initialized is useful in at least some cases. This would involve having a bool
static
variable in the base class that is set by the derived class, and then checked before returning any values:
public string ProtectedName
{
get
{
Check();
return _protectedName;
}
protected set { _protectedName = value; }
}
private void Check()
{
if (!ValuesSet)
throw new NotInitiatedException("Application level constants have not been set");
}
The NotInitiatedException
is a new class derived from Exception
.
My implementation is not what was considered kosher by another developer, and he changed the implementation so that there is a method to update the value and not a derived class. This allowed the setter to become private. The method in the base class would look like this:
public void SetName(string name)
{
Name = name;
}
The Sample
The sample included with this article is extremely basic. The window only has a single element:
<TextBlock Text="{Binding Name,Source={StaticResource runtimeVariables}}"
FontSize="14"/>
Without any change you see the value that is provided by the derived class. To see this change, you have to change the code. In the App.xaml.cs, there is the following:
public partial class App : Application
{
public App()
{
new RuntimeVariablesDerived();
}
}
Just comment out “new RuntimeVariablesDerived()
” and you will see the default value in the window. Otherwise the value will be that provided by the derived class.
Conclusion
This is not what I would prefer since I really would like to have something where I could better protect these values (make them read-only), but the protection is not bad, especially since there is no way to change the values through the properties. A nice thing about the design is that a “testing” derived class can be used, and properties can be added to set the application into a better environment for unit testing. This can normally be quite a challenge without proper forethought. What would have been really nice is if the static constructor could be made protected, then it would not be possible to use a static property without there being an instantiated class.