[ Link to First Tutorial ]
While there is much you can “pick up as you go” if you are an Objective-C programmer, understanding some of the fundamental differences in C# can save you a great deal of time and confusion. But programming Windows Phone 7 involves two languages, C# and Xaml, and fully grokking Xaml and the relationship between the declarative Xaml and the object-oriented C# takes some getting used to.
Let’s begin with Xaml, which is an XML compliant declarative language that has two key advantages for Phone Developers:
- Objects are created at design time, rather than at run time
- Xaml is highly machine readable, and so you can, for example, use the exact same files in both Visual Studio and Expression Blend
Note: You will most often see Xaml spelled with all uppercase letters, XAML. I use Xaml for two reasons: first, that is how it was originally submitted for standardization and second, more important, it looks better. |
Xaml Objects are CLR Objects
While Xaml allows you to declare your objects at design time, by the time they are instantiated at run time they are indistinguishable from run-time objects. That is, a TextBlock created in Xaml and a TextBlock created in code are identical in the underlying .xap file
.xap is pronounced zap and contains everything needed by the Browser plug-in to run your application. It turns out that .xap files are just .zip files and you can rename them and examine them with a utility such as WinZip. |
Let’s examine this idea that you can create the same object in either Xaml or in C#. We’ll start by creating a new Windows Phone Application named XamlDemoAndMore. We’ll give the ContentGrid six rows,
<Grid x:Name="ContentGrid" Grid.Row="1"> <Grid.RowDefinitions> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> </Grid.RowDefinitions>
Now let’s add a TextBlock to the first row by declaring it in the Xaml,
<TextBlock Text="Created In Xaml" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" FontFamily="Georgia" FontSize="18" /> </Grid>
I got a little fancy with my TextBlock (adding the FontFamily and FontSize) so that we can see what that looks like when created in C#. To do this, open MainPage.xaml.cs (the code-behind page) and we’ll add a second TextBlock dynamically,
using System.Windows; using System.Windows.Controls; using System.Windows.Media; using Microsoft.Phone.Controls; namespace XamlDemoAndMore { public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); var newTextBlock = new TextBlock { Text = @"Created in C# in my \Source directory", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Stretch, FontFamily = new FontFamily( "Georgia" ), FontSize = 24 }; ContentGrid.Children.Add(newTextBlock); Grid.SetRow( newTextBlock, 1 ); } } }
The first four lines are using statements, which include the namespaces needed to use various objects such as the TextBlock, Grid, etc.
Notice that the class MainPage has the keyword partial. This indicates that some of the declaration of the class was created elsewhere; which indeed it was. Visual Studio has created another part of the class declaration for MainPage on your behalf. In fact, it has also declared that MainPage derives from PhoneApplicationPage, so you can safely remove the colon and the base class declaration as redundant, though I suspect Visual Studio adds it there both as documentation and as instructional.
The first method we see has no return value, and so we know it is a constructor. In this case, the constructor uses initialization to declare values for all the properties (within the body of the constructor). An alternative is to create an empty TextBlock and then set its properties individually,
var newTextBlock = new TextBlock(); newTextBlock.Text = @"Created in C# in my \Source Directory"; newTextBlock.HorizontalAlignment = HorizontalAlignment.Center;
Notice that the new TextBlock is identified with the keyword var – this provides a flexible but type safe declaration. The type of newTextBlock is not var; once the assignment is made the type is whatever type you’ve assigned to it (in this case TextBlock).
The last two lines assign the new TextBlock to the Grid (more exactly, to the Children Collection of the grid) and set which row within the grid the TextBlock will appear.
Notice the at-sign before the static text. This does not distinguish an NSString, but rather creates a verbatim string, which changes the back-slash from an escape character to a literal. If you remove the @-sign you must escape the backslash like this:
newTextBlock.Text = "Created in C# in my \\Source Directory";
You Can But You Don’t
While you can create any object dynamically, typically you don’t. Instead you declare the object in Xaml, giving you design time checking and machine readability. Typically you only use dynamic creation in C# when you must, for example when you can’t know until run time which type you’ll create.
The Customer Class
Let’s return to the Customer Class we’ve seen in previous tutorials, and build it up step by step. We begin with a simple class to track the name and address of a customer. To do this, we’ll use:
- Member variables (fields)
- Member properties
- Member methods (functions)
A Demo Project
We need a project in which we can work with simple values and display the results. Create a new Windows Phone 7 application and in MainPage.xaml add the following to the Content Grid,
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> <RowDefinition Height="1*" /> </Grid.RowDefinitions> <TextBlock Name="Answer1" Grid.Row="0" /> <TextBlock Name="Answer2" Grid.Row="1" /> <TextBlock Name="Answer3" Grid.Row="2" /> <TextBlock Name="Answer4" Grid.Row="3" /> <TextBlock Name="Answer5" Grid.Row="4" /> <TextBlock Name="Answer6" Grid.Row="5" /> <TextBlock Name="Answer7" Grid.Row="6" /> </Grid>
Fields and Properties
The classic way to create fields and properties is to make the field private and the property public.
Private indicates that only methods of this class can see or change the member variables, while public allows methods from any class to access the property. |
public class Customer { private string _firstName; public string FirstName { get { return _firstName; } set { _firstName = value; } } }
It is very common to create a backing variable (_firstName) and then getters and setters that take no action except to return or set the backing variable. In this case, C# offers the faster and simpler alternative of automatic properties. Thus, you could rewrite the six lines of code above in a single line:
public string FirstName { get; set; }
Methods
Methods are declared within the body of the class definition (remember, there are no header files in C#). They default to private (though it is good practice to mark private methods explicitly). Methods return a value (or void) and have a signature. A method’s signature is the method name plus its parameters.
Constructors
Constructors are responsible for creating an instance of a class, and are the only methods that do not have a return value (nor are they marked void).
public class Customer { public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public Customer (string first, string last, string address) { FirstName = first; LastName = last; Address = address; } }
Default Constructor
There is a special constructor, called a default constructor that does not take any parameters. It is typically used to provide default values for one or more properties or fields,
public Customer() { FirstName = string.Empty; LastName = string.Empty; Address = string.Empty; }
The name default constructor causes untold confusion, because of the Miranda Rule which states that if you do not have a constructor, one will be provided for you. The constructor that is provided for you is a default constructor that does nothing, exactly as if you had written,
public Customer() { }
The fact that a default constructor is provided “by default” is the cause of the confusion; but the name implies no parameters, not that it is provided for you. Note also that if you do provide a constructor and also want a default constructor, you must create one explicitly. This is perfectly legal, in C# you can have two methods with the same name (called method-overloading) as long as their parameter list differs in number
foo (int); foo (int, int);
or the parameter list differs in type
foo (int); foo (int, int); foo (string); foo (int, int, string);
Thus, you might have,
public Customer() { FirstName = string.Empty; LastName = string.Empty; Address = string.Empty; } public Customer (string first, string last, string address) { FirstName = first; LastName = last; Address = address; }
Non-constructor methods work the same way except that they need to have a return value. What matters to C# is that the signatures (name and parameter list) are different, and since the signature does not include the return type you are free to vary the return type as well.
public Double Doubler(Double val) { return val * 2; } public int Doubler (int val) { return val * 2; }
Because the parameter differs in type (Double vs. int) the methods can be overloaded, and overloaded methods can have different return values (Double and int respectively in this case).
Returning Values
In C#, as in Objective-C there are two ways to return values; either in the return statement or by passing in a parameter. Since parameters are passed by reference, this is very easy to work with. In MainPage.xaml instantiate a Customer and invoke each of the Doubler methods,
public MainPage()
{
InitializeComponent();
var cust = new Customer();
var i1 = 5;
var d1 = 20.5d;
var d2 = 35.7d;
int intAnswer1;
Double doubleAnswer1;
Double doubleAnswer2;
intAnswer1 = cust.Doubler( i1 );
Answer1.Text = “Doubler(i1) = ” + intAnswer1;
doubleAnswer1 = cust.Doubler( d1 );
Answer2.Text = “Doubler(d1) = ” + doubleAnswer1;
// cust.Doubler( d2, doubleAnswer2 );
// Answer3.Text = “Doubler(d2,doubleAnswer2) = ”
+ doubleAnswer2;
}
The results are just as expected, but uncommenting the third example (in which we pass in DoubleAnswer2) will cause a compile error. This is because DoubleAnswer2 has not been initialized, and C# requires definite assignment – that is, you must assign a value to a variable before “using” it, such as passing it as a parameter. It is silly, however, to assign a value to this variable (e.g., Answer3 = 0.0) because we’re just going to change the value in the method. C# solves this with the keyword out, which you put on both the call and the called method,
cust.Doubler(d2, out doubleAnswer2); Answer3.Text = "Doubler(d2,doubleAnswer2) = " + doubleAnswer2.ToString(); // .... public void Doubler(Double val, out Double answer) { answer = val * 2; }
Typically you would use the out parameter when you either have two or more values to retrieve or you wish to use the return value for a flag (e.g., a boolean) and the out parameter for the returned value.
Object Initializers
If you have a default constructor and public “setter” properties, there are three ways to initialize the values in your object:
- The Constructor
- The default constructor and explicit assignment
- The default constructor and object initializaton
var customer1 = new Customer( "George", "Washington", "Capitol NYC" ); var customer2 = new Customer(); customer2.FirstName = "George"; customer2.LastName = "Washington"; customer2.Address = "Capitol, NYC"; var customer3 = new Customer { FirstName = "George", LastName = "Washington", Address = "Capitol, NYC" };
You can prove to yourself that these have the same effect with these three lines of code,
Answer4.Text = customer1.FirstName + " " + customer1.LastName + " " + customer1.Address; Answer5.Text = customer2.FirstName + " " + customer2.LastName + " " + customer2.Address; Answer6.Text = customer3.FirstName + " " + customer3.LastName + " " + customer3.Address;
Memory Management
As is often mentioned, C# does not use pointers. Well, you can, but again, you don’t. Every variable is a reference (with the exception of variables of built-in types such as int, double, etc.) . That parenthetical point is important, however, and was glossed over in the example above. Passing a Double in to a method to get a return value will not work unless you use one of two keywords out (as shown above) or ref – which indicates that the built-in type should be passed as a reference.
I felt safe in glossing over this due to definite assignment and because 99% of the time, from a practical standpoint, it doesn’t come up.
All objects (anything defined by a class) are automatically treated as references, and all objects are instantiated with the keyword new. You do not, normally, need to do anything to clean up after your objects; the garbage collector will take care of that for you (the exception to this rule is when you are working with unmanaged code, a topic obscure enough and unusual enough that you can safely ignore it for a very long time, until it bites you and then you can read all about it in an intermediate C# book).
For 99% of your work, you can just “new up” an object, use it, and then forget it, as we did with customers 1,2 and 3 above and as wel’ll see as we move forward with the customer class.
Object Oriented Programming
C#, like all object oriented languages, rests on three pillars:
- Encapsulation
- Polymorphism
- User Defined Types
Encapsulation is effected through the use of properties. A property looks to the consumer like a field, but to the creator like a method. This is nearly ideal.
As an example, let’s say we add a field to the Customer class discount, we want to raise a particular customer’s discount from 5% to 10%. We would start by adding a property to the Customer,
private double _discount = .05; public double Discount { get { return _discount; } set { _discount = value; } }
We can then iterate through the customers whose discount we wish to increase, and for a given customer we could write
cust Discount = .1;
thereby raising that customer’s discount to 10%. The key advantage of using a property here is that the code increasing the discount does not have direct access to the backing variable, and so if/when we move that to a database lookup, the client code does not break. In addition, we can change the setter function to do additional work,
public double Discount { get { return _discount; } set { _discount = value; UpdateDatabase(); if ( value > .15 ) AddCustomerToVIPList(); if ( value > .25 ) StartAudit( this ); } }
Again, the code setting the discount is fully unaware of these additional steps; to that code it looks like a field was set.
Note that the this reference passed in to the StartAudit method is a reference to the Customer, and so we can know that StartAudit will probably look like this
public void StartAudit(Customer theCust) { //.... }
Notice also that no special element is required to show that this is a reference (no * and no keyword ref) as objects are always passed by reference. Thus, changes made to theCust in StartAudit will affect the customer passed in from the setter in Discount.
Polymorphism
For most of your work in Windows Phone 7 programming you’ll be working with classes that already exist, and overriding their methods without having to first declare the method. Declaring an overridable method is trivial, however; you simply mark the method with the keyword virtual. Thus, you might add a CreditRating to your Customer class that uses a protected (visible to this class and classes derived from this class) method, LookUpCreditScore, which goes to an independent vendor to get a credit score.
protected virtual double LookUpCreditScore() { return 400.0d; } private double creditRating; public double CreditRating { get { return LookUpCreditScore(); } }
Keeping things simple, I just return a credit score of 400 for this demo. You might now create a class derived from Customer that overrides the virtual method LookUpCreditScore. To make it explicit that you intend to override (and not hide) this inherited method, you use the keyword override,
The derived type inherits all of the fields, properties, methods and events of the parent type, and is free, as we see here, to override methods to achieve different behavior. As you might expect, because a VIP is a Customer, you can use a reference to a VIP wherever a reference to a Customer is expected, and you will receive the overridden behavior.
Here is the code to test this.
In Customer.cs:
public class Customer { public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public Customer() { FirstName = string.Empty; LastName = string.Empty; Address = string.Empty; } public Customer(string first, string last, string address) { FirstName = first; LastName = last; Address = address; } protected virtual double LookUpCreditScore() { MessageBox.Show("In Customer"); return 400.0d; } private double creditRating; public double CreditRating { get { return LookUpCreditScore(); } } public class Vip : Customer { protected override double LookUpCreditScore() { MessageBox.Show( "In VIP!" ); double baseScore = base.LookUpCreditScore(); baseScore += 100.00; return baseScore; } } }
Notice that Vip is not only derived from Customer, it is nested within Customer. MainPage cannot invoke LookUpCreditScore directly as it is protected (private to the Customer class and classes that derive from it) but it can use the public property CreditRating,
public MainPage() { InitializeComponent(); cust = new Customer(); SomeMessage(); cust = new Customer.Vip(); SomeMessage(); } public void SomeMessage() { MessageBox.Show( "Customer credit rating is " + cust.CreditRating ); }
The result is that the first time SoemMessage is called, it is called on the Customer and you’ll see the messageBox from within Customer’s LookUpCreditScore and the second time it is called, it is called on the Vip derived class, and so you’ll see the messageBoxes from both Vip and from the base type Customer and the rating will be 500 rather than 400. (Remember that a derived type includes the base type.
Interfaces
As mentioned earlier, the keyword interface means something entirely different in C#; specifically it is a way to obtain the advantages of Multiple-inheritance without the complexity of M.I. (which C# does not support). An interface can be thought of as a mix-in or as a contract; either way, the interface author is saying “This is what you must have and do if you want to implement this interface” and the interface implementer is saying “I’ve fulfilled this contract and you can rely on that.”
It is easier to understand all of this by looking at an example. Let’s create an interface named IStorable, which assures that a class can be written to the disk and read from the disk (this is just an example, and not part of actually storing data to disk).
public interface IStorable { string Path { get; set; } void Write( string stringToWrite ); void Read( string stringToRead ); }
Notice that the interface tells you that there is a property Path with both a getter and a setter, but it does not implement either. Similarly it tells you there are Write and Read methods, and it tells you their signature and return type, but that is all it tells you. Also note that you do not put the accessibility in the interface (public, private, etc.).
If Customer wants its clients (classes that interact with Customer methods) to be able to rely on the fact that it supports this interface, it announces this by adding IStorable after the colon (and after base class declarations),
class Customer : IStorable
But having done so, it then is required to have the three elements of that interface,
public class Customer : IStorable { public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } private string _path; public string Path { get { return _path; } set { _path = value; } } public void Write(string writeMe) { //... } public void Read(string whatWasRead) { //.... }
The implementing class (Customer) does assign access labels (public, private, etc.) and must provide properties, methods and events that match the signature of the corresponding properties, methods and events in the interface.
Thus, the interface says “here’s the method you must have” and the implementing class provides the specific implementation of those elements. (Note that a second class implementing IStorable may very well implement Read and Write differently, though the methods will still have these same signatures).
Note that there is a vestigial convention to name interfaces with an initial upper-case I; IStorable, INotify, IClaudius. I’ve gone along with this because most programmers do, though it is not required. |
Delegates and Events & MVVM v MVC
This tutorial is incomplete without a write up of Delegates and Events, as well as good coverage of MVVM, but these topics were covered in an earlier tutorial. In the spirit of never wasting your time, I’ll refer you to that tutorial, though I will mention that some iPhone developers find it helpful to approach delegates with reference to Objective-C selectors (the similarity is strong but not perfect).
We will return to MVVM in even greater detail in a coming tutorial.
Generics
While C# does support all of the standard C-style Arrays, it also has access to a set of type-safe collections in the Generics library. This allows you to have, for example, a type safe Dictionary in which you define that they key will be an integer (e.g., an ID) and the value will be a string. You can also freely define the key and/or the value to be more complex, user-defined types.
It is common in iPhone development to see a type-safe ObservableCollection that implements the interfaces INotifyCollectionChanged and INotifyPropertyChanged, as discussed in the previous tutorial.
Resources
No tutorial can cover a language as rich as C# in full, even with the understanding that you already know Objective-C. If you are interested in acquiring C# expertise, there are a number of excellent books, and the on-line documentation that comes with Visual Studio can also provide excellent help, as can any number of sites on the Web, especially Microsoft’s Visual C# Developer Center
Previous Tutorial | Next Tutorial |
4 Responses to iPhone to Windows Phone 7 Tutorial: Objective-C, C#, Xaml