Binding FormattedString for Xamarin Forms

Xamarin Forms doesn't have a Label with a Bindable FormattedString. For example, if you want a bindable bold word in the middle of a sentence in a Label, it's very hard to design it with common control. For that, I create my own component for that.

LabelRenderer.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace PSC.Controls
{
    public class Label : Xamarin.Forms.Label
    {
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            UpdateFormattedTextBindingContext();
        }

        protected override void OnPropertyChanged(
           [CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);

            if (propertyName == FormattedTextProperty.PropertyName)
                UpdateFormattedTextBindingContext();
        }

        private void UpdateFormattedTextBindingContext()
        {
            var formattedText = this.FormattedText as FormattedString;

            if (formattedText == null)
                return;

            foreach (var span in formattedText.BindableSpans)
                span.BindingContext = this.BindingContext;
        }
    }

    [ContentProperty("BindableSpans")]
    public class FormattedString : Xamarin.Forms.FormattedString
    {
        private ObservableCollection<Span> _bindableSpans = 
            new ObservableCollection<Span>();

        public IList<Span> BindableSpans { 
            get { return this._bindableSpans; } 
        }

        public FormattedString()
        {
            this._bindableSpans.CollectionChanged += OnCollectionChanged;
        }

        private void OnCollectionChanged(object sender, 
            NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (var bindableSpan in e.OldItems.Cast<Span>())
                    base.Spans.Remove(bindableSpan);
            }
            if (e.NewItems != null)
            {
                foreach (var bindableSpan in e.NewItems.Cast<Span>())
                    base.Spans.Add(bindableSpan);
            }
        }

        /// <param name="text">To be added.</param>
        /// <summary>
        /// Cast a string to a FromattedString that contains 
        /// a single span with no attribute set.
        /// </summary>
        /// <returns>To be added.</returns>
        public static implicit operator FormattedString(string text)
        {
            return new FormattedString
            {
                BindableSpans = { 
                    new Span { Text = text ?? "" } 
                }
            };
        }
        /// <param name="formatted">To be added.</param>
        /// <summary>
        /// Cast the FormattedString to a string, 
        /// stripping all the attributes.
        /// </summary>
        /// <returns>To be added.</returns>
        public static explicit operator string(FormattedString formatted)
        {
            return formatted.ToString();
        }
    }

    [ContentProperty("Text")]
    public sealed class Span : BindableObject
    {
        Xamarin.Forms.Span _innerSpan;

        public Span() : this(new Xamarin.Forms.Span()) { }

        public Span(Xamarin.Forms.Span span)
        {
            _innerSpan = span;
            // important for triggering property inheritance from parent Label
            this.BackgroundColor = this._innerSpan.BackgroundColor;
            this.FontSize = this._innerSpan.FontSize;
            this.FontAttributes = this._innerSpan.FontAttributes;
            this.FontFamily = this._innerSpan.FontFamily;
            this.ForegroundColor = this._innerSpan.ForegroundColor;
            this.Text = this._innerSpan.Text ?? "";
        }
        public static readonly BindableProperty BackgroundColorProperty =
            BindableProperty.Create(nameof(BackgroundColor), 
                typeof(Color), typeof(Span), Color.Default);

        public Color BackgroundColor
        {
            get { return (Color)GetValue(BackgroundColorProperty); }
            set { SetValue(BackgroundColorProperty, value); }
        }

        public static readonly BindableProperty FontAttributesProperty =
            BindableProperty.Create(nameof(FontAttributes), 
                typeof(FontAttributes), 
                typeof(Span), 
                FontAttributes.None);

        public FontAttributes FontAttributes
        {
            get { return (FontAttributes)GetValue(FontAttributesProperty); }
            set { SetValue(FontAttributesProperty, value); }
        }

        public static readonly BindableProperty FontFamilyProperty =
            BindableProperty.Create(nameof(FontFamily), 
                typeof(string), 
                typeof(Span), 
                string.Empty);

        public string FontFamily
        {
            get { return (string)GetValue(FontFamilyProperty); }
            set { SetValue(FontFamilyProperty, value); }
        }

        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), 
                typeof(double), 
                typeof(Span), -1.0, BindingMode.OneWay, 
                null, null, null, null, 
                bindable => Device.GetNamedSize(
                    NamedSize.Default, typeof(Label)));

        [TypeConverter(typeof(FontSizeConverter))]
        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }

        public static readonly BindableProperty 
            ForegroundColorProperty =
                BindableProperty.Create(nameof(ForegroundColor), 
                   typeof(Color), 
                   typeof(Span), 
                   Color.Default);

        public Color ForegroundColor
        {
            get { return (Color)GetValue(ForegroundColorProperty); }
            set { SetValue(ForegroundColorProperty, value); }
        }

        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(nameof(Text), 
                                    typeof(string), 
                                    typeof(Span), 
                                    string.Empty);

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public Xamarin.Forms.Span InnerSpan { 
            get { return _innerSpan; } 
        }

        protected override void OnPropertyChanged(
            [CallerMemberName] string propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            _innerSpan.BackgroundColor = this.BackgroundColor;
            _innerSpan.FontSize = this.FontSize;
            _innerSpan.FontAttributes = this.FontAttributes;
            _innerSpan.FontFamily = this.FontFamily;
            _innerSpan.ForegroundColor = this.ForegroundColor;
            _innerSpan.Text = this.Text ?? "";
        }

        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
        }

        public static implicit operator Xamarin.Forms.Span(Span bindableSpan)
        {
            return bindableSpan.InnerSpan;
        }

        public static implicit operator Span(
            Xamarin.Forms.Span span)
        {
            return new Span(span);
        }
    }
}

In your XAML you use this control like that:

<ctl:Label
    FontSize="Small"
    TextColor="#333"
    HorizontalOptions="FillAndExpand">
    <Label.FormattedText>
        <ctl:FormattedString>
            <ctl:Span Text="{Binding YourVar1}" FontAttributes="Bold"/>
            <ctl:Span Text="{Binding YourVar2, StringFormat=' {0}'}"/>
        </ctl:FormattedString>
    </Label.FormattedText>
</ctl:Label>

Download the code from GitHub.

Happy coding!

An error occurs with MobileCenter for Xamarin iOS

I added Microsoft Mobile Center to my project after creating the app there. On MobileCenter documentation you can know the Install Identifier for your application (MobileCenter documentation is here).

System.Guid installId = MobileCenter.InstallId;

This function is working fine if you have Android or iOS 10. With iOS less than 10 an error occurs:

Guid should contain 32 digits with 4 dashes (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).

and the StackTrace is similar to

at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID, System.Object failureMessageFormatArgument, System.String failureArgumentName, System.Exception innerException) [0x00030] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:198 \n at System.Guid+GuidResult.SetFailure (System.Guid+ParseFailureKind failure, System.String failureMessageID) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:184 \n at System.Guid.TryParseGuidWithDashes (System.String guidString, System.Guid+GuidResult& result) [0x0008f] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:695 \n at System.Guid.TryParseGuid (System.String g, System.Guid+GuidStyles flags, System.Guid+GuidResult& result) [0x00115] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:443 \n at System.Guid.Parse (System.String input) [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.4.0.123/src/mono/mcs/class/referencesource/mscorlib/system/guid.cs:262 \n at Microsoft.Azure.Mobile.MobileCenter.get_InstallId () [0x0000a] in :0 \n at myInventories.Helpers.LogHelpers.SendMessageToAzure (System.String EventName, System.String PageName, System.String BaseClass, myInventories.Logs.ActionType Action, System.String MoreInfo, System.Collections.Generic.Dictionary`2[TKey,TValue] DictionaryInfo) [0x00008] in /Users/enricorossini/Projects/myInventories/myInventories/myInventories/Helpers/LogHelpers.cs:72

To avoid this error you have to check the OS version.

Implementation

In your solution you have to add in all projects Device Information Plugin (for NuGet Xam.Plugin.DeviceInfo). Then you check easily the OS version with

// check the OS version to avoid error on MobileCenter
string MobileId = "";
var osInfo = CrossDeviceInfo.Current;
if ((osInfo.Platform == Plugin.DeviceInfo.Abstractions.Platform.iOS) && 
    (osInfo.VersionNumber.Major < 10)) 
{
    MobileId = "Unknown";
}
else {
    MobileId = MobileCenter.InstallId.ToString();
}

Happy coding!

Xamarin Forms Components

Hello guys!

From today on Nuget there are 4 new components for Xamarin Forms created by me.

Happy coding!

Custom editor render for Xamarin on iOS

In Xamairin the Editor component doesn’t have a border on iOS. If you want to add one in the iOS project just added the following code.

using UIKit;
using WordBankEasy.iOS.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(Editor), typeof(CustomEditorRenderer))]
namespace PSC.iOS.Renderers
{
    public class CustomEditorRenderer : EditorRenderer
    {
        protected override void OnElementChanged(
			    ElementChangedEventArgs<Editor> e)
        {
            base.OnElementChanged(e);

            if(Control != null)
            {
                Control.Layer.BorderColor = 
                              UIColor.FromRGB(204, 204, 204).CGColor;
                Control.Layer.BorderWidth = 0.5f;
                Control.Layer.CornerRadius = 3f;
            }
        }
    }
}

Happy coding!

What is the difference between Xamarin.Form’s layout options?

In Xamarin.Forms every View has the two properties HorizontalOptions and VerticalOptions. Both are of type LayoutOptions and can have one of the following values:

  • LayoutOptions.Start
  • LayoutOptions.Center
  • LayoutOptions.End
  • LayoutOptions.Fill
  • LayoutOptions.StartAndExpand
  • LayoutOptions.CenterAndExpand
  • LayoutOptions.EndAndExpand
  • LayoutOptions.FillAndExpand

Apparently it controls the view’s alignment on the parent view. But how exactly is the behavior of each individual option? And what is the difference between Fill and the suffix Expand?

Theory

The structure LayoutOptions controls two distinct behaviors:

  • Alignment:

    How is the view aligned within the parent view?

    • Start: For vertical alignment the view is moved to the top. For horizontal alignment this is usually the left-hand side. But note, that on devices with right-to-left language setting this is the other way around, i.e. right aligned.
    • Center: The view is centered.
    • End: Usually the view is bottom or right aligned. On right-to-left languages, of course, it is left aligned.

    • Fill: This alignment is slightly different. The view will stretch across the full size of the parent view. If the parent, however, is not larger then its children, you won’t notice any difference between those alignments. Alignment only matters for parent views with additional space available.

  • Expansion:

    Will the element occupy more space if available?

    • Suffix Expand:

      If the parent view is larger than the combined size of all its children, i.e. additional space is available, then the space is proportioned amongst child views with that suffix. Those children will “occupy” their space, but do not necessarily “fill” it. We’ll have a look on this behavior in the example below.

    • No suffix: The children without the Expand suffix won’t get additional space, even if more space is available. Again, if the parent view is not larger than its children, the expansion suffix does not make any difference as well.

Example

To demonstrate the effect of different LayoutOptions, we create a tiny example app. It consists of a gray StackLayout with some padding and some spacing between its child elements.

static readonly StackLayout stackLayout = new StackLayout {
    BackgroundColor = Color.Gray,
    Padding = 2,
    Spacing = 2,
};

The constructor of our App adds eight child elements before assigning the StackLayout to the MainPage. Note that we use a device-dependent padding for the MainPage to avoid elements being overlaid by the iOS status bar.

public App()
{
    AddElement("Start", LayoutOptions.Start);
    AddElement("Center", LayoutOptions.Center);
    AddElement("End", LayoutOptions.End);
    AddElement("Fill", LayoutOptions.Fill);
    AddElement("StartAndExpand", LayoutOptions.StartAndExpand);
    AddElement("CenterAndExpand", LayoutOptions.CenterAndExpand);
    AddElement("EndAndExpand", LayoutOptions.EndAndExpand);
    AddElement("FillAndExpand", LayoutOptions.FillAndExpand);

    MainPage = new ContentPage {
        Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0),
        Content = stackLayout,
    };
}

The method AddElement creates a new Label with different text and corresponding layoutOption. Its text is centered horizontally and vertically using the two properties HorizontalTextAlignment and VerticalTextAlignment.

Furthermore, it adds a flat yellow BoxView. This will serve as a visual separator between the space occupied by the white labels.

static void AddElement(string text, LayoutOptions layoutOption)
{
    stackLayout.Children.Add(new Label {
        Text = text,
        BackgroundColor = Color.White,
        HorizontalTextAlignment = TextAlignment.Center,
        VerticalTextAlignment = TextAlignment.Center,
        HorizontalOptions = layoutOption,
        VerticalOptions = layoutOption,
        WidthRequest = 160,
        HeightRequest = 25,
    });
    stackLayout.Children.Add(new BoxView {
        HeightRequest = 1,
        Color = Color.Yellow,
    });
}

The screenshot shows the resulting layout. We make the following observations:

  • The different alignment is only visible if there is space available. If the stackLayout would not fill the page, we might not be able to see a difference between elements with options Start or StartAndExpand.

  • Additional space is evenly proportioned amongst all labels with Expand suffix. To see this more clearly we added yellow horizontal lines – actually flat BoxViews – between every two neighboring labels. Labels with more space than their requested height do not necessarily “fill” it. In this case the actual behavior is controlled by their alignment. E.g. they are either aligned on top, center or label of their space or fill it completely.

  • If there is space available and no other expanding element – like in the horizontal direction – the alignment controls the element’s position even if it is not expanding, like the first four labels.

screenshot

Custom ContextAction with Xamarin Forms

I using a Xamarin Forms ListView and I want to enable or disable the Context Actions based on a certain binding or in the code behind.

The way I found is to use BindingContextChanged in a ViewCell. I show you an example

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
  <ContentPage.Content>
    <StackLayout>
      <ListView x:Name="listDictionaries" 
                ItemsSource="{Binding DictionariesList}" 
                IsVisible="{Binding ShowList}"
                HorizontalOptions="FillAndExpand" 
                VerticalOptions="FillAndExpand" 
                HasUnevenRows="true" 
                IsPullToRefreshEnabled="true" 
                RefreshCommand="{Binding Refresh}" 
                SeparatorVisibility="Default" 
                ItemTapped="OnItemTapped" 
                IsRefreshing="{Binding IsBusy, Mode=OneWay}">
        <ListView.ItemTemplate>
          <DataTemplate>
            <ViewCell BindingContextChanged="OnBindingContextChanged">
              <ViewCell.View>
                <StackLayout>
                  <Grid Padding="10" ColumnSpacing="10">
                    <Grid.RowDefinitions>
                      <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                      <ColumnDefinition Width="Auto" />
                      <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Label Text="{Binding Name}" FontSize="Large" 
                           Grid.Row="0" Grid.Column="0" />
                    <Label Text="{Binding Description}" FontSize="Small" 
                           Grid.Row="1" Grid.Column="0" />
                  </Grid>
                </StackLayout>
              </ViewCell.View>
            </ViewCell>
          </DataTemplate>
        </ListView.ItemTemplate>
      </ListView>
    </StackLayout>
  </ContentPage.Content>
</ContentPage>

Then you insert in the code the following code:

/// <summary>
/// Called when binding context changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs"/> 
/// instance containing the event data.</param>
private void OnBindingContextChanged(object sender, EventArgs e) {
    base.OnBindingContextChanged();

    if (BindingContext == null)
        return;

    ViewCell theViewCell = ((ViewCell)sender);
    var item = theViewCell.BindingContext as DictionaryModel;
    theViewCell.ContextActions.Clear();

    if (item != null) {
        MenuItem mn = new MenuItem();
        mn.Clicked += Menu_Clicked;
        mn.Text = "Delete";
        mn.IsDestructive = true;
        mn.CommandParameter = item.Id;
        theViewCell.ContextActions.Add(mn);
    }
}

private async void Menu_Clicked(object sender, EventArgs e) {
    var mi = ((MenuItem)sender);
}

Happy coding!

How to update the data in listview in Xamarin.Forms?

First you add a new class as a ViewModel like:

public class RoomViewModel : BaseViewModel
{
   [here following code]
}

If you don't have BaseViewModel try to download from nuget Refractored.MvvmHelpers. Then in your class define an observable collection like

  public ObservableCollection<RoomRecommandation> _roomSuggestionList = 
               new ObservableCollection<RoomRecommandation>();
  
  public ObservableCollection<RoomRecommandation> Recommendations
  {
     get { return _roomSuggestionList; }
  }

In your ContentPage add a listview like:

<ListView ItemsSource="{Binding Recommendations}">
 <ListView.ItemTemplate>
   <DataTemplate>
     <ViewCell>
       <Grid Padding="10" RowSpacing="10" ColumnSpacing="10">
         <Grid.RowDefinitions>
           <RowDefinition Height="" />
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
           <ColumnDefinition Width="Auto" />
           <ColumnDefinition Width="" />
           <ColumnDefinition Width="Auto" />
         </Grid.ColumnDefinitions>
         <controls:CircleImage Grid.Column="0" BorderColor="#DDD3CB" 
                               BorderThickness="3" WidthRequest="66"
                               HorizontalOptions="CenterAndExpand" 
                               VerticalOptions="CenterAndExpand" 
                               Aspect="AspectFill" 
                               Source="{Binding Image}" />
         <Label Grid.Column="1" Text="{Binding Description}" 
                               VerticalOptions="Start" />
         <Label Grid.Column="2" Text="{Binding Price, StringFormat='£{0}'"
                               FontSize="Small" VerticalOptions="Start" />
        </Grid>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
 </ListView>

Then in your ContentPage code:

  RoomViewModel vm = null;
  public RoomPage ()
  {
      InitializeComponent ();
      LoadData();
  }

  public void LoadData()
  {
      if (vm == null)
      {
          vm = new RoomViewModel();
          BindingContext = vm;
      }
  }

Happy coding!

Xamarin Forms MasterDetail Page Navigation Recipe

In this recipe I will show you how to add a hamburger menu to your Xamarin Forms application using the MasterDetailPage class. The Xamarin.Forms MasterDetail Page consists of one page for the master and one or more pages for the detail.

When used for the main navigation, as in this recipe, we will have a single master page and 4 detail pages. The code for this recipe comes from the Xamarin Forms CRM sample. I have simplified and streamlined the code, and a full Xamarin Studio solution is included at the bottom of this post.

Xamarin.Forms MasterDetail

Think of the MasterDetailPage as the traffic cop of your application navigation. When a user clicks on the hamburger menu, Xamarin.Forms MasterDetail pushes the menu onto the screen. Then when the user taps on a menu item, MasterDetail creates the target page and pushes it onto the stack. Besides those two things, and some styling, MasterDetail doesn’t do much. Have a look at the code.

public class RootPage : MasterDetailPage
{
	public RootPage ()
	{
		var menuPage = new MenuPage ();
		menuPage.Menu.ItemSelected += (sender, e) => 
                             NavigateTo (e.SelectedItem as MenuItem);
		Master = menuPage;
		Detail = new NavigationPage (new ContractsPage ());
	}

	void NavigateTo (MenuItem menu)
	{
		Page displayPage = (Page)Activator.CreateInstance (menu.TargetType);
		Detail = new NavigationPage (displayPage);
		IsPresented = false;
	}
}

Pretty simple right? The only complicated bit is with the (Page)Activator.CreateInstance (menu.TargetType) part, but if you have been doing C# on .Net for a while, you probably have seen it before. All it is, is another way of creating objects.

MasterBehavior

In Xamarin.Forms 1.3, the Xamarin.Forms MasterDetail MasterDetail Page received a new property called MasterBehavior. This property controls how the menu will react when activated. The enumeration MasterBehavior has a few options and they are:

public enum MasterBehavior
{
    Default,
    Popover,
    Split,
    SplitOnLandscape,
    SplitOnPortrait
}

The normal behavior of the MasterDetailPage in landscape mode is “SplitOnLandscape”. I think splitting is normal when using the MasterDetailPage for detail drill in type navigation but not when using it as a hamburger menu or your apps main navigation. When using the MasterDetailPage for app navigation, I set the behavior to popover.

The Menu Page

The menu page is just what the name implies, it’s the Xamarin Forms page that will be shown when the user clicks the menu button. This is the master part of the Xamarin.Forms MasterDetail Page. Our MenuPage is a simple content page with a StackLayout containing a label and a ListView of our menu items.

Please note the public property Menu. This property allows our MasterDetail page access to the ItemSelected event. When the event is fired, the item selected will be sent to the NavigateTo method on the master detail object.

public class MenuPage : ContentPage
{
	public ListView Menu { get; set; }

	public MenuPage ()
	{
		Icon = "settings.png";
		Title = "menu"; // The Title property must be set.
		BackgroundColor = Color.FromHex ("333333");

		Menu = new MenuListView ();

		var menuLabel = new ContentView {
			Padding = new Thickness (10, 36, 0, 5),
			Content = new Label {
				TextColor = Color.FromHex ("AAAAAA"),
				Text = "MENU", 
			}
		};

		var layout = new StackLayout { 
			Spacing = 0, 
			VerticalOptions = LayoutOptions.FillAndExpand
		};
		layout.Children.Add (menuLabel);
		layout.Children.Add (Menu);

		Content = layout;
	}
}

Menu Item

Menu Item is a simple type that holds our menu data and the most important data is the Target type. When a user clicks on a menu item, the NavigateTo method will create a new page from the TargetType, and push that page onto the stack.

public class MenuItem
{
	public string Title { get; set; }
	public string IconSource { get; set; }
	public Type TargetType { get; set; }
}

Menu List View

The menu list view couldn’t be simpler; we use the built in ImageCell for the rows and set the bindings to the properties on the MenuItem type. We set the first item as the selected item and also used it when constructing the MasterDetail Page initial detail class.

public class MenuListView : ListView
{
	public MenuListView ()
	{
		List data = new MenuListData ();

		ItemsSource = data;
		VerticalOptions = LayoutOptions.FillAndExpand;
		BackgroundColor = Color.Transparent;

		var cell = new DataTemplate (typeof(ImageCell));
		cell.SetBinding (TextCell.TextProperty, "Title");
		cell.SetBinding (ImageCell.ImageSourceProperty, "IconSource");

		ItemTemplate = cell;
	}
}

Menu List Data

The MenuListData class is a data structure holding our applications navigation. In your app you could get this data from the database or configuration file. The menu structure of my application doesn’t change much, so I just use code.

public class MenuListData : List
{
	public MenuListData ()
	{
		this.Add (new MenuItem () { 
			Title = "Contracts", 
			IconSource = "contracts.png", 
			TargetType = typeof(ContractsPage)
		});

		this.Add (new MenuItem () { 
			Title = "Leads", 
			IconSource = "Lead.png", 
			TargetType = typeof(LeadsPage)
		});

		this.Add (new MenuItem () { 
			Title = "Accounts", 
			IconSource = "Accounts.png", 
			TargetType = typeof(AccountsPage)
		});

		this.Add (new MenuItem () {
			Title = "Opportunities",
			IconSource = "Opportunity.png",
			TargetType = typeof(OpportunitiesPage)
		});
	}
}

The Content Pages

These pages are what is created when a user clicks on our menu items.

public class ContractsPage : ContentPage
{
	public ContractsPage (){}
}

public class LeadsPage : ContentPage
{
	public LeadsPage (){}
}

public class AccountsPage : ContentPage
{
	public AccountsPage (){}
}

public class OpportunitiesPage : ContentPage
{
	public OpportunitiesPage (){}
}

Download the source code PSC-MasterDetail.zip (79.74 kb)

Happy coding!

Advertsing

125X125_06

Planet Xamarin

Planet Xamarin

Calendar

<<  November 2017  >>
MonTueWedThuFriSatSun
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

Month List