Mono winforms on linux

WinForms

Quick Resources

Code Status

Support for Windows Forms 2.0 is complete. At this point, we are largely just fixing bugs and polishing our code.

About System.Windows.Forms

System.Windows.Forms (aka Managed.Windows.Forms, MWF, Winforms) is one of the many GUI Toolkits for use with Mono and is compatible with Microsoft’s System.Windows.Forms. Support for Winforms 1.1 and 2.0 has been completed, and is now in a maintenance/bug fixing state.

System.Windows.Forms in Mono is implemented using System.Drawing. All controls are natively drawn through System.Drawing. System.Windows.Forms implements its own driver interface to communicate with the host OS windowing system. Currently, we have drivers for X11, Win32, and macOS. These drivers translate the native window messages into WndProc compatible messages, to provide as much compatibility with native .Net as possible.

In terms of integrating visually with the desktop, we currently ship with a classic Win32 theme.

Contributing

Currently, all of the controls are pretty much complete. We are now mainly fixing bugs in our implementation. See Bugs.

If you would like to contribute, you can:

  • If you find a bug in one of our methods, you can file a bug with a small test case.
  • You can fix a bug in one of our methods, either that you found or from our bugs page.

Please note there are a few rules for contributing to Winforms:

  • The general rules from Contributing, especially the Special Notes at the bottom.
  • Controls must be drawn using System.Drawing code.
  • Drawing code must be tied into the theming interface.
  • If your method can be tested by an automated NUnit test, please include a test as well.

If you are working on a large feature, you may wish to post your intent on GitHub to prevent duplication of effort.

Drivers

There is a per-windowing system driver which performs the following tasks:

  • Keyboard input
  • Window creation
  • Event translation

Mono’s Windows.Forms implementation translates the native system events such as X11 into Win32 WM_ messages and dispatches them using the WndProc mechanism. This allows applications that depend on overriding WndProc to get some features not exposed by the APIs to still run.

Accessibility

Currently Windows.Forms does not support accessibility, this is being worked on in a separate project that started in January of 2008. See the Accessibility page for details about how this will integrate into the system.

Sample Code

Sample applications for Mono’s Windows.Forms are available in winforms on github

Theming

This section discusses the theming issues in Mono’s implementation of Windows Forms as these questions are asked frequently:

Why Not Use Native Widgets?

It is not feasible to use native widgets on all operating systems. Each OS/Windowing system has different behavior/properties/features for what on the surface looks like the same widget. A RadioButton in Gnome is different from a RadioButton in Win32, which is different from a RadioButton in macOS. To use the native widgets means to reduce the functionality of MWF to the least common denominator of all supported operating systems. If we were designing our own GUI toolkit, this might even be acceptable, however we are implementing an already defined API with defined behavior (and even with application relied-upon side-effects). A RadioButton has to behave exactly like it behaves on Win32 with MS.Net, or else applications written for it may not work properly anymore. And that’s the whole point of Winforms: to allow existing .Net SWF apps to run on Mono. For other uses, there are other choices that may be more appropriate, such as Gtk#.

Fitting In

Although we cannot use native widgets, we still would like to make a reasonable effort to achieve a more native “look and feel” on different platforms. To accomplish this, we have created a theming interface with the hope that in the future it will facilitate our controls looking more native. Having a separate theme class from the get-go forces those who are writing the controls to design code where the drawing code is not integrated and intermingled with the control code.

Maturity of the Theming Interface

Currently, we have the default managed Win32 Classic theme and a native Windows VisualStyles theme. We would like to have native theming on Linux and macOS.

The best option we currently have for theming is the VisualStyles theme. On Windows, this calls the native Windows API and allows it to draw natively for us. Unfortunately, the System.Windows.Forms.VisualStyles namespace is not currently implemented on Linux or macOS. The best way forward would be to implement this namespace on those platforms. (Alternatively, one could port Wine’s uxtheme.dll and we could ship Clearlooks and Aqua themes that people have done.)

Читайте также:  Драйвер для принтера dsp 1512r

The VisualStyles namespace allows you to focus simply on drawing one element, like a “selected button” instead of having to try to sift through the theming code to find out how it draws a button.

History

There were two previous attempts to implement System.Windows.Forms in Mono. These initial attempts were done on top of other toolkits: first Gtk, then Wine. Each one had its share of problems, causing it to be abandoned.

The Gtk effort had the following problems:

  • Mapping one toolkit’s semantics to another was very hard.
  • It would be very cumbersome to map all events into WndProc events, the messaging system at the core of Windows.Forms.
  • The development effort happened early in the Mono life, with this very sporadic development effort, the code quickly became obsolete or bit-rotted.
  • Using this on other systems required a Gtk+ installation on the target system (macOS and embedded devices would have suffered the most).

The Wine effort could have been a successful approach, however there were several technical obstacles:

  • Multithreading support. Wine has a specific thread setup that is not compatible with Mono. While this was solvable, it would have required larger patches to Wine.
  • Dependencies. To have Mono and Wine interoperate, a glue library was required. This library required to know where Wine was installed to (we had a Wine patch that eased this requirement, but it was not accepted into Wine).
  • Moving target. Key functions in Wine changed often enough that with every Wine release we had to start over making our glue code work again, sometimes becoming incompatible with previous Wine versions.
  • Wine/Gdi+ interactions: The way System.Drawing had to be made compatible with Wine was extremely inefficient. Wine uses the GDI model (raster painting), while our System.Drawing implementation, built on top of Cairo, uses the GDI+ model (compositing model). Making them both talk was extremely inefficient as every transition from one model to the other required the bitmaps to be copied.

The current approach is to implement all controls fully in managed code, and uses an abstract theme interface to paint the widgets. The default theme interfaces renders the widgets using System.Drawing.

Webbrowser Control

Information about the WebBrowser control can be found on the WebBrowser page.

Источник

Porting Winforms Applications

by Jonathan Pobst

The amount of effort required to get an existing Winforms app running on Mono can vary greatly. Although many small apps will run on Mono unmodified, many apps will require some work on the developer’s part to run smoothly on Mono. This guide will attempt to port a non-trivial open source application to document several of the issues a developer may run into while porting their app to Mono.

The Project

For this guide, the open source application NClass has been chosen. It is a UML compliant class designer that looks very much like the one that ships in Visual Studio 2005. It is licensed under the GPL and LGPL, and has not been written with Mono in mind. We will be using Mono 1.2.4 to port NClass to run on Linux.

NClass running on .NET with included example file:

Getting Started

For the purpose of this guide, we will make the assumption that the developer is more familiar with Windows and Visual Studio than the Linux counterparts, given that the app is originally a Windows app.

Because we grabbed the source version, first we need to load up NClass.sln in Visual Studio and compile the solution.

Once we have a compiled executable, we can run MoMA on it to give us an idea of what kinds of issues we may run into.

The results from the MoMA scan can be seen here.

MoMA Summary

Methods we are using that are not in Mono 1.2.4:

  • void ListView.add_ItemSelectionChanged (ListViewItemSelectionChangedEventHandler)
  • void TreeView.set_ShowNodeToolTips (bool)
  • void TreeNode.set_ToolTipText (string)
  • void PrintDialog.set_UseEXDialog (bool)

Methods that are marked with a [MonoTODO] attribute in Mono.1.2.4:

  • void Control.set_AutoSize (bool) — This method currently does nothing
  • void ContainerControl.set_AutoScaleMode (AutoScaleMode) — Call scaling method
  • void ComboBox.set_AutoCompleteMode (AutoCompleteMode) — AutoCompletion algorithm is currently not implemented.
  • void ComboBox.set_AutoCompleteSource (AutoCompleteSource) — AutoCompletion algorithm is currently not implemented.

Looking at the report, we can immediately see several places we will most likely need to make adjustments for our program to work.

The TODOs look pretty harmless, we can infer that if we rely on AutoSize, it’s probably not going to work, and we are not going to have auto completion for our ComboBox. Most of these missing methods won’t be too big of a problem either once we work around them. We can expect that we will not have ToolTips in our TreeView, and we can’t set our PrintDialog to use the Windows XP dialog. The missing event ListView.ItemSelectionChanged however will likely be a problem. Because that event does not exist, it will not get fired, so whatever code we are doing in the event handler will not get called.

Читайте также:  Как снять показания счетчика на принтере

Trying It Out

Even though MoMA shows there could be problems running the application, sometimes those problems aren’t hit until certain features of the program are executed. So let’s try to run the application.

Open “Mono-1.2.4 Command Prompt” that was created in the start menu by the Mono installer. Go to the directory that contains the executable “NClass.NClass.exe”, and try running it with Mono using:

Unfortunately, nothing seemed to happen.

What actually happened is that the application ran, but hit an error and quit. To see the error, we need to redirect the error output to a file, so we run:

Looking at the log that was created, we see this error:

This isn’t really unexpected. It is telling us that we tried to set the property PrintDialog.UseEXDialog, which MoMA already told us didn’t exist in Mono. So it’s time to start porting our source code to work around these issues.

Porting Strategies

There are several approaches to porting code, depending on your goals.

  • The unsupported code can simply be removed or commented out if it is not needed.
  • Compiler conditional directives (#if) can be used to create separate executables for .NET and Mono.
  • The runtime (.NET or Mono) can be detected and use different code.
  • The code can be rewritten to use supported methods in Mono.

Here is the section of code that is setting PrintDialog.UseEXDialog in MainForm.Designer.cs:

Remove / Comment Code

If the code isn’t really needed, the easiest thing to do is to remove it:

or comment it out:

However, there will be plenty of cases when this is not feasible.

Compiler Conditionals

Another strategy is to create different assemblies for .NET and Mono. This can be done by wrapping all code that is not supported by Mono in conditional directives and compiling with and without the directive.

When compiling the version for Mono, we would specify the flag MONO in the Build tab of the GUI project properties.

The downside of this approach is that we must ship separate assemblies for different platforms.

Runtime Conditionals

In order to have only one assembly for all platforms, but run different code on different platforms, we can detect at runtime whether we are using Mono or .NET. First, create a function to detect if the assembly is running on Mono (from the Technical FAQ):

Then use the function to determine which code to run:

Notice that we moved the code into a separate function, as this will prevent the JIT engine from throwing an exception as it JITs the code and determines that the method did not exist. By moving the code into a different method we prevent the JIT engine from hitting it.

Rewriting Code

Sometimes the best option is to simply rewrite the problem code in a way that is supported by Mono. For example, MoMA told us that this application uses the ListView.ItemSelectionChanged event, which is not implemented in Mono. However, the event ListView.SelectedIndexChanged event is implemented, and could be used for the same purpose.

Here is the problem code:

Which can be modified to:

We then modify lstItems_ItemSelectionChanged from:

When possible (which isn’t always the case), this method is the cleanest, as it uses the same code and provides the same functionality on both the Mono and .NET runtime.

Porting NClass

For our NClass application, we will comment out PrintDialog.UseEXDialog, TreeView.ShowNodeToolTips, and TreeNode.ToolTipText. We will rewrite both occurrences of ListView.ItemSelectionChanged.

MainForm.Designer.cs — Line 1196

TreeDialog.Designer.cs — Line 66

TreeDialog.cs — Line 74

MembersDialog.designer.cs — Line 238

Rewriting the listMembers_ItemSelectionChanged method takes a little bit more effort. One way is to change it from:

Also, make the changes to lstItems.ItemSelectionChanged and lstItems_ItemSelectionChanged outlined in the “Rewriting Code” section above.

With these changes made, rebuild the solution in Visual Studio. Then run the NClass.NClass.exe executable again. This time we get much better results:

Loading up the example file:

Class members dialog:

Continuing the Port

Now that the application runs, you have a good starting point to work from. However, it needs to be thoroughly tested for issues that MoMA can’t find. When exercising the application’s code, it is possible that you will run into things that do not function as expected or the application may crash unexpectedly. If these issues are bugs in Mono, please file the bug with a small test case (not your entire application) so we can fix them. See Bugs for details. Other issues may just be poorly written code that can be fixed by rewriting it to be more robust and error-proof.

Читайте также:  Драйвер для принтера лбр 2900

There are still many issues with the NClass port, but for the sake of brevity, we will not attempt to fix them in this guide.

Running on Linux

Ultimately, the goal is to run the application on Linux, so let’s look at running NClass on Linux. If you already have a Linux installation, you can look for Mono packages in your distro’s package repositories.

If they are not there or are out of date, there are packages for many popular distros on Mono’s Downloads page. If you cannot find pre-made packages, you can also compile from the source code. Instructions are available here.

If you do not have a Linux installation, another option is to run the openSUSE/Mono VMWare image. This is the method we will use, which requires the following:

After installing the VMWare player and unzipping the VMWare image, start the Mono image. It should bring you to the openSUSE desktop:

Enabling User Shares

To transfer the application to the Mono image, we need to create a NClass directory and turn on directory sharing.

  • Right click the desktop, choose Create Directory, name the directory NClass.
  • Click the “Computer” menu in the bottom left, choose Control Center.
  • Choose “YaST” at the bottom, the password is “mono”.
  • Choose “Network Services”, then “Samba Server”.
  • Check “Allow Users to Share Their Directories” on the “Shares” tab.
  • Choose “Finish”.
  • Back on the desktop, Right click the “NClass” folder you created and choose “Sharing Options”.
  • Check “Share this folder” and “Allow other people to write to this folder”.
  • Click “Modify Share”.

Now go back to Windows, and access your new share by typing “\\mono\nclass” into the Run dialog. Copy your NClass project to the Mono image share.

On the Mono image desktop, double click your NClass folder, and navigate to your compiled NClass.NClass.exe application. Double click it and the application should run.

Linux Platform Differences

There are two classes of issues you may run into during your port. The first class are issues stemming from differences between the .NET and Mono runtime. These can be worked out while still on Windows as shown above. However, there is a second class of issues that you may come across. These are issues stemming from differences between Windows and Linux (or macOS, etc.).

Common OS Porting Issues

  • Use of Win32 P/Invokes
  • Case sensitivity in file names.
  • Use of “\” in paths.

As an example, lets say you have a subdirectory in your application called “Sounds” that contains the file “Finished.wav”. To play this sound, you have the following function:

This function exhibits all three mentioned porting issues.

Mono has a special execution mode that will help you get your port moving faster (if you do not want to change the filename casing or path separators, see the IOMap page for details.

Win32 P/Invokes

The PlaySound function is declared in the Win32 API dll “winmm.dll”, which does not exist on Linux. There are two ways around this. One is to find the equivalent function in your target platform’s API, and use runtime detection to determine which API to call. If you are lucky, the easy way is to replace your API call with managed code. In this case, we can use the System.Media.SoundPlayer class to play the sound for us, like this:

Case Sensitivity

Another difference between Windows and many other operating systems such as Linux is that the file system is case sensitive. That is, in Windows the files “readme.txt” and “README.TXT” are the same, but in Linux those are distinct files. Looking at our example, our file is called “Finished.wav”, but we are referencing it in code as “finished.wav”. Although this will work on Windows, it will generate a FileNotFoundException on Linux. So we must be consistent about the case of our file names:

The Path Separator

Another issue you may run across is the path separator (“\”) used in file paths. In many other operating systems, such as Linux, the path separator is a forward slash (“/”) instead of a backwards slash like Windows. In our example, we have hard coded a backwards slash that will cause our file to not be found.

We can correct this using Path.DirectorySeparatorChar:

or using Path.Combine:

Either method will ensure that the correct path separator is used on the correct operating system.

For some more issues you might run into and a more detailed look at options for dealing with them, see Guidelines:Application Portability.

Conclusion

Using this guide, we have managed to port a non-trivial winforms app to start up on Mono on Linux in a couple of hours. Although it is not complete, once the application is starting up on Mono, it’s a good beginning to test and see what issues remain.

Источник

Поделиться с друзьями
КомпСовет
Adblock
detector