We’ve been powering our way through some quality native applications lately using the Xamarin cross-platform development software and we thought we’d start sharing some knowledge nuggets outside of the excellent documentation and tips that are already out there.
MVC and Reusable Views
If you’ve developed software at any level then it’s likely you’ll be familiar with the Model-view-controller (MVC) pattern. There are more appropriate derivatives of MVC that are better suited for cross-platform development such as MVVM but the principle of this tutorial remains valid.
The MVC pattern is well designed for separating business data, logic and UI, but there are times when you need a reusable cross-section of the three at your disposal. For instance say that you’ve setup a model, a controller and a view to manage capturing a photo, adding the photo to a model and saving the image to file. Now say that you need to use this setup or “rig” if you will in various places. In Xamarin, you could embed a child ContainerViewController which then manages your capture view screens. This is OK, but requires one controller to have a good understanding of another before it can set it up with a meaningful capture output – this coupling of controllers is enough to make a novice engineer have a nose bleed.
Here’s a better solution.
Encapsulated Commands
In the spirit of the Command Pattern, one can create classes that:
- Can be configured at runtime
- Encapsulate all logic to perform a specific command
- Can be invoked by a naive caller who doesn’t require an understanding of the underlying command
Sounds great, now let’s have a look at an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class MediaCaptureCommand { private UIViewController viewController; private string folder; private string filePrefix; private EventHandler callback; public MediaCaptureCommand (UIViewController viewController, string folder, string filePrefix, EventHandler callback) { this.viewController = viewController; this.folder = folder; this.filePrefix = filePrefix; this.callback = callback; } public void Invoke() { MediaManager mediaManager = App.Instance.GetMediaManager (); mediaManager.Capture (this.viewController, this.folder, this.filePrefix, this.callback); } } |
This simple command encapsulates all of the information required to call a custom MediaManager class’s capture method.
Here’s a simple view that’ll make use of our new command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class MediaCaptureView : UIView { public MediaCaptureCommand CaptureCommand { get; set; } private UIButton captureButton; public MediaCaptureView (CGRect frame, MediaCaptureCommand captureCommand) : base(frame) { this.CaptureCommand = captureCommand; this.InitView (); } private void InitView() { this.captureButton = new UIButton (UIButtonType.Custom); this.captureButton.TouchUpInside += (object s, EventArgs ea) => { if (this.CaptureCommand != null) { this.CaptureCommand.Invoke(); } }; this.AddSubview (this.captureButton); } } |
Any view controller can now create this command in a snap, construct a reusable view which has a command property and a button handler that invokes away – sweet.
We actually abstract our commands and use an ICommand interface with a generic Invoke method, but we left that out to improve clarity and (hopefully) digest factor.
Happy coding!