Chances are, most Windows Mobile/iPhone user's don't have an Exchange Server sitting around for their own personal use. Well those people are now in luck! Google has released a new service called Google Sync that uses the ActiveSync protocol to deliver your Mail and Contacts from their services to your phone. This also works with Google Apps hosted domains! This is pretty awesome. Embrace, extend, extinguish?
WindowlessControls Tutorial - Part 4: Data Binding (continued)
After rereading my previous post about data binding, I don't feel that my sample does justice to the overall flexibility/utility of WindowlessControls. So, I decided to write another sample: a template for a Windows Mobile contact list application.
Let's start out by deciding the basic behavior of the Contact List:
- The contact list should show the name on the left of the item, and the contacts number below it.
- The contact's picture will be to the right of the name and number.
- When the contact is selected, it should expand and show the street address and email address.
Like the previous example, we populate our ItemsControl with the data we want to bind. To get the list of Pocket Outlook contacts, access the handy OutlookSession class:
myItemsControl.ContentPresenter = typeof(ContactPresenter);
myItemsControl.Control = new StackPanel();
OutlookSession session = new OutlookSession();
foreach (Contact contact in session.Contacts.Items)
{
if (contact.Picture != null)
myItemsControl.Items.Add(contact);
}
Now, let's set up the IInteractiveContentPresenter that will represent a contact. This ContactPresenter is a bit more complex than the previous Image Gallery example:
public class ContactPresenter : StackPanel, IInteractiveContentPresenter
{
// selection state rectangle
WindowlessRectangle myRectangle = new WindowlessRectangle();
// contact picture
WindowlessImage myImage = new WindowlessImage();
// obvious properties
WindowlessLabel myName = new WindowlessLabel(string.Empty, WindowlessLabel.DefaultBoldFont);
WindowlessLabel myNumber = new WindowlessLabel();
WindowlessLabel myAddress = new WindowlessLabel();
WindowlessLabel myEmail = new WindowlessLabel();
// this panel will house the email and address, and only be visible when focused
StackPanel myExtendedInfo = new StackPanel();
public ContactPresenter()
{
OverlayPanel overlay = new OverlayPanel();
// stretch to fit
overlay.HorizontalAlignment = WindowlessControls.HorizontalAlignment.Stretch;
overlay.VerticalAlignment = VerticalAlignment.Stretch;
// dock the picture to the right and the info to the left
DockPanel dock = new DockPanel();
StackPanel left = new StackPanel();
left.Layout = new DockLayout(new LayoutMeasurement(0, LayoutUnit.Star), DockStyle.Left);
myImage.Layout = new DockLayout(new LayoutMeasurement(0, LayoutUnit.Star), DockStyle.Right);
myImage.MaxHeight = 100;
myImage.MaxWidth = 100;
dock.Controls.Add(myImage);
dock.Controls.Add(left);
// 5 pixel border around the item contents
dock.Margin = new Thickness(5, 5, 5, 5);
// make the overlay fit the dock, so as to limit the size of the selection rectangle
overlay.FitWidthControl = overlay.FitHeightControl = dock;
// set up the rectangle color and make it fill the region
myRectangle.Color = SystemColors.Highlight;
myRectangle.HorizontalAlignment = WindowlessControls.HorizontalAlignment.Stretch;
myRectangle.VerticalAlignment = VerticalAlignment.Stretch;
// the rectangle does not paint by default
myRectangle.PaintSelf = false;
// add the extended info that is only visible when focused
StackPanel nameAndNumber = new StackPanel();
nameAndNumber.Controls.Add(myName);
nameAndNumber.Controls.Add(myNumber);
myExtendedInfo.Visible = false;
myExtendedInfo.Controls.Add(myEmail);
myExtendedInfo.Controls.Add(myAddress);
// set up the left side
left.Controls.Add(nameAndNumber);
left.Controls.Add(myExtendedInfo);
// add the foreground and the background selection
overlay.Controls.Add(myRectangle);
overlay.Controls.Add(dock);
// add the item
Controls.Add(overlay);
// this is the bottom border
WindowlessRectangle bottomBorder = new WindowlessRectangle(Int32.MaxValue, 1, Color.Gray);
Controls.Add(bottomBorder);
}
#region IInteractiveStyleControl Members
public void ApplyFocusedStyle()
{
// make the rectangle paint to denote that it is focused
myRectangle.PaintSelf = true;
myExtendedInfo.Visible = true;
}
public void ApplyClickedStyle()
{
}
#endregion
#region IContentPresenter Members
Contact myContact;
public object Content
{
get
{
return myContact;
}
set
{
// populate the various UI elements with the contact
myContact = value as Contact;
if (myContact.Picture != null)
myImage.Bitmap = new StandardBitmap(new Bitmap(myContact.Picture));
myName.Text = myContact.FileAs;
if (!string.IsNullOrEmpty(myContact.MobileTelephoneNumber))
myNumber.Text = myContact.MobileTelephoneNumber;
else
myNumber.Text = myContact.HomeTelephoneNumber;
myAddress.Text = string.Format("{0}\n{1} {2} {3}", myContact.HomeAddressStreet, myContact.HomeAddressCity, myContact.HomeAddressState, myContact.HomeAddressPostalCode);
myEmail.Text = myContact.Email1Address;
}
}
#endregion
}
And the results of this tutorial can be seen in the video below.
This took me about 30 minutes to whip together, with a bit more time, this could easily be turned into a real Contact List application! Click here for the updated source code to this tutorial.
Mono/Dalvik Interop
I've been working away at getting Mono into a usable state on Android, and finally have something to show for it. I (mostly) completed the interop layer that allows code invocation of Mono from Dalvik, and vice versa. As a proof of concept test, I have my test verify reentrancy (i.e., you can invoke Mono method from Dalvik, which in turn invokes a Dalvik method from Mono).
Java code that calls into Mono:
String ret = (String)MonoBridge.invoke("MonoDalvikBridge.DalvikBridge, MonoDalvikBridge", String.class, "Echo", "hello");
Log.i("MonoActivity", ret);
And here's the C# method that was called. In turn, it calls back into Java:
public static string Echo(string echo)
{
DebugLog(echo);
string ret = Invoke<string>("com.koushikdutta.monodalvikbridge.MonoBridge", "echo", echo);
DebugLog(ret);
return echo;
}
And finally, this is the Java method that was called from C#:
public static String echo(String str)
{
DebugLog(str);
return str;
}
As shown, passing and returning "primitive" types between both runtimes works seamlessly. Passing complex types and calling methods on those objects works as well, by way of a runtime wrapper is created for it (MonoObject/DalvikObject).
The basic syntax for invoking a method from one runtime into another is:
Invoke(className | runtimeObject, methodName, returnType, params object[] arguments)
That said, I'm pretty excited for when Mono supports the upcoming C# "dynamic" keyword. That will make interop (at least from Mono to Dalvik) quite trivial.
There's still a bit of clean up and convenience functions I need to implement, but the majority of this is complete. The code is all open source, for those that are curious.
WindowlessControls Tutorial - Part 4: Data Binding
A colleague of mind was asking me a few questions about WindowlessControls, which reminded me that I had been neglecting this tutorial series!
Anyhow... Data binding is all the rage in UI development these days. In short, data binding is a way to have your UI elements be "bound" to your internal data structures. Changes to the data will be reflected as changes in the UI. WPF does this quite nicely with the ItemsControl class. So, that provided a template for the implementation of WindowlessControl's version of ItemsControl. This tutorial will demonstrate how to create an image gallery/viewer application using a data binding technique.
We'll start out with the form template similar to the the previous tutorial. A form with a background image and a foreground which contains the ItemsControl object:
// set up the background bitmap and controlAn ItemsControl contains 3 properties of interest:
PlatformBitmap backgroundBitmap = PlatformBitmap.FromResource("Wallpaper.jpg");
WindowlessImage background = new WindowlessImage(backgroundBitmap);
background.Stretch = Stretch.Uniform;
overlay.Controls.Add(background);
// set up the foreground
StackPanel foreground = new StackPanel();
overlay.Controls.Add(foreground);
// set up the ItemsControl which will hold the images
myItemsControl.BackColor = Color.Transparent;
// the images will be contained in a wrap panel
myItemsControl.Control = new WrapPanel();
// this tells the ItemsControl what is used to represent the content items
myItemsControl.ContentPresenter = typeof(ImageResourcePresenter);
foreground.Controls.Add(myItemsControl);
- ContentPresenter - This is the type of WindowlessControl that will represent your data. It must implement IInteractiveContentPresenter.
- Control - This is the panel that will lay out your ContentPresenters.
- Items - This is the collection of objects that will be bind to the UI.
// set up the ItemsControl which will hold the images
myItemsControl.BackColor = Color.Transparent;
// the images will be contained in a wrap panel
myItemsControl.Control = new WrapPanel();
// this tells the ItemsControl what is used to represent the content items
myItemsControl.ContentPresenter = typeof(ImageResourcePresenter);
foreground.Controls.Add(myItemsControl);
Let's start out by implementing the content presenter. Let's set the following goals for our presenter:
- Display the image (obviously).
- Show a highlight border around the image when it is focused.
- The image should have a grow/shrink animation when clicked.
Here's the implementation of the content presenter:
// an ImageResourcePresenter will be shown as a WindowlessImage
// with a WindowlessBackground that is visible when it is selected
class ImageResourcePresenter : OverlayPanel, IInteractiveContentPresenter
{
WindowlessRectangle myRectangle = new WindowlessRectangle();
WindowlessImage myImage = new WindowlessImage();
public ImageResourcePresenter()
{
// spacing between images
Margin = new Thickness(5, 5, 5, 5);
// limit the maximum size of an item
MaxWidth = 100;
MaxHeight = 100;
// stretch to fit
HorizontalAlignment = WindowlessControls.HorizontalAlignment.Stretch;
VerticalAlignment = VerticalAlignment.Stretch;
// allow a 5 pixel border between the image and the rectangular selected background
myImage.Margin = new Thickness(5, 5, 5, 5);
myImage.VerticalAlignment = VerticalAlignment.Center;
myImage.HorizontalAlignment = WindowlessControls.HorizontalAlignment.Center;
// set up the rectangle color and make it fill the region
myRectangle.Color = SystemColors.Highlight;
myRectangle.HorizontalAlignment = WindowlessControls.HorizontalAlignment.Stretch;
myRectangle.VerticalAlignment = VerticalAlignment.Stretch;
// the rectangle does not paint by default
myRectangle.PaintSelf = false;
Controls.Add(myRectangle);
Controls.Add(myImage);
}
#region IInteractiveStyleControl Members
public void ApplyFocusedStyle()
{
// make the rectangle paint to denote that it is focused
myRectangle.PaintSelf = true;
}
public void ApplyClickedStyle()
{
// when clicked, remove the margin, so the image can "grow"
myImage.Margin = Thickness.Empty;
}
#endregion
#region IContentPresenter Members
string myName;
public object Content
{
get
{
return myName;
}
set
{
myName = value as string;
// see if this image is a file or a resource, and load it
if (!File.Exists(myName))
{
myImage.Bitmap = PlatformBitmap.FromResource(myName);
}
else
{
myImage.Bitmap = PlatformBitmap.From(myName);
}
}
}
#endregion
}
So to create this presenter, start with an OverlayPanel, which houses a background (the focused state rectangle) and a foreground (the WindowlessImage). The interesting bit of the code is the implementation of the Content property. Let's come back to that later.
So, next is to populate the Items on the ItemsControl with images. The follow code will search for all image resources in the assembly and also any image files that maybe in \My Documents\My Pictures.
// let's get a list of image resources
Assembly ass = Assembly.GetCallingAssembly();
string[] names = ass.GetManifestResourceNames();
foreach (string name in names)
{
foreach (string extension in imageExtensions)
{
if (name.EndsWith(extension, StringComparison.CurrentCultureIgnoreCase))
{
myItemsControl.Items.Add(name);
break;
}
}
}
// now let's search the file system for images
foreach (string name in Directory.GetFiles("\\My Documents\\My Pictures"))
{
foreach (string extension in imageExtensions)
{
if (name.EndsWith(extension, StringComparison.CurrentCultureIgnoreCase))
{
myItemsControl.Items.Add(name);
break;
}
}
}
Now, rewinding to the Content property. Behind the scenes, when an object is added to the Items collection, the ItemsControl creates an instance of the given ContentPresenter and sets the Content property to the item to which it is bound. When the Content value changes, the IInteractiveContentPresenter should update the UI representation accordingly. As you can see above, the ContentPresenter determines if the Content is a image file or an image resource, and then loads the image from the appropriate source.
That's all there is to data binding! Changing your to add or remove images is as simple as updating the Items collection. By using data binding and a proper custom WindowlessControls, rich and flexible user interfaces can be quickly developed for Windows Mobile.