In my last few posts I introduced a simple app that lets you browse the various built-in font variations supported by Xamarin Forms. If you recall, the application adopted a “stack navigation” design, which is quite common in mobile applications. However, there is one small flaw with our implementation so far – on the iOS platform it is customary to indicate that a ListView/TableView cell can be used to “drill down” into the navigation stack by including a “disclosure accessory” on the far right end of each cell. This accessory can be seen in the iOS Settings app below (the small chevron icons in the right margin of each cell):
Now, we could simply change our cell templates to add a custom image aligned to the right edge – but that wouldn’t be 100% correct. A Xamarin Forms
ListView maps to the native
UITableView, and if you review the iOS programming guide for Table Views you can see that there is built-in support for these that is provided by the operating system. These built-in icons can vary from one version of iOS to the next, and by using them (instead of custom icons) we can be assured that our application will continue to look correct as the platform evolves. It’s also one less graphics resource we would otherwise need to worry about maintaining in our application.
Thankfully, Xamarin Forms gives us the ability to extend the default features of XAML controls, and we can use this to activate platform-specific features such as this.
Implementing support for cell accessories involves two main steps: adding support in XAML, and customizing the iOS rendering process. In the first step we will need to define an attached property that will allow us to enable/disable the accessory options on a per-cell basis. With that in place, we will be able to use either XAML markup or code to switch a given cell from one accessory type to another (or none).
Right now you might be asking “what is an attached property?”
Simply stated, an “attached property” is a way to define a new XAML property that can be applied to controls – without modifying the source code of those controls. Former Silverlight and WPF developers might recognize this concept – it works almost the same way as in those platforms. An attached property is used very similarly to a normal XAML property, except that we need to explicitly state which namespace and class are providing the property. It will also enable databinding scenarios in our Xamarin Forms application – which can be very handy if we need to toggle checkmarks for example. Let’s not get ahead of ourselves though… we need to first define the property itself and then we can take a look at how it can be used.
In our example application we really only care about the basic “disclosure” accessory; however there are several other built-in accessory types in iOS. We might as well support them all! The native UiKit Class Reference defines four different built-in accessory types which are also mirrored in Xamarin.iOS as the
UiKit.UITableViewCellAccessory enum. Ideally we would like to just leverage that enum directly within our Xamarin Forms code, but because the UiKit namespace is platform-specific it is not available in the context of our Xamarin Forms project. In this case, the simplest solution is to create a new enumeration in the PCL project. I’ve called this enum “AccessoryType”, which is defined as:
There is an enumeration value to map to each of the supported cell accessory types that I want to enable under iOS.
After defining the various accessory types, we need to define the attached property itself. To define an attached property, you need to create a static property of type
BindableProperty, and initialize it appropriately. Initialization of the property is handled using one of several overloads of the
BindableProperty.CreateAttached() static API method. This requires a few bits of information: the name of the property being defined, the datatype of the property, the type which this property will be attached to (needs to inherit from
BindableObject – luckily, all XAML controls do), and a default value. Our property is defined as such:
With this in place, we can proceed to make use of our new attached property. Most people will be using XAML Markup (as opposed to pure code) to define their Xamarin Forms UI layout – it tends to be much more concise and easier to debug. For our example, we have static XAML cells, but these could also just as easily be data-bound template cells. To use the attached property from XAML markup, you need to first add a namespace declaration to let the XAML parser know where the code for the property can be found. This is usually placed in the root-level element, but it doesn’t need to be. When declaring the namespace we need to provide both an alias and a reference to the C# namespace where the property can be found – the declaration is essentially a way to map between a C# namespace and a XAML namespace. In our application I used “
xmlns:xaml="clr-namespace:DisclosureAccessoryDemo.Xaml;assembly=DisclosureAccessoryDemo"“. The three parts of that declaration are the alias itself (“xaml”), the assembly that contains the namespace (in this case, also the name of the PCL project), and the namespace within that assembly which contains classes that you want to reference in XAML markup. Here is the full text of the updated main XAML page:
There are several reasons that you might want to manipulate the values of our new attached property via code. You might be building your layout in code instead of XAML markup. You might need to programmatically apply/remove accessory values (such as when implementing a checklist). Whatever the reason, it is easy to access our attached property using code – although it might not seem intuitive if you aren’t already accustomed to the approach.
Because our property is “attached” to an object from an outside source, there is no normal property found on the control we have attached it to. In other words, our property (whose name is “Accessory” as defined above) cannot be retrieved or assigned using the traditional member notation – there is no “
myObject.Accessory“. Instead, in order to access our property we need to use the
GetValue() methods of the
BindableObject base class. For example, here is how you might go about displaying a checkmark accessory on a text cell:
Note that the type of cell here is
TextCell, but above when we defined the attached property it was created to target the
Cell class. This works because
TextCell inherits from
Cell (as does
We have now defined the attached property and shown how to use it within Xamarin Forms, but at this point if you run the project it will not have changed anything about the behavior of the application. In Part 2 I will show how to implement a customer renderer for iOS that produces the desired end result.