Basics

By dlebansais

Introduction

I’ve been looking for quite a while on the Internet for programmers writing WPF applications in Eiffel. There is a lot of literature on WPF (the Windows Presentation Framework), but nothing that would explain how to use the Eiffel language to make one.

So here I come, and I hope this first article will help you getting started if you want to try it.

C# is clearly the best choice for WPF applications, and unless you’re an Eiffel fan there is no good reason to avoid C#. Eiffel is best for writing maintainable and reusable software, use it for the bulk of your application. Use C# for the user interface layer with WPF. However, if the user interface part of your application is simple, you might prefer to use Eiffel for everything.

Getting started

To get started, you’ll need to install EiffelStudio (currently 6.4) and a C complier (EiffelStudio requires one). I selected Visual Studio 2008 because I can create WPF programs in C# and investigate how it works, then try to obtain the same result with EiffelStudio.

You also need to install the .NET framework. I’m on Vista so I don’t really need it, but installed .NET 3.5 SP1 nonetheless.

There are other Eiffel compilers, but I don’t know much about them. If you don’t use EiffelStudio you’re welcome sharing your experience here!

Our first program

First of all, let’s create a WPF “Hello, World!” program to see how it looks. Visual Studio has a wizard for that, just do the following:

  • Open Visual Studio, close any solution that could have been loaded at startup, select the File menu and Choose New / Project.
  • In the Project wizard, select Visual C# on the left and WPF applications on the right. Give your application a name, for instance WpfSample and choose a location, where Visual Studio will create your program. Click OK, after a minute the solution is ready.
  • In the Solution Explorer pane, double click on “Windows1.xaml” to bring up the Xaml designer (if not done automatically already), it should show an empty window in the design pane and the following lines in the window pane:

    Generated XAML

  • Insert the following line into the grid element:

    Inserted label

  • Press F5 to save your work, compile and execute the program. It should pop a window looking like this:

    Fresh "Hello, world!" in C#

Now, our goal is to do the same with EiffelStudio. It’s not an easy task, and while I could just give you the code sample, I will instead show you what happens if we follow the naive approach.

EiffelStudio doesn’t have a wizard for WPF applications, but has one for .NET, so let’s use it.

  • Close the sample, close Visual Studio and open EiffelStudio. If you’re on Vista, I recommend starting it as administrator, at least for the first time, or some objects don’t compile.
  • Select the File menu and New project. You might have the project dialog already up, if so just jump to the next step directly.
  • Select a .Microsoft NET project and click Create.
  • In the wizard, set the application name as WpfSample et select a location where to create the program, click Next.
  • Change the root class name to APP, click Next then Finish.

This will create our sample application. It might take a few minutes if you’re creating a .NET application for the first time. At this stage, our sample cannot use WPF because only the core .NET objects are available in the project. If we want to have access to more, we need to include them, like you would include a library. In the .NET world, this is done by adding “assemblies”, a digest of grouped object interfaces. This step is pretty easy, since the list of assemblies is immediately visible in the project:

Project view

Right-click the assemblies node to bring up a context menu and select “add assemblies”, to pop up the assemblies selection window:

Add assemblies menu

Assemblies list

WPF stands for Windows Presentation Framework. If you look closely to the list you will see the following lines:

  • PresentationBuildTasks
  • PresentationCore
  • PresentationFramework
  • PresentationFrameworkAero
  • PresentationFrameworkClassic
  • PresentationFrameworkLuna
  • PresentationFrameworkRoyale

PresentationFramework is probably the assembly we are looking for, and PresentationFrameworkAero etc. interfaces for specific UI design. Let’s select PresentationFramework and click OK.

Theoretically, this should make the APPLICATION class available to us like it is in C# (identifiers are not case-sensitive in Eiffel). And indeed, the following code now compiles:


class
    APP

inherit
    APPLICATION
        rename
            make as make_app
        end

create
    make

feature {NONE} -- Initialization

    make
	-- Run application.
    do
         make_app
    end

end

However, if we try to execute it we get a “System.InvalidOperationException” that tells us there can be only one instance of the Application class in the same AppDomain.

Now that w have started running into issues with the .NET framework, it’s probably a good time to review the documentation available to us. I found this links: http://docs.eiffel.com/book/platform-specifics/net (a review of the .NET support in EiffelStudio). And there is of course http://msdn.microsoft.com the online documentation for Microsoft products. You can type “WPF <class name> class” in the search engine to easily find information about a class.

The page for the Application class confirms what the exception message states.

“Application implements the singleton pattern to provide shared access to its window, property, and resource scope services. Consequently, only one instance of the Application class can be created per AppDomain.“

After struggling with the environment, I eventually figured that you can’t inherit from APPLICATION. But you can create an instance of that class in your root class and use it as a starting point. Here is the modified code:


class
    APP

create
    make

feature {NONE} -- Initialization

    make
	-- Run application.
    local
        main_app: APPLICATION
    do
        create main_app.make
    end

end

Of course, this code doesn’t do much but at least it works!

Adding a window

Looking closely, the APPLICATION class has a run method taking a window object as argument. If we can create it, there is a good chance it will display the window and return when it’s closed. So, our next step is to create that window object.
First, let’s add a new class to the project like Visual Studio does.


class
    WINDOW1

inherit
    WINDOW
        rename
            make as make_window
        end

create
    make

feature {NONE} -- Initialization

    make
        -- Our window
    do
        make_window
    end

end

And let’s create the window object then call run.


class
    APP

create
    make

feature {NONE} -- Initialization

    make
        -- Run application.
    local
        main_app: APPLICATION
        main_win: WINDOW1
        status: INTEGER
    do
        create main_app.make
        create main_win.mak

        status := main_app.run(main_win)
    end

end

Problem: when the main_win object is created, an exception is thrown: “Declaration referenced in a method implementation can not be a final method ”. A search on the net for this error led me to the following page:
http://support.microsoft.com/kb/315528.

Again, I tried to fix the problem but eventually gave up. I just used a different approach which is to just create an object of the base WINDOW class. Just replace “main_win: WINDOW1” with “main_win: WINDOW”, remove the WINDOW1 class from the project, then recompile and run.

But we get a new exception: “System.InvalidOperationException: The calling thread must be STA ”.

Fixing the threading model

If you didn’t know yet, a thread in the .NET environment can be either in single-threading apartment (STA) mode or multi-threading apartment (MTA) mode. The WPF graphic engine requires STA, and unfortunately EiffelStudio creates the main thread with no apartment mode.

In C#, the problem is easily fixed by adding the [STAThread] attribute to the class. Since doing it in Eiffel is out of our league for now, let’s change the mode in the code before we call the run method:


feature {NONE} -- Initialization

    make
        -- Run application.
    local
        main_app: APPLICATION
        main_win: WINDOW
        status: INTEGER
        main_thread: SYSTEM_THREAD
    do
        create main_app.make
        create main_win.make

        main_thread := {SYSTEM_THREAD}.current_thread
        main_thread.set_apartment_state({APARTMENT_STATE}.sta)

        status := main_app.run(main_win)
    end

end

Curiously, that code doesn’t work, we still get the same exception. It seems that the current thread mode cannot be changed. Fortunately, the current thread has nothing special that forces us to keep it, therefore we can create a new thread with the appropriate mode and use it as our true main thread, like this:


class
	APP
create
	make

feature {NONE} -- Initialization

	make
		-- Run application.
	local
		start: THREAD_START
		thread: SYSTEM_THREAD
	do
		create start.make(current, $ui_thread)
		create thread.make(start)
		thread.set_apartment_state({APARTMENT_STATE}.sta)
		thread.start
	end

	ui_thread
		-- Application main thread
	local
		main_app: APPLICATION
		main_win: WINDOW
		status: INTEGER
	do
		create main_app.make
		create main_win.make

		status := main_app.run(main_win)
	end

end

And it works!

So, what about some XAML now?

Of course, it doesn’t say “Hello, world!” yet. At this stage, we have the option of adding the grid and the label from the code, or do it in some XAML script that the window would load. Only with the second approach would we make a true WPF sample, so we should try that. Fortunately, there yet another option that is more powerful: the XAML_READER class. An instance of this class can load XAML data, parse it and return an object of the type specified in the data. For instance, in the root element in XAML is <window… /> it will return a reference to an object of class WINDOW.

One thing to take into account is that XAML_READER reads streams, so we first need to read the XAML file and turn it into a stream. This can be accomplished with the FILE_STREAM class, obviously (this one didn’t take me long to figure out, fortunately).

Let’s see how it looks:


    local
        fstream: FILE_STREAM
        main_win: WINDOW
    do
        create fstream.make_from_path_and_mode("Window1.xaml", {FILE_MODE}.open)
        fstream.flush
        main_win ?= {XAML_READER}.load(fstream)

So easy… This code reads the same XAML file as the C# file. Here is how the XAML file looks like:


<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns: x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Sample" Width="200" Height="100" >
    <Grid>
        <Label Width="75">Hello, world!</Label>
    </Grid>
</Window>

Interestingly, the same code works for the APPLICATION class. We can create a XAML file looking as below:


<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns: x="http://schemas.microsoft.com/winfx/2006/xaml" >
</Application>

And in the code create the application object the same way we created the window object:


    local
        main_app: APPLICATION
        fstream: FILE_STREAM
    do
        create fstream.make_from_path_and_mode("App.xaml", {FILE_MODE}.open)
        fstream.flush
        main_app ?= {XAML_READER}.load(fstream)

Conclusion

And here is the final code, along with a screenshot of the execution result:


note
    description: "Project root class"

class
    APP

create
    make

feature {NONE} -- Initialization

    make
        -- Run application.
    local
        start: THREAD_START
        thread: SYSTEM_THREAD
    do
        create start.make(current, $ui_thread)
        create thread.make(start)
        thread.set_apartment_state({APARTMENT_STATE}.sta)
        thread.start
    end

    ui_thread
        -- Application main thread
    local
        fstream: FILE_STREAM
        main_app: APPLICATION
        main_win: WINDOW
        status: INTEGER
     do
        create fstream.make_from_path_and_mode("App.xaml", {FILE_MODE}.open)
        fstream.flush
        main_app ?= {XAML_READER}.load(fstream)

        create fstream.make_from_path_and_mode("Window1.xaml", {FILE_MODE}.open)
        fstream.flush
        main_win ?= {XAML_READER}.load(fstream)

        status := main_app.run(main_win)
    end

end

A "Hello, world!" application in Eiffel

To be continued…

In my next posts, I will investigate how to make some progress with WPF:

  • Handling events,
  • Taking XAML data from resources instead of files,
  • Adding attributes to objects,
  • Inheriting from WINDOW and APPLICATION…

and much more!

Leave a Reply