.NET/Link Example: A "RealTimeAlgebra" Dialog

This example demonstrates many basic features of creating interactive user interface elements from Mathematica code. It can be run in a modal way, using DoNETModal, or modeless, using DoNETModeless. This example is a .NET version of the Java-based one that is included with J/Link.

To try this example, evaluate all the initialization cells (you can do this with the menu command Kernel/Evaluation/Evaluate Initialization). Then go to the Examples section.

Code

Needs["NETLink`"]

createWindow[] :=
	NETBlock[
		Module[{form, trackbar, listener},
			InstallNET[];
			(* We load some types from which we need to call static members. *)
			LoadNETType["System.Windows.Forms.ScrollBars"];
			LoadNETType["System.Windows.Forms.AnchorStyles"];
			LoadNETType["System.Windows.Forms.DockStyle"];
			LoadNETType["System.Windows.Forms.FormStartPosition"];
			LoadNETType["System.Drawing.FontFamily"];
			(* Now create the form and populate it with components. *)
			form = NETNew["System.Windows.Forms.Form"];
			form@Text = "RealTimeAlgebra";
			trackbar = NETNew["System.Windows.Forms.TrackBar"];
			trackbar@Minimum = 0;
			trackbar@Maximum = 20;
			trackbar@LargeChange = 1;
			inText = NETNew["System.Windows.Forms.TextBox"];
			inText@Text = "Expand[(x+1)^a]";
			inText@ScrollBars = ScrollBars`Vertical;
			outText = NETNew["System.Windows.Forms.TextBox"];
			outText@Multiline = True;
			(* The next line is the equivalent of a bitwise-or of flags in a .NET language like C#, e.g.:
					ScrollBars.Vertical | ScrollBars.Horizontal.
			*)
			outText@ScrollBars = BitOr[NETObjectToExpression[ScrollBars`Vertical], NETObjectToExpression[ScrollBars`Horizontal]];
			outText@Font = NETNew["System.Drawing.Font", FontFamily`GenericMonospace, 10];
			outText@WordWrap = False;
			outText@Dock = DockStyle`Fill;
			trackbar@Dock = DockStyle`Top;
			inText@Dock = DockStyle`Top;
			outText@Parent = form;
			trackbar@Parent = form;
			inText@Parent = form;  (* Last one added appears at top of frame. *)
			form@StartPosition = FormStartPosition`CenterScreen;
			(* Here we wire up the Scroll event to call back into Mathematica and execute the scrollFunc function. *)
			AddEventHandler[trackbar@Scroll, scrollFunc];
			form@Width = 400;
			form@Height = 200;
			(* inText and outText are globals, because we need to refer to them by
			   name in the scrollFunc. They must escape the NETBlock wrapping this createWindow function,
			   hence the call to KeepNETObject. The form object also needs to escape the NETBlock, but it
			   escapes automatically, as it is the NETBlock's return value.
			*)
			KeepNETObject[inText, outText];
			form
		]
	]

(* This is what will be called in response to moving the slider position: *)
scrollFunc[trackBar_, eventArgs_] :=
	outText@Text = Block[{a = trackBar@Value}, FixCRLF[ToString[ToExpression[inText@Text]]]]


RealTimeAlgebraModal[] :=
	NETBlock[
		DoNETModal[createWindow[]]
	]


RealTimeAlgebraModeless[opts___?OptionQ] :=
	Module[{form},
		form = createWindow[];
		(* We use the form's Closing event to specify code to be executed when the form
		   is closed. It is important for a modeless form to use the Closing event, not the
		   Closed event. The Closing event fires before Closed, and there is logic in the Closed
		   event that turns off sharing, so that afterwards no other code that calls Mathematica
		   could be executed from .NET.
		   The use here is typical--we clean up the object references that need to persist
		   throughout the lifetime of the window. Note that the RealTimeAlgebraModeless
		   function is not wrapped in NETBlock. If it were, then the inText and outText objects
		   would be released when that NETBlock ended, which happens right after the window is
		   displayed. The inText and outText objects escape the NETBlock wrapping createWindow
		   because KeepNETObject is called on them. But KeepNETObject doesn't mean "keep forever"--it
		   simply promotes the objects to the "release" list of the next enclosing NETBlock.
		*)
		AddEventHandler[form@Closing, ReleaseNETObject[inText, outText]&];
		DoNETModeless[form, opts];
		(* We don't need a reference to the form anymore, even though it will remain visible and active. *)
		ReleaseNETObject[form]
	]

Examples

This dialog lets you drag a slider to change the value of the varaible a from 0 to 20 and recompute the expression in the top text panel.

This runs the dialog in a modal way:

RealTimeAlgebraModal[]

Try entering Print[a] or  in the top panel and dragging the slider. Notice how the output appears in the notebook. Close the window to end the program.

For comparison, next run the dialog in a modeless way:

RealTimeAlgebraModeless[]

Note how you can continue to use the front end for computations while the RealTimeAlgebra window is up.

Try Print[a] in the upper panel now. Note that the Print output does not appear in the front end. When using ShareKernel, which is how modeless interfaces are implemented, the .NET link is the kernel's $ParentLink while the slider is being dragged, so all side-effect output gets sent to .NET, where it is discarded by the internal code used for event callbacks. You can get the output to appear in the front end by using ShareFrontEnd. ShareFrontEnd is a Mathematica function that turns on sharing of the front end between the kernel and the link to .NET, but it is also an option to DoNETModeless, so you will rarely need to call ShareFrontEnd as a function.

Close the previous modeless window and then try this:

RealTimeAlgebraModeless[ShareFrontEnd -> True]

Now putting Plot[Sin[a x],{x,0,2Pi}] in the upper panel and dragging the slider will cause plots to appear in the front end.

After you are finished, close the RealTimeAlgebra window and front end sharing will be turned off.