Contents |
This page lists some snippets of Nemerle code you may want to look at. If you have any examples to be submitted here -- please edit this page or contact us.
The tutorials gathered here are meant to be used as an easy introduction to the the language, by stepping through the process of writing small applications.
There is also Grokking Nemerle -- a "book" covering most of the language.
You can find various examples of Nemerle code in snippets directory in Nemerle source tree. You are welcomed to transform them into tutorials. Examples include:
These are also bundled with the source distribution.
There are also several more advanced examples there:
Sokoban -- Sokoban solver by Bartosz Podlejski
Examples done by students during the Nemerle course at the CS Institute in the Wroclaw University.
In June 2004 there was a contest to create finite state automata controlling the ants. Our entry is now available here.
This year's contest page of our team is located here ICFPC2005.
These are the few examples ported from the Win32 Computer Language Shootout. We didn't submit them yet, looking for volunteers to port more. When we have enough examples, we will also send them to the recently linked renewed shootout.
This simple program will trace memory leaks in programs written in C. By Jacek Śliwerski.
A simplistic HTTP server. The main idea behind it is to serve web applications written in Nemerle. It already served as a subscription server for the XVIII-th FIT (a local conference) and later for the CSL 2004.
RiDL is a set of tools to simplify building compilers using Nemerle. It includes a lexical analyzer generator and a parser generator.
It is created by Kojo Adams and uses BSD Licence.
Speagram is an eager purely functional language with a strong type system and unusual syntax resembling natural language.
Asper is a text editor and IDE for Nemerle written in Nemerle.
Contents |
This tutorial describes the basics of programming in Nemerle. It is also a quick tour of some of the features and programming concepts that distinguish Nemerle from other languages.
We assume the reader is familiar with C#, Java or C++. But even if you are new to programming, you should find the code easy to understand.
Nemerle is a .NET compatible language. As such, it relies heavily on the .NET Framework, which not only defines how critical parts of all .NET languages work, but also provides these services:
The BCL and CLR ensure that programs written in any .NET language can be easily used in any other .NET language. These features of language neutrality and interoperability make .NET an attractive platform for development.
Further, Nemerle is compatible with Mono, an open-source implementation of the published .NET Common Language Infrastructure (CLI) standard. This opens up the exciting possibility of writing .NET programs that not only work across different languages, but span different operating systems. With Mono, you can design programs that run on Linux, Mac OS X, and the BSD Unix flavors, as well as Windows.
So, while Nemerle is defined (and constrained) in part by the .NET Framework, it is very much its own language. It offers a unique combination of features not found in other .NET languages, which give it advantages of conciseness and flexibility that should prove attractive to a wide range of programmers, from students to seasoned developers.
To run the examples in this tutorial, you will need to install Nemerle. Hacker types will want to
This section lists simple examples that look almost the same in C# (or Java, or C++).
We start with a classic first example:
System.Console.WriteLine ("Hello world!");
To run this example:
Observe how an individual code statement ends with a semi-colon;
This program writes "Hello world!" to the console. It does this by calling System.Console.WriteLine, a function in the .NET Framework.
As this example shows, you can write a bunch of statements in a file (separated by semi-colons), and Nemerle will execute them. However, this example really isn't a proper, stand-alone program. To make it one, you need to wrap it in a class.
Lets expand our example to include a class. Enter these lines in your hello.n file:
class Hello { static Main () : void { System.Console.WriteLine ("Hello world!"); } }
Notice how blocks of code are grouped together using curly braces { }, typical of C-style programs.
When you compile and run this program, you get the same results as before. So why the extra lines? The answer is that Nemerle, like most .NET languages, is object-oriented:
This example is much closer to what a C# programmer would write. The only difference is that in Nemerle we write the method's return type on the right, after the colon. So, static Main():void specifies that method Main returns void, or no usable type. This is the equivalent of a subroutine in Basic.
Adder is a very simple program that reads and adds two numbers. We will refine this program by introducing several Nemerle concepts.
To start, enter and compile this code:
/* Our second example. This is a comment. */ using System; // This is also a comment public class Adder // As in C#, we can mark classes public. { public static Main () : void // Methods, too. { /* Read two lines, convert them to integers and return their sum. */ Console.WriteLine ("The sum is {0}", // System.Int32.Parse converts string into integer. Int32.Parse (Console.ReadLine ()) + Int32.Parse (Console.ReadLine ())); } }
When run, Adder lets you type in two numbers from the console, then prints out the sum.
As you can see, a lengthy statement can be continued on multiple lines, and mixed with comments, as long as it ends with a semi-colon;
The using declaration imports identifiers from the specified namespace, so they can be used without a prefix. This improves readability and saves typing. Unlike C#, Nemerle can also import members from classes, not only from namespaces. For example:
using System; using System.Console; public class Adder { public static Main () : void { WriteLine ("The sum is {0}", Int32.Parse (ReadLine ()) + Int32.Parse (ReadLine ())); } }
You probably noticed that the code that reads and converts the integers is needlessly duplicated. We can simplify and clarify this code by factoring it into it's own method:
using System; public class Adder { // Methods are private by default. static ReadInteger () : int { Int32.Parse (Console.ReadLine ()) } public static Main () : void { def x = ReadInteger (); // Value definition. def y = ReadInteger (); // Use standard .NET function for formatting output. Console.WriteLine ("{0} + {1} = {2}", x, y, x + y); } }
Within the Main method we have defined two values, x and y. This is done using the def keyword. Note that we do not write the value type when it is defined. The compiler sees that ReadInteger returns an int, so therefore the type of x must also be int. This is called type inference.
There is more to def than just declaring values: it also has an impact on how the value can be used, as we shall see in the next section.
In this example we see no gain from using def instead of int as you would do in C# (both are 3 characters long :-). However def will save typing, because in most cases type names are far longer:
FooBarQuxxFactory fact = new FooBarQuxxFactory (); // C# def fact = FooBarQuxxFactory (); // Nemerle
When creating objects, Nemerle does not use the new keyword. This aligns nicely with the .NET concept that all types, even simple ones like int and bool, are objects. That being said, simple types are a special kind of object, and are treated differently during execution than regular objects.
class LineCounter { public static Main () : void { // Open a file. def sr = System.IO.StreamReader ("SomeFile.txt"); // (1) mutable line_no = 0; // (2) mutable line = sr.ReadLine (); while (line != null) { // (3) System.Console.WriteLine (line); line_no = line_no + 1; // (4) line = sr.ReadLine (); }; // (5) System.Console.WriteLine ("Line count: {0}", line_no); } }
Several things things about this example require further remarks. The first is the very important difference between the lines marked (1) and (2).
In line (1) we define an immutable variable, sr. Immutable means the value cannot be changed once it is defined. The def statement is used to mark this intent. This concept may at first seem odd, but quite often you will find the need for variables that don't change over their lifetime.
In (2) we define a mutable variable, line_no. Mutable values are allowed to change freely, and are defined using the mutable statement. This is the Nemerle equivalent of a variable in C#. All variables, mutable or not, have to be initialized before use.
In (3) we see a while loop. While the line is not null (end of file), this loop writes the line to the console, counts it, and reads the next. It works much like it would in C#. Nemerle also has do ... while loops.
We see our mutable counter getting incremented in (4). The assignment operator in Nemerle is =, and is similar to C#.
Lastly, in (5), we come to the end of of our while loop code block. The line count gets written after the while loop exits.
This section introduces some of the more functional features of Nemerle. We will use the functional style to write some simple programs, that could easily be written in the more familiar imperative style, to introduce a few concepts of the functional approach.
Functional programming (FP) is style in which you do not modify the state of the machine with instructions, but rather evaluate functions yielding newer and newer values. That is, the entire program is just one big expression. In purely functional languages (Haskell being the main example) you cannot modify any objects once they are created (there is no assignment operator, like = in Nemerle). There are no loops, just recursive functions. A recursive function calls itself repeatedly until some end condition is met, at which time it returns its result.
Nemerle does not force you to use FP. However you can use it whenever you find it necessary. Some algorithms have a very natural representation when written in functional style -- for example functional languages are very good at manipulating tree-like data structures (like XML, in fact XSLT can be thought of as a functional language).
We will be using the terms method and function interchangeably.
Let's rewrite our previous Line Counter example using a recursive function instead of the loop. It will get longer, but that will get fixed soon.
class LineCounterWithoutLoop { public static Main () : void { def sr = System.IO.StreamReader ("SomeFile.txt"); mutable line_no = 0; def read_lines () : void { // (1) def line = sr.ReadLine (); when (line != null) { // (2) System.Console.WriteLine (line); // (3) line_no = line_no + 1; // (4) read_lines () // (5) } }; read_lines (); // (6) System.Console.WriteLine ("Line count: {0}", line_no); // (7) } }
In (1) we define a nested method called read_lines. This method simulates the while loop used in our previous example. It takes no parameters and returns a void value.
(2) If line wasn't null (i.e. it was not the last line), (3) we write the line we just read, (4) increase the line number, and finally (5) call ourself to read rest of the lines. The when expression is explained below.
Next (6) we call read_lines for the first time, and finally (7) print the line count.
The read_lines will get called as many times as there are lines in the file. As you can see this is the same as the while loop, just expressed in a slightly different way. It is very important to grok this concept of writing loops as recursion, in order to program functionally in Nemerle.
If you are concerned about performance of this form of writing loops -- fear not. When a function body ends with call to another function no new stack frame is created. It is called a tail call. Thanks to it the example above is as efficient as the while loop we saw before.
In Nemerle the if expression always need to have the else clause. It's done this way to avoid stupid bugs with dangling-else:
// C#, misleading indentation hides real code meaning if (foo) if (bar) m1 (); else m2 ();
If you do not want the else clause, use when expression, as seen in the example. There is also unless expression, equivalent to when with the condition negated.
Our previous aim of rewriting line counter removed the loop and one mutable value. However one mutable value has left, so we cannot say the example is written functionally. We will now kill it.
class FunctionalLineCounter { public static Main () : void { def sr = System.IO.StreamReader ("SomeFile.txt"); def read_lines (line_no : int) : int { // (1) def line = sr.ReadLine (); if (line == null) // (2) line_no // (3) else { System.Console.WriteLine (line); // (4) read_lines (line_no + 1) // (5) } }; System.Console.WriteLine ("Line count: {0}", read_lines (0)); // (6) } }
In (1) we again define a nested method called read_lines. However this time it takes one integer parameter -- the current line number. It returns the number of lines in the entire file.
(2) If line we just read is null (that was last line), we (3) return the current line number as number of lines in entire file. As you can see there is no return statement. The return value of a method is its last expression.
(4) Otherwise (it was not last line) we write the line we just read. Next (5) we call ourselves to read the next line. We need to increase the line number, since it is the next line that we will be reading. Note that as a return value from this invocation of read_lines we return what the next invocation of read_lines returned. It in turn returns what the next invocation returned and so on, until, at the end of file, we reach (3), and final line count is returned through each invocation of read_lines.
In (6) we call the read_lines nested method, with initial line number of 0 to read the file and print out line count.
We have already seen type inference used to guess types of values defined with def or mutable. It can be also used to guess type of function parameters and return type. Try removing the : int constraints from line marked (1) in our previous example.
Type inference only works for nested functions. Type annotations are required in top-level methods (that is methods defined in classes, not in other methods). This is design decision, that is here not to change external interfaces by accident.
It is sometimes quite hard to tell the type of parameter, from just looking how it is used. For example consider:
class HarderInference { static Main () : int { def f (x) { x.Length }; f ("foo"); } }
When compiling the f method we cannot tell if x is a string or array or something else. Nevertheless, we can tell it later (looking at f invocation) and Nemerle type inference engine does it.
If function with incomplete type information was not used or its type was ambiguous, compiler would refuse to compile it.
Now, once you read through all this, please move to the Grokking Nemerle tutorial, that is much more complete. You can also have a look at The Reference Manual if you are tough.
We will write some example code, so you can see some common code patterns when writing programs in Nemerle.
Let us create an array and print its elements walking through it in loop.
using System.Console; module MyApp { Main () : void { // create an array containing strings (array [string]) def elements = array ["{0}", "a {0} a", "{0}", "b {0}", "b {0}"]; // iterate through elements of array for (mutable i = 0; i < elements.Length; i++) // print current value of `i' with using format from array WriteLine (elements [i], i); } }
The keys stored in elements array looks a little bit strange, but we will use WriteLine method as a good example of processing key together with value associated to it.
We will now make the collection of keys unique. For this purpose we will use generic dictionary type, which in Nemerle is called Nemerle.Collections.Hashtable.
using Nemerle.Collections; using System.Console; module MyApp { Main () : void { // create an array containing strings (array [string]) def elements = array ["{0}", "a {0} a", "{0}", "b {0}", "b {0}"]; def hash = Hashtable (); // count the number of occurrences of each unique element foreach (e in elements) { unless (hash.Contains (e)) hash[e] = 0; hash[e] += 1; } // print each element with number indicating its total count foreach (keypair in hash) { WriteLine (keypair.Key, keypair.Value); } } }
For each entry in hashtable we store the number of how many times it occured.
Ok, iterating over collection with foreach is nice, but it is still *too long*!
// print each element with number indicating its total count foreach (keypair in hash) WriteLine (keypair.Key, keypair.Value); // pass matching overload of WriteLine to iteration method hash.Iter (WriteLine)
Hashtable have the special Iter overload, which takes a generic two-argument function, which will be called for every element stored in collection. The declaration of Iter looks like this:
class Hashtable ['a, 'b] { public Iter (f : 'a * 'b -> void) : void { ... } ... }
and after substituting 'a and 'b with actual types in our hash, we just use it as Iter : (string * int -> void) -> void, which takes string * int -> void function as the only argument.
And one more way of iterating over a Hashtable is to query KeyValuePairs property and use pairs of key and value in foreach statement:
foreach( (key, value) in hash.KeyValuePairs) WriteLine(key, value);
Contents |
In this tutorial you will see step-by-step how to build a simple text editor in Nemerle using Gtk#. Gtk# is a .Net binding for the GTK+ GUI toolkit. It is an alternative to Windows.Forms.
The final appearence of our application will be something like:
In order to compile and run examples presented below, you need to install Gtk#. Assuming you have pkg-config installed, you can compile gtk# examples using
ncc example.n -pkg:gtk-sharp -out:example.exe
Otherwise you will need to copy gtk-sharp.dll to the current directory and use
ncc example.n -r:gtk-sharp.dll -out:example.exe
First we must create an application and display a window, which will be the base of our editor:
using System; using Gtk; class MainWindow : Window { public this() { // set caption of window base ("Very Simple Editor"); // resize windows to some reasonable shape SetSizeRequest (300,200); } } module SimpleEditor { Main() : void { Application.Init(); def win = MainWindow(); // exit application when editor window is deleted win.DeleteEvent += fun (_) { Application.Quit () }; win.ShowAll (); Application.Run(); } }
The MainWindow class is quite simple now, but we will add things to it in a moment.
SimpleEditor is a module with one method, Main - the entry point of our application. It is executed first when the program is run and creates the editor window.
One more thing to note is win.DeleteEvent, which is a signal triggered when the window is destroyed (for example user clicks close). We specify what should happen by attaching an anonymous function to the event. Here, we will simply quit the application.
Now we need to add an area to the window where actual text can be written by the user.
Modify the MainWindow class to hold the TextView object:
class MainWindow : Window { /// text input area of our window input : TextView; public this() { // set caption of window base ("Very Simple Editor"); // resize windows to some reasonable shape SetSizeRequest (300,200); def scroll = ScrolledWindow (); input = TextView (); scroll.Add (input); // place scrolledwin inside our main window Add (scroll); } }
In our window's constructor we must choose how the text input will be positioned. We use a Gtk container to encapsulate TextView into ScrolledWindow. This allows text in the text view to be easily viewed using scroll bars.
Add (scroll) adds the ScrolledWindow as part of our text editor window. So, now we have created working text input and viewing.
But we still want to perform some more advanced operations in our editor. Ha, we will even need a menu! We will create a menu and put it into a vertical box. Instead of adding scrolling to the main window, put it into the box as well:
def menu = MenuBar (); def mi = NMenuItem ("File"); menu.Append(mi); def vbox = VBox (); vbox.PackStart (menu, false, false, 0u); vbox.PackStart (scroll, true, true, 0u); // place vertical box inside our main window Add (vbox);
In the last instruction we add the entire vertical box to the main window, above the text view.
NMenuItem, defined below, is the next class we will work with. We will extend it and add some actions to it. Here is the initial code:
class NMenuItem : MenuItem { public name : string; public this(l : string) { base(l); name = l; } }
For now the constructor only sets the caption of the base MenuItem.
We want our only menu item, File, to have some submenus for the tasks available in the editor.
Add a property to NMenuItem, which allows easy adding of child menus:
// class NMenuItem : MenuItem // { // this property allows us to set submenus of this menu item public SubmenuList : list [NMenuItem] { set { def sub = Menu(); foreach (submenu in value) sub.Append (submenu); this.Submenu = sub; } } // }
SubmenuList simply iterates through the elements of the supplied list and adds each of them to the current instance's submenu.
Use this property in the constructor of MainWindow:
// def menu = MenuBar (); // def mi = NMenuItem ("File"); mi.SubmenuList = [ NMenuItem ("Open"), NMenuItem ("Save as...") ]; // menu.Append(mi);
Note the commented out lines. We will uncomment these later.
Finally, we will attach actions to the items in our editor's menu. We will implement opening and saving files.
Add a second constructor to NMenuItem, which will accept an action function to be perfomed after choosing the given menu item:
public this(l : string, e : object * EventArgs -> void) { base(l); name = l; this.Activated += e; }
Like the previous one, this constructor sets the current menu item's caption, but it also attaches the given function to the Activated event of the item.
Now we will define a function which will handle opening and saving files by our editor. Add it to MainWindow:
// handler of opening and saving files OnMenuFile (i : object, _ : EventArgs) : void { def mi = i :> NMenuItem; def fs = FileSelection (mi.name); when (fs.Run () == ResponseType.Ok :> int) match (mi.name) { | "Open" => def stream = IO.StreamReader (fs.Filename); input.Buffer.Text = stream.ReadToEnd(); | "Save as..." => def s = IO.StreamWriter(fs.Filename); s.Write(input.Buffer.Text); s.Close(); | _ => (); }; fs.Hide(); }
This method creates a window for selecting files (Gtk FileSelection), checks which menu item it was called from, and takes the appropriate action. Here we match on the name of NMenuItem, but you could also use something like enums to distinguish various menu items (or attach a specific handler to each of them).
Actions performed for opening and saving files are quite simple - we use Buffer.Text properties of TextView to get and set the contents of the editor.
The last thing to do is actually create the menu items using handlers. Uncomment the lines we entered in the earlier step to read:
def menu = MenuBar (); def mi = NMenuItem ("File"); mi.SubmenuList = [ NMenuItem ("Open", OnMenuFile), NMenuItem ("Save as...", OnMenuFile) ]; menu.Append(mi);
The result of these steps can be accessed here and it looks like this:
using System; using Gtk; class NMenuItem : MenuItem { public name : string; public this(l : string) { base(l); name = l; } public this(l : string, e : object * EventArgs -> void) { base(l); name = l; this.Activated += e; } // this property allows us to set submenus of this menu item public SubmenuList : list [NMenuItem] { set { def sub = Menu(); foreach (submenu in value) sub.Append (submenu); this.Submenu = sub; } } } class MainWindow : Window { /// text input area of our window input : TextView; public this() { // set caption of window base ("Very Simple Editor"); // resize windows to some reasonable shape SetSizeRequest (300,200); def scroll = ScrolledWindow (); input = TextView (); scroll.Add (input); def menu = MenuBar (); def mi = NMenuItem ("File"); mi.SubmenuList = [ NMenuItem ("Open", OnMenuFile), NMenuItem ("Save as...", OnMenuFile) ]; menu.Append(mi); def vbox = VBox (); vbox.PackStart (menu, false, false, 0u); vbox.PackStart (scroll, true, true, 0u); // place vertical box inside our main window Add (vbox); } // handler of opening and saving files OnMenuFile (i : object, _ : EventArgs) : void { def mi = i :> NMenuItem; def fs = FileSelection (mi.name); when (fs.Run () == ResponseType.Ok :> int) match (mi.name) { | "Open" => def stream = IO.StreamReader (fs.Filename); input.Buffer.Text = stream.ReadToEnd(); | "Save as..." => def s = IO.StreamWriter(fs.Filename); s.Write(input.Buffer.Text); s.Close(); | _ => (); }; fs.Hide(); } } module SimpleEditor { Main() : void { Application.Init(); def win = MainWindow(); // exit application when editor window is deleted win.DeleteEvent += fun (_) { Application.Quit () }; win.ShowAll (); Application.Run(); } }
Graphical User Interface (GUI) with Windows.Forms
Windows.Forms is the standard GUI toolkit for the .NET Framework. It is also supported by Mono. Like every other thing in the world it has its strong and weak points. Another GUI supported by .NET is Gtk#. Which outguns which is up to you to judge. But before you can do that, get a feel for Windows.Forms by going throught this tutorial.
Note 1: To compile programs using Windows.Forms you will need to include -r:System.Windows.Forms in the compile command. E.g. if you save the examples in the file MyFirstForm.n, compile them with:
ncc MyFirstForm.n -r:System.Windows.Forms -o MyFirstForm.exe
Note 2: I am not going to go too much into details of every single thing you can do with Windows.Forms. These can be pretty easily checked in the Class reference. I will just try to show you how to put all the bricks together.
Contents |
using System.Drawing; using System.Windows.Forms; class MyFirstForm : Form { public static Main() : void { Application.Run(MyFirstForm()); } }
This is not too complex, is it? And it draws a pretty window on the screen, so it is not utterly useless, either :)
Let us now try to customize the window a little. All the customization should be placed in the class constructor.
using System.Drawing; using System.Windows.Forms; class MyFirstForm : Form { public this() { Text="My First Form"; //title bar ClientSize=Size(300,300); //size (without the title bar) in pixels StartPosition=FormStartPosition.CenterScreen; //I'm not telling FormBorderStyle=FormBorderStyle.FixedSingle; //not resizable } public static Main() : void { Application.Run(MyFirstForm()); } }
Wonderful. Now the time has come to actually display something in our window. Let us add a slightly customized button. All you need to do is to add the following code to the constructor of your Form class (i.e. somewhere between public this() { and the first } after it).
def button=Button(); button.Text="I am a button"; button.Location=Point(50,50); button.BackColor=Color.Khaki; Controls.Add(button); def label=Label(); label.Text="I am a label"; label.Location=Point(200,200); label.BorderStyle=BorderStyle.Fixed3D; label.Cursor=Cursors.Hand; Controls.Add(label);
I hope it should pretty doable to guess what is which line responsible for. Perhaps you could only pay a little more attention to the last one. Buttons, labels, textboxes, panels and the like are all controls, and simply defining them is not enough, unless you want them to be invisible. Also, the button we have added does actually nothing. But fear not, we shall discuss a bit of events very soon.
Menus are not hard. Just take a look at the following example:
def mainMenu=MainMenu(); def mFile=mainMenu.MenuItems.Add("File"); def mFileDont=mFile.MenuItems.Add("Don't quit"); def mFileQuit=mFile.MenuItems.Add("Quit"); def mHelp=mainMenu.MenuItems.Add("Help"); def mHelpHelp=mHelp.MenuItems.Add("Help"); Menu=mainMenu;
It will create a menu with two drop-down submenus: File (with options Don't Quit and Quit) and Help with just one option, Help.
It is also pretty easy to add context menus to controls. Context menus are the ones which become visible when a control is right-clicked. First, you need to define the whole menu, and than add a reference to it to the button definition:
def buttonCM=ContextMenu(); def bCMWhatAmI=buttonCM.MenuItems.Add("What am I?"); def bCMWhyAmIHere=buttonCM.MenuItems.Add("Why am I here?"); ... def button=Button(); ... button.ContextMenu=buttonCM;
Naturally, however nice it all might be, without being able to actually serve some purpose, it is a bit pointless. Therefore, we will need to learn handling events. There are in fact two ways to do it. Let us take a look at the easier one for the begnning.
The first thing you will need to do is to add the System namespace:
using System;
You can skip this step but you will have to write System.EventArgs and so on instead of EventArgs.
Then, you will need to add a reference to the event handler to your controls. It is better to do it first as it is rather easy to forget about it after having written a long and complex handler.
button.Click+=button_Click; ... mFileDont.Click+=mFileDont_Click; mFileQuit.Click+=mFileQuit_Click; ... mHelpHelp.Click+=mHelpHelp_Click;
Mind the += operator instead of = used when customizing the controls.
Finally, you will need to write the handlers. Do not forget to define the arguments they need, i.e. an object and an EventArgs. Possibly, you will actually not use them but you have to define them anyway. You can prefix their names with a _ to avoid warnings at compile time.
private button_Click(_sender:object, _ea:EventArgs):void{ Console.WriteLine("I was clicked. - Your Button"); } private mFileQuit_Click(_sender:object, _ea:EventArgs):void{ Application.Exit(); }
This is the way you will generally want to do it. But in this very case the handlers are very short, and like all handlers, are not very often used for anything else than handling events. If so, we could rewrite them as lambda expressions which will save a good bit of space and clarity.
button.Click+=fun(_sender:object, _ea:EventArgs){ Console.WriteLine("I wrote it. - button_Click as a lambda"); }; mFileQuit.Click+=fun(_sender:object, _ea:EventArgs){ Application.Exit(); };
After you are done with the whole application, you should clean everything up. In theory, the system would do it automatically anyway but it certainly is not a bad idea to help the system a bit. Especially that it is not a hard thing to do at all, and only consists of two steps.
The first step is to add a reference to System.ComponentModel and define a global variable of type Container:
using System.ComponentModel; ... class MyFirstForm : Form { components : Container = null; ...
In the second and last step, you will need to override the Dispose method. It could even fit in one line if you really wanted it to.
protected override Dispose(disposing : bool) : void { when (disposing) when (components!=null) components.Dispose(); base.Dispose(d); }
You should now have a basic idea as to what a form looks like. The time has come to do some graphics.
There is a special event used for painting. It is called Paint and you will need to create a handler for it.
using System; using System.Drawing; using System.Windows.Forms; class MySecondForm : Form { public this() { Text="My Second Form"; ClientSize=Size(300,300); Paint+=PaintEventHandler(painter); } private painter(_sender:object, pea:PaintEventArgs):void{ def graphics=pea.Graphics; def penBlack=Pen(Color.Black); graphics.DrawLine(penBlack, 0, 0 , 150, 150); graphics.DrawLine(penBlack, 150, 150, 300, 0); } public static Main():void{ Application.Run(MySecondForm()); } }
A reference to the painter method should be added in the form class constructor (it affects the whole window), but this time it is a PaintEventHandler. Same as PaintEventArgs in the handler itself. In the handler, you should first define a variable of type PaintEventArgs.Graphics. This is the whole window. This is where you will be drawing lines, circles, strings, images and the like (but not pixels; we will discuss bitmaps later on). To draw all those things you will need pens and brushes. In our example we used the same pen twice. But sometimes you will only need it once. In such case, there is no point in defining a separate variable for it. Take a look at the next example.
Another way to define a handler is to override the default one provided by the framework. It is not at all complicated, it is not dangerous and in many cases it will prove much more useful. Take a look at the example overriding the OnPaint event handler:
protected override OnPaint(pea:PaintEventArgs):void{ def g=pea.Graphics; g.DrawLine(Pen(Color.Black), 50, 50, 150, 150); }
Naturally, when you override the handler, there is no need any longer to add a reference to it in the class constructor.
By the same token can you override other events. One of the possible uses is to define quite complex clickable areas pretty easily:
protected override OnPaint(pea:PaintEventArgs):void{ def g=pea.Graphics; for (mutable x=0; x<300; x+=10) g.DrawLine(Pen(Color.Blue), x, 0, x, 300); } protected override OnMouseDown(mea:MouseEventArgs):void{ when (mea.X%10<4 && mea.Button==MouseButtons.Left) if (mea.X%10==0) Console.WriteLine("You left-clicked a line.") else Console.WriteLine($"You almost left-clicked a line ($(mea.X%10) pixels)."); }
What is an animation without double-buffering? Usually, a flickering animation. We do not want that. Luckily enough, double-buffering is as easy as abc in Windows.Forms. In fact, all you need to do is to set three style flags and override the OnPaint event handler instead of writing your own handler and assigning it to the event.
class Animation:Form{ mutable mouseLocation:Point; //the location of the cursor public this(){ Text="A simple animation"; ClientSize=Size(300,300); SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true); //double-buffering on } protected override OnPaint(pea:PaintEventArgs):void{ def g=pea.Graphics; g.FillRectangle(SolidBrush(Color.DimGray),0,0,300,300); //clearing the window def penRed=Pen(Color.Red); g.FillEllipse(SolidBrush(Color.LightGray), mouseLocation.X-15, mouseLocation.Y-15, 29, 29); g.DrawEllipse(penRed, mouseLocation.X-15, mouseLocation.Y-15, 30, 30); g.DrawLine(penRed, mouseLocation.X, mouseLocation.Y-20, mouseLocation.X, mouseLocation.Y+20); g.DrawLine(penRed, mouseLocation.X-20, mouseLocation.Y, mouseLocation.X+20, mouseLocation.Y); } protected override OnMouseMove(mea:MouseEventArgs):void{ mouseLocation=Point(mea.X,mea.Y); Invalidate(); //redraw the screen every time the mouse moves } public static Main():void{ Application.Run(Animation()); } }
As you can see, the basic structure of a window with animation is pretty easy. Main starts the whole thing as customized in the constructor. The OnPaint event handler is called immediately. And then the whole thing freezes waiting for you to move the mouse. When you do, the OnMouseMove event handler is called. It checks the new position of the mouse, stores it in mouseLocation and tells the window to redraw (Invalidate();).
Try turning double-buffering off (i.e. comment the SetStyle... line) and moving the mouse slowly. You will see the viewfinder flicker. Now turn double-buffering back on and try again. See the difference?
Well, in this example you have to move the mouse slowly to actually see any difference because the viewfinder we are drawing is a pretty easy thing to draw. But you can be sure that whenever anything more complex comes into play, the difference is very well visible without any extra effort.
I promised I would tell you how to draw a single pixel. Well, here we are.
protected override OnPaint(pea:PaintEventArgs):void{ def g=pea.Graphics; def bmp=Bitmap(256,1); //one pixel height is enough; we can draw it many times for (mutable i=0; i<256; ++i) bmp.SetPixel(i, 0, Color.FromArgb(i,0,i,0)); for (mutable y=10; y<20; ++y) g.DrawImage(bmp, 0, y); }
So, what do we do here? We define a bitmap of size 256,1. We draw onto it 256 pixels in colours defined by four numbers: alpha (transparency), red, green, and blue. But as we are only manipulating alpha and green, we will get a nicely shaded green line. Finally, we draw the bitmap onto the window ten times, one below another.
Mind the last step, it is very important. The bitmap we had defined is an offset one. Drawing onto it does not affect the screen in any way.
Now, that you know the DrawImage method nothing can stop you from filling your window with all the graphic files you might be having on your disk, and modifying them according to your needs.
class ImageWithAFilter:Form{ img:Image=Image.FromFile("/home/johnny/pics/doggy.png"); //load an image bmp:Bitmap=Bitmap(img); //create a bitmap from the image public this(){ ClientSize=Size(bmp.Width*2, bmp.Height); } protected override OnPaint(pea:PaintEventArgs):void{ def g=pea.Graphics; for (mutable x=0; x<bmp.Width; ++x) for (mutable y=0; y<bmp.Height; ++y) when (bmp.GetPixel(x,y).R>0){ def g=bmp.GetPixel(x,y).G; def b=bmp.GetPixel(x,y).B; bmp.SetPixel(x,y,Color.FromArgb(255,0,g,b)); } g.DrawImage(img,0,0); //draw the original image on the left g.DrawImage(bmp,img.Width,0); //draw the modified image on the right } ... }
This example draws two pictures: the original one on the left and the modified one on the right. The modification is a very simple filter entirely removing the red colour.
Adding icons is unfortunately a little bit more complicated than other things in Windows.Forms.
You will need to provide handlers for two events: MeasureItem and DrawItem, reference them in the right place, and set the menu item properly.
mFileQuit.Click+=EventHandler( fun(_) {Application.Exit();} ); mFileQuit.OwnerDraw=true; mFileQuit.MeasureItem+=MeasureItemEventHandler(measureItem); mFileQuit.DrawItem+=DrawItemEventHandler(drawItem);
private measureItem(sender:object, miea:MeasureItemEventArgs):void{ def menuItem=sender:>MenuItem; def font=Font("FreeMono",8); //the name and the size of the font miea.ItemHeight=(miea.Graphics.MeasureString(menuItem.Text,font).Height+5):>int; miea.ItemWidth=(miea.Graphics.MeasureString(menuItem.Text,font).Width+30):>int; } private drawItem(sender:object, diea:DrawItemEventArgs):void{ def menuItem=sender:>MenuItem; def g=diea.Graphics; diea.DrawBackground(); g.DrawImage(anImageDefinedEarlier, diea.Bounds.Left+3, diea.Bounds.Top+3, 20, 15); g.DrawString(menuItem.Text, diea.Font, SolidBrush(diea.ForeColor), diea.Bounds.Left+25, diea.Bounds.Top+3); }
Nemerle type-safe macros
Contents |