Log window from scratch
Hello again! I was waiting for you to continue with our Log Window!
In the last post we created the basic UI and functionality. However, that window does little by itself so we want to convert it into a component we can plug into other projects. That way, we could focus on building a nice project with the help of a logging window!
Grabbed a drink? Then we’re ready to start!
The host program
We said we wanted to use the window from another project, right? Why don’t we start building that first?
Let’s add a new C# project into our solution. This time, we don’t want a
WPF Application but a
Console Application. Let’s give it a meaningful name… like
We’ll have a new project with a new autogenerated Program.cs file. Let’s keep it and modify its
Main to something familiar:
Now, set this project as the startup project: one way is to right-click on the project then select Set as StartUp Project. Then run the solution and you’ll have the expected message in the console. Nothing new here.
What if we wanted to use the
LogEntry class we created in our
LogWindowUI project? Can we do this?
Unfortunately we can’t… yet.
HostProgram to be able to use classes from the
LogWindowUI project we need to connect them somehow. To do so, expand the
HostProgram project and right-click
Add Reference.... Now enable the checkbox next to the
LogWindowUI project and accept the dialog.
Our code still doesn’t compile, but now we can add this line:
Because, if you remember, our
LogEntry class was defined inside a namespace called
We can happily run our solution now and the formatted log message will show up in the console. We’re getting there!
Breaking it up
Okay, we’ve connected our projects but our
LogWindowUI one is still a
Windows Application and we want to make it a
Class Library. We’ll do that now, but first you should know everything will be broken until we complete some steps.
Start by opening the Properties page in the project (one way is double-clicking the
Properties entry when you expand the project). Select
Class Library from the Output type control, then save.
You will now see several errors in the Error List panel. The first one says:
Library project file cannot specify ApplicationDefinition element.
Do you remember, from previous post, that the startup of the UI felt like Magic because we apparently never said where it should start? Remember the two concepts we discussed: Application and Window?
One of the files,
App.xaml, defines the entry point and which Window to display when we launch the program. Because we’ll be using this project as a
Class Library, the host program will be responsible of managing the life cycle itself. We’ll just provide tools for it to work. So, delete
App.xaml and the error will go away.
Except not! It looks like it has deleted
App.xaml.cs as well and we don’t have an Application anymore!
Let’s add a new class called
App in our
LogWindowUI project with this code, pretty similar to what
With this, all errors will go away and we can run our console-based hello log. Impressive :)
But, how do we connect them now?
Showing window from host program
So far we’ve gained access to the classes in the
LogWindowUI project from the
HostProgram one. However, we’re left with a sad log message in the console and no WPF window.
To keep things clean, we should expose some class that lets us create the window, send it messages and close it when necessary instead of dealing with the internal classes from the outside. We only want to have one window at the same time, so… guess which design pattern we’ll be using?
You may have heard of the Singleton pattern before. It allows us to restrict the instantiation of a class to a single object. It’s used when it doesn’t make sense to have several instances of one class, commonly when you’re defining a manager of some sorts or a wrapper to an underlying system which should have a single entry point.
A simple C# implementation of the pattern is this one:
However, this lazy initialization (the instance isn’t created until the first piece of code accesses it by using the
Instance property) takes life cycle control from us (and isn’t thread safe). We’d want to explicitly control when the instance is created and when it’s destroyed. Let’s make it so:
This way, we control the life cycle ourselves. It isn’t thread safe either, but we’ll be sure to
Destroy in a single-thread part of the program. Let’s also add a new method to test it:
Calling from host
For now, our host program just has a
Main function from which we’ll call everything:
And it works as expected, yay! Now, let’s show the window again!
STA, Application and Window
App.xaml file we had before we broke everything? If we stripped it a bit we’d have this:
Which is telling the runtime to use the
LogWindowUI.App class as the Application and then display the Window defined at
x:Class attribute points to
So it looks like we need an Application that displays a Window. Okay, what else?
In the previous post we mentioned WPF uses the
Single Thread Apartment model: any object created within a thread can only be modified from that thread (the UI Thread). Guess we need one of those too.
We’ve defined our
LoggerUI singleton class as the entry point to our WPF library. We could have the Application as a member of
LoggerUI and tie its creation to the creation of the singleton itself. Something like this:
Basically, just what we said we’d do. But there’s a catch.
When we run this code, it’s not guaranteed the Application and its Window are running after we call
LoggerUI.Initialize() (which ends up calling the constructor and thus this code). If we were to start logging stuff right after calling
Initialize it could fail because
m_application could be uninitialized. Ah, the joy of multithreading.
One way of solving this issue is to wait for the Application to be ready. After all, it’s part of the initialization process. We can use an
AutoResetEvent to wait in our main thread until the Application and Window are running. We could rewrite the constructor with this code:
Note how we’ve separated the Window creation from the Application’s
Run method. When you create a Window it starts hidden, so we must call
Show ourselves or let
Run do it for us. Oh, and
Run blocks until the Window is closed, so we must signal the event before doing it. The Application is already created by then so we can start manipulating it.
If we were to run the program now we’d see something like this:
All that’s left is sending messages from the host program to our WPF library instead of mocking them!
As you can see in the previous picture, we had a way of adding messages to the window. For testing purposes we did that in the
MainWindow’s constructor. Let’s delete that code now, so there’s nothing sending messages.
Now, in the
LoggerUI singleton class, let’s update the
Did you notice the
Dispatcher.BeginInvoke thing we mentioned in the previous post?
MainWindow.AddLogEntry like this:
Our current code won’t compile if we do this, because in our
Main function we’re still using the old version of
Add. Let’s fix it:
Run it and we have this:
Or we could do this as well:
Yay! It works! Looks like we did it, awesome job!
In this post we’ve managed to convert our WPF project into a
Class Library we can plug into other projects and log whatever we want from that project. We’ve also defined a Singleton entry point for the library and created the UI Thread in which the Application and Window live.
In the next entry we’ll work on adding some extra functionality to the window, like colors, filters and auto-scroll.
See you then!