Building a Xamarin Forms UI - Controls brings together the user interface concepts from the preceding courses in the Building a Xamarin Forms UI series to create a simple data entry screen with text fields, a date picker, a slider control, and an embedded image. We see how to combine controls within a ContentView, coordinating their actions with C# in the code-behind to create a user defined control. We learn how to account for differences between Android and iOS layouts with conditional formatting.
Let's take what we have learned about views and layouts and place controls on a page. The page is called UserDetailsPage and it will contain a few controls and one content view analogous to a user-defined control. I'll use a stack layout with a dark blue title frame that will contain a grid with an image and a heading. The frame's color is the static resource dark blue button and the corner radius is the resource frame corner rounding. The grid will have one row and two columns. There are two ways to have images in your Xamarin forms app. The first is to embed a resource into the Xamarin forms cross-platform app. While this method is a little more complicated, you only add the image once to your solution. The other method predates Xamarin forms and requires you to load the image as a resource into each of your target platform applications.
In the case of Android, you add the image as an existing item to the drawable folder under the resources folder. Use a xaml image control, specifying the image name, including extension as the source. The image will go in the left-hand column of the grid, so that's column index 0, and I'll add a label to column1.
Before I can run this to see how it looks in the emulator, I need to wire up the controls button in the MainPage by creating a clicked event that implements a navigation PushAsync targeting the UserDetailsPage.
Okay, now I can click through to the user details page and fine-tune my xaml. Let's change the text color, center it, and increase the font size. I don't want the controls hard up against the edges of the page, so I'll give the stack layout a margin. Next, another label, but I'll use the built-in font size of title this time. I want to put a line or separator under that label heading, but there is no line element, so I'll use a box view with a height of one. Next, put in a field for the user to type their name. This is an editor control called username with the placeholder text enter name. Xamarin forms has two controls for entering free text. The editor control allows multiple lines of text, where the entry control is restricted to a single line. You can use the keyboard attribute on an editor or entry field to restrict the type of input allowed.
I don't want my page to have the controls stacked one on top of the other, so I'll place each field with their label in horizontal stack layouts. Now it's just a case of getting the page to look right. I'll make both labels 50 wide and each editor field 300 wide with the WidthRequest attribute. I want a little more space around my data entry fields, so I'll give each horizontal stack layout a margin of five.
Now I want to create a control that allows users to specify their age with either a slider control or a date picker, and I want these controls to work in unison. I'll start with a new content view, called AgeView view, funnily enough, and this time I will use a grid layout. In the user details page, I'll create a namespace reference for XAML_UI.Views and add the AgeView to the bottom of my stack layout. Now I can start a debugging session and design the AgeView xaml. Under the date of birth label, I'll place a date picker control that will span both columns and use the long date format specified with a capital D. Under that, I'll place a label that will dynamically display the user's age as the controls are manipulated.
Next is the slider control with a minimum age of zero and a maximum of 110 that spans both columns. I'll make the minimum track color chocolate and the slide button forest green. Finally, I'll put labels for the minimum and maximum slider values underneath. We need to write a bit of code that will synchronize the date picker and AgeSlider controls. For the date picker, we target the date selected event. I'll type DateSelected, and let Visual Studio create a new event handler for me in the code behind file. ValueChanged is the event we want to target for the slider control. Once again, I'll type the event name and let Visual Studio create the event handler. Because the AgeView is essentially a user-defined control, I don't need to create a view model for synchronizing its UI elements. I will create two private class variables, age, and dob, and initialize them in the AgeView constructor.
I'll set the age to 21, and using the AddYears method of the date-time class, I'll add -21 years to the current date, which is now. Once we've got our age and date of birth, we can set the controls appropriately. When the user changes one control, I want the other control to update automatically. I'll need to get the age from the date control and the date of birth from the AgeSlider. The first method, AgeFromDOB, just subtracts the birth year from this current year. Yes, I know this is a long way from perfect in that only whole years are calculated, but it will do for demonstration purposes. This method also updates the private class member DOB. The next method, DOBFromAge, gets the date of birth from the age expressed in years. This is the same code as in the constructor, so yes, I could have set the date picker date using this function, but we can use the control's events, which will be cleaner. Now the interesting part. The code that executes when the controls' events fire. Typically, an event handler has two parameters - the first, sender, refers to the control in question. The second parameter is a class containing information related to the event triggering the handler. We are interested in the new date that has been selected in the DOB picker.
We pass that date to AgeFromDOB to get the new age and assign the age to the AgeSlider value property. When the AgeSlider changes, we take the new value from the slider and pass it to the DOBFromAge function. DOBFromAge takes an integer parameter, but the new value is a double.
In the act of passing it to DOBFromAge, we convert it into an INT 32 with the ToInt32 method of the static Convert class.
Finally, I'll set the age label text using C# string interpolation. All this means is that when a string is proceeded by a dollar sign, variables within the string enclosed in curly braces have their values displayed. Okay, let's run that. While not perfect, that does work how I intended it to. DOB and the age label updates as I move the slider control, and picking a birth year moves the slider and updates the age label.
Before we see what it looks like on iOS, I want to refactor the AgeView constructor using the controls' events to set the initial values. I'll initialize the private age variable in the declaration and only set the AgeSlider value in the constructor. Let's run that with some breakpoints set to see the code flow. When the ContentView is first created setting the AgeSlider value triggers the value changed event. The new slider value is converted to an int and passed to the date of birth calculation, which returns a date that is assigned to the date picker control. Finally, the updated private class member age is used to set the Agelable text.
Some of you may be wondering why I've explicitly set the controls' values in the code-behind file with with AgeSlider.Value and DOB_Picker.Date instead of using data binding. If you don't know what data binding is, don't worry, I'll be getting to that in a later course. Data binding isn't intended for this scenario where you're coordinating the XAML view class with its controls and making what is essentially a user control. Data binding is used for connecting view-model classes to views. Currently we have no view-model classes. This isn't to say you couldn't use data binding, but the complexity of implementing it in this situation isn't worth it.
Let's run this on the iOS simulator. I'll set xaml UI.iOS as the start-up project and add the CAminiLogo image to the iOS asset catalogs. Click on assets underneath asset catalogs, and we can see the Xamarin, or as it's officially called the Xamagon, app icon images. We just need to click on the add asset button above the app icon and select add image set. To make this work, you need to rename the image set to the name of the image file without the extension. I only have one size of this image, so I'll just add it in the one by slot. That's it; let's select the iPhone simulator and run. This looks good, but I'm not happy with the name and phone labels position relative to the data entry fields. If I manually adjust the top margin for each label, they do line up, but of course, this will make them unaligned for the android version. The solution is conditional formatting based on the target platform. Within a control, you can define platform-specific values for any of the control's attributes. Within the label, I'll define a Label.Margin tag, and use the onPlatform tag targeting the type of value in question, in this case, its thickness. For each target platform, I specify the values I want to use. Basically, we are talking conditional compilation. For iOS the top margin is 10 and for Andriod it's 15, and I need to remove the marign setting from the main label tag.
Running the android emulator again shows us that the labels still line up.
Hallam is a software architect with over 20 years experience across a wide range of industries. He began his software career as a Delphi/Interbase disciple but changed his allegiance to Microsoft with its deep and broad ecosystem. While Hallam has designed and crafted custom software utilizing web, mobile and desktop technologies, good quality reliable data is the key to a successful solution. The challenge of quickly turning data into useful information for digestion by humans and machines has led Hallam to specialize in database design and process automation. Showing customers how leverage new technology to change and improve their business processes is one of the key drivers keeping Hallam coming back to the keyboard.