|
TutorialOne
IntroductionDeveloping applications with NSDuctTape is a relatively straightforward process. In this tutorial, you will be creating an application similar to the Currency Converter described in Apple's documentation. This tutorial assumes that you have already installed Mono on your machine. If not, go to http://www.go-mono.com/mono-downloads/download.html and get it. Additionally, it is also assumed that you have downloaded the binaries for NSDuctTape (or built them from source). Building the InterfaceApple provides an application called Interface Builder for laying out user interface elements and wiring up behaviors and data flow between elements. If you've already installed Apple's developer tools (located on the OSX DVD), you should have Interface Builder installed at /Developer/Applications/Interface Builder.app. To begin laying out your interface, start Interface Builder and choose the "Application" template. This will leave you with a variety of windows on your screen, one of which will be an empty window on which you will lay out your controls:
One of the windows on your screen will be the Library (if not, you can select it from the Tools menu). Among other things, the library is your source for creating new controls on the window. Drag a few text fields from the library to your window, for starters.
You may notice, as you do this, that your window will provide guide lines for you at certain points. This is to help you ensure that everything on the window has consistent spacing without requiring you to manually enter dimensions for every element. While you're dragging elements onto your form, also drag a few text labels, a horizontal line, and a button onto your window. Lay them out so that they look a bit like this:
To change the text of the labels and the button, double-click on a control and enter new label text. Also, the best way to get the labels to line up properly is to set them to right-align. To accomplish this, find the Inspector window. The top of it looks like this:
If you select a label, and then select the first "tab" of this window, you'll be presented with an assortment of options for the label, including alignment. Once you have the window laid out how we want it, it would be a good idea to lock the current size of the window. To do this, select the window itself, and then go to the ruler tab on the inspector. It looks like this:
Check the boxes for minimum/maximum size, and then click the "Use Current" buttons to lock in your selection. Now, when you design your model class (the class that contains the "business logic" for the application), only the exchange rate and US dollar amount will be editable, so select the third text field, go to the first tab of the inspector, and unselect the "Editable" checkbox. Also, the convert button should be invoked when the user presses the Enter key, so select it, go to the first tab of the Inspector, and set its "Key Equivalent" to Enter. To do this, click in the box next to the Key Equivalent label, and press Enter. It should like this when you're done:
Next, you need to deal with how focus will flow through this application. To choose the initial focus, find the document window (it contains icons for the application's menu, window, and a few other things), and control-drag (that is, hold down the Control key, and then start a drag operation) from the icon for the window to your topmost text box:
When you release the mouse button, you'll be presented with a short list of options. Choose "initialFirstResponder":
Now that your interface knows which element should receive focus first, you should also indicate how the flow of focus should continue. Begin by control-dragging from the first text field to the second text field. When you get your list of options, this time you should select "nextKeyView". Repeat this process between the second and third text fields. Whenever the user invokes the "Convert" button (either by clicking it or by pressing Enter), focus should return to the exchange rate field so that it is easy to begin a new calculation. To ensure this happens, control drag from the button to the exchange rate field and select the "selectText:" option. You'll come back to this file later, but right now, it's time to write some code. Save your interface as CurrencyConverter.nib (be sure to use the NIB format rather than the XIB format). Writing the CodeUse your favorite editor and create a new file called Program.cs. It's contents should look something like this: using NSDuctTape;
namespace CurrencyConverter
{
internal class Program
{
static void Main(string[] args)
{
Application.Run("CurrencyConverter.nib", System.Reflection.Assembly.GetExecutingAssembly().GetTypes());
}
}
}There's not a whole lot to talk about in this file, except that the second argument to Application.Run specifies the list of types that should be available to the Objective C runtime (and therefore, to the user interface). In this case, you're going to expose all of the types that are in the assembly you're creating. Next up, it's time for NotifyPropertyChangingBase.cs: using System;
using System.ComponentModel;
namespace CurrencyConverter
{
public abstract class NotifyPropertyChangingBase : NSDuctTape.INotifyPropertyChanging
{
public event PropertyChangedEventHandler PropertyChanged;
public event NSDuctTape.PropertyChangingEventHandler PropertyChanging;
public IDisposable ChangeProperties(params string[] properties)
{
return new PropertyChangingScope(this, properties);
}
private void NotifyPropertyChanging(params string[] properties)
{
NSDuctTape.PropertyChangingEventHandler handler = PropertyChanging;
if (handler != null)
{
foreach (string property in properties)
handler(this, new NSDuctTape.PropertyChangingEventArgs(property));
}
}
private void NotifyPropertyChanged(params string[] properties)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
for (int property = properties.Length - 1; property >= 0; property--)
handler(this, new PropertyChangedEventArgs(properties[property]));
}
}
private class PropertyChangingScope : IDisposable
{
public PropertyChangingScope(NotifyPropertyChangingBase parent, string[] properties)
{
_parent = parent;
_properties = properties;
_parent.NotifyPropertyChanging(_properties);
}
public void Dispose()
{
if (!_isDisposed)
_parent.NotifyPropertyChanged(_properties);
_isDisposed = true;
}
bool _isDisposed;
readonly string[] _properties;
readonly NotifyPropertyChangingBase _parent;
}
}
}INotifyPropertyChanging is a special interface in NSDuctTape. When the library sees that a type implements this interface, it registers additional methods with the Objective C runtime that allow the runtime to access, modify, and observe changes to the public properties on the type. The contract for INotifyPropertyChanging specifies that an event should be raised both before and after the value of a public property changes, and that if multiple properties are changed at the same time, the calls to the events should be nested. NotifyPropertyChangingBase implements this interface and also provides a convenient way to fulfill the contract for INotifyPropertyChanging, as you'll see in the next file. Finally, the brains of the application, Converter.cs: namespace CurrencyConverter
{
public class Converter : NotifyPropertyChangingBase
{
public Converter()
{
}
public float SourceCurrencyAmount
{
get { return _sourceCurrencyAmount; }
set
{
using (ChangeProperties("SourceCurrencyAmount"))
_sourceCurrencyAmount = value;
}
}
public float Rate
{
get { return _rate; }
set
{
using (ChangeProperties("Rate"))
_rate = value;
}
}
public float ConvertedAmount
{
get { return _convertedAmount; }
private set
{
using (ChangeProperties("ConvertedAmount"))
_convertedAmount = value;
}
}
public void UpdateConvertedAmount()
{
ConvertedAmount = SourceCurrencyAmount * Rate;
}
float _sourceCurrencyAmount;
float _rate;
float _convertedAmount;
}
}This is a very straightforward model, so I won't go into great detail about, except to point out the using block around the main bodies of each of the property setters. As alluded to earlier, this handles all of the event notifications without overly cluttering up your code. Now that your code is complete, it's time to return to Interface Builder. Values and BehaviorsMac OS X Tiger introduced a new concept called "bindings". The notion behind bindings is that, in classic Model-View-Controller architecture, much of the controller's code consists of pretty standard boilerplate code that ferries values back and forth between the model and the view. Bindings handle all of this value-ferrying for you without requiring that you write all the boilerplate code to accomplish it. In order for you to wire up your bindings, you application will require an instance of your Converter class. Grab an Object from the library:
and drop it into your document window:
Next, select the object in the document window, and go to the next to the last tab of the Inspector. Set the class name of this object to "Converter":
Now you simply need to tell the controls in your window where to look for their values and behaviors. Select the topmost text field and go to the bindings tab on the Inspector (it's the greenish icon). Expand the Value section, click the check box for "bind to" and choose your converter as the object to which it would bind. In the "model key path" field, enter the name of the property to which you want the text field to bind--in this case, Rate:
Repeat this process for the other two fields, binding to SourceCurrencyAmount and ConvertedAmount, as appropriate. The "Convert" button is slightly different. Go to its binding tab, and open the section for "Target". Again, bind to the converter, but don't change the model key path. Instead, set the selector name to UpdateConvertedAmount. In Objective C, selectors are somewhat analogous to methods. For a method on a .NET type that takes no arguments, the selector name will be exactly the same as the method name (for methods that take parameters, an additional string will be appended to the method name). Save your nib file again. You're ready to build (and run!) your application. Putting It All TogetherBring up a terminal window, and navigate to the folder containing all of your C# files and your nib file. Also make sure that NSDuctTape.dll is in this folder (it's not strictly necessary for the files to all be in the same folder, but it certainly makes explanations easier). First, compile your code with gmcs: gmcs -out:CurrencyConverter.exe -reference:NSDuctTape.dll *.cs Now, wrap it into a Macintosh application with macpack: macpack -m:2 \"-n:Currency Converter\" -o:. -a:CurrencyConverter.exe -r:CurrencyConverter/CurrencyConverter.nib,NSDuctTape.dll At this point, you should have a working application in your folder. |
Great stuff!
Nice! keep the tutorials coming!