Building the user interface

It's now time to build the user interface screens; we are going to start by building the view-models. Inside the FileStorage.Portable project, add a new folder called ViewModels, add a new file called MainPageViewModel.cs, and implement the following:

public class MainPageViewModel : ViewModelBase
  {
     #region Private Properties
     private string _descriptionMessage = "Welcome to the Filing Room";
     private string _FilesTitle = "Files";
     private string _exitTitle = "Exit";
     private ICommand _locationCommand;
     private ICommand _exitCommand;
     private ISQLiteStorage _storage;
     #endregion
  }

We include the ISQLiteStorage object in this view-model because we will be creating the database tables when this view-model is created. Don't forget we need to implement the public properties for all private properties; the following are two properties to get you started:

#region Public Properties
public ICommand LocationCommand
  {
    get
     {
       return _locationCommand; 
     }
    set
     {
       if (value.Equals(_locationCommand))
         {
            return;
         }
       _locationCommand = value; OnPropertyChanged("LocationCommand"); 
      }
   }
public ICommand ExitCommand
  {
    get
      {
        return _exitCommand;
      }
    set
      {
       if (value.Equals(_exitCommand))
         {
            return; 
         }
       _exitCommand = value; OnPropertyChanged("ExitCommand");
      }
    }
#endregion

Then we add the remaining properties. We call the SetupSQLite function from the constructor to set up the database as follows:

#region Constructors
public MainPageViewModel (INavigationService navigation, Func<Action, ICommand> commandFactory,
IMethods methods, ISQLiteStorage storage) : base (navigation, methods)
  {
     _exitCommand = commandFactory (() => methods.Exit());
     _locationCommand = commandFactory (async () => await Navigation.Navigate(PageNames.FilesPage, null));
     _storage = storage;
     SetupSQLite().ConfigureAwait(false); 
  }
#endregion
private async Task SetupSQLite()
  {
    // create Sqlite connection _storage.CreateSQLiteAsyncConnection();
    // create DB tables await _storage.CreateTable<FileStorable>
    CancellationToken.None);
  }
}

The SetupSQLite function is responsible for creating the asynchronous connection to the local database and building the one table from the FileStorable object.

Now let's build the page for this view-model. Add a new folder called Pages inside the FileStorage project, add in a new file called MainPage.xaml, and implement the following:

<?xml version="1.0" encoding="UTF-8"?>
<ui:ExtendedContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:ui="clr-namespace:FileStorage.UI;assembly=Xamarin.Forms"
    x:Class="FileStorage.Pages.MainPage"
    BackgroundColor="White"
    Title="Welcome">
<ui:ExtendedContentPage.Content>
      <Grid x:Name="Grid" RowSpacing="10" Padding="10, 10, 10, 10" VerticalOptions="Center">
      <Grid.RowDefinitions> 
          <RowDefinition Height="*"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="Auto"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
      <Image x:Name="Image" Source="files.png" HeightRequest="120" 
          WidthRequest="120" Grid.Row="0" Grid.Column="0"/>
      <Label x:Name="DesciptionLabel" Text="{Binding DescriptionMessage}" 
          TextColor="Black" HorizontalOptions="Center" Font="Arial, 20" 
          Grid.Row="1" Grid.Column="0"/>
      <Button x:Name="LocationButton" Text="{Binding FilesTitle}"
          Command="{Binding LocationCommand}" 
          Style="{StaticResource ButtonStyle}" Grid.Row="2" Grid.Column="0"/>
      <Button x:Name="ExitButton" Text="{Binding ExitTitle}"
          Command="{Binding ExitCommand}" Style="{StaticResource ButtonStyle}" 
          Grid.Row="3" Grid.Column="0"/>
      </Grid>
   </ui:ExtendedContentPage.Content>
</ui:ExtendedContentPage>

Remember our custom control ExtendedContentPage?

We are going to use this for all pages so that every page has alert functionality connected with its view-model. The following line gives the reference to our custom control:

xmlns:ui="clr-namespace:FileStorage.UI;assembly=Xamarin.Forms"

We have to declare a new ExtendedContentPage like the following:

<ui:ExtendedContentPage

The rest of the page is the same as previous projects. A simple Grid contains an image, label, and two buttons. Now implement the following for MainPage.xaml.cs:

public partial class MainPage : ExtendedContentPage, INavigableXamarinFormsPage
  {
    #region Constructors
    public MainPage (MainPageViewModel model) : base(model) 
      {
         BindingContext = model;
         InitializeComponent (); 
      }
    #endregion
    #region INavigableXamarinFormsPage interface
    public void OnNavigatedTo(IDictionary<string, object> navigationParameters)
      {
         this.Show (navigationParameters);
      }
    #endregion 
  }

We are able to assign the BindingContext property through the constructor because we are registering this item inside the IoC container.

Now we move on to the next page, where we will be including the CarouselView. We will also be loading in our files that are saved locally in our database. Our first step is to create a new view-model for each view that is going to appear in the CarouselView. Add a new file to the ViewModels folder called FileItemViewModel.cs and implement the following:

public class FileItemViewModel : ViewModelBase
  {
    #region Private Properties
    private string _fileName;
    private string _contents;
    #endregion
    #region Public Properties
    public string FileName
      {
        get
          { 
            return _fileName;
          }
        set
         {
            if (value.Equals(_fileName))
              {
                return;
              }
        _fileName = value; OnPropertyChanged("FileName");
         } 
      }
    public string Contents
      { 
        get 
          { 
            return _contents;
          }
        set 
         { 
           if (value.Equals(_contents))
             { 
                return; 
             }
          _contents = value; OnPropertyChanged("Contents");
         } 
       }
    #endregion
    #region Public Methods
    public void Apply(FileStorable file)
      {
          FileName = file.Key ?? string.Empty; 
          Contents = file.Contents ?? string.Empty;
      }
    #endregion
    #region Constructors
    public FileItemViewModel(INavigationService navigation, IMethods methods) : 
    base(navigation, methods) { }
    #endregion 
  }

It is very simple, just two properties to contain the filename and text contents of the file. These two items will be saved in a FileStorable object in our local database. We have an Apply function that will take a FileStorable object to load the properties of the view-model.

Now let's build the page. Inside the ViewModels folder, add a new file called FilesPageViewModel.cs and implement the following:

public class FilesPageViewModel : ViewModelBase
    {
        #region Private Properties
        private readonly Func<FileItemViewModel> _fileFactory;
        private readonly ISQLiteStorage _storage;
        private readonly SynchronizationContext _context;
        private ICommand _editFileCommand;
        private ICommand _createFileCommand;
        private bool _noFiles;
        #endregion
   }

We have two commands for editing a file, which will be bound to the custom binding SelectCommandProperty on the CarouselView. When a user touches the current child on the CarouselLayout, this command will be invoked.

Notice the SynchronizationContext property?

This will be used for threading purposes to ensure we update the ObservableCollection on the main UI thread.

Now let's add the public properties as follows:

#region Public Properties
public Subject<DataChange> DataChanges { get; private set; }
public ICommand EditFileCommand
  {
    get
     {
       return _editFileCommand;
     }
    set
     {
       if (value.Equals(_editFileCommand))
         {
            return;
         }
    _editFileCommand = value; 
    OnPropertyChanged("EditFileCommand");
     } 
   }
public ICommand CreateFileCommand
  {
    get
      { 
         return _createFileCommand;
      }
    set 
      { 
        if (value.Equals(_createFileCommand))
      {
        return;
      }
    _createFileCommand = value; 
    OnPropertyChanged("CreateFileCommand");
     }
   }
public bool AnyFiles
   {
     get
       {
         return _noFiles; 
       }
     set
       { 
         if (value.Equals(_noFiles))
          {
            return; 
          }
         _noFiles = value;
         OnPropertyChanged("AnyFiles");
       }
     }
public ObservableCollection<FileItemViewModel> Cells { get; set; }
#endregion

Tip

Don't forget that we only need a public property for the properties that are going to be bound to the view.

We have an ObservableCollection of type FileItemViewModel; so, for every file we pull from the database, a new view-model will be created to show the details on the child view of the CarouselView. We also have an IObservable property called DataChanges; every time we update the ObservableCollection, we will publish a new event through the stream, and because we will be binding this property to the CarouselView, the list of children will be structured accordingly.

Now let's add the constructor as follows:

#region Constructors
public FilesPageViewModel(INavigationService navigation, Func<Action<object>, ICommand> commandFactory, 
    IMethods methods, ISQLiteStorage storage, Func<FileItemViewModel> 
    fileFactory) : base(navigation, methods)
       {
           DataChanges = new Subject<DataChange>();
           // retrieve main thread context _context = 
           SynchronizationContext.Current; 
           _storage = storage; 
           _fileFactory = fileFactory;
           Cells = new ObservableCollection<FileItemViewModel>();
          _editFileCommand = commandFactory(async (file) =>
             {
                await Navigation.Navigate(PageNames.EditFilePage,
                new Dictionary<string, object>()
                 {
                   {
                     "filename", (file as FileItemViewModel).FileName}, 
                       {
                         "contents", (file as FileItemViewModel).Contents} });
                       });
              _createFileCommand = commandFactory(async (obj) =>
               {
                 var fileName = await ShowEntryAlert("Enter file name:");
                 if (!string.IsNullOrEmpty(fileName))
                   { 
                     await Navigation.Navigate(PageNames.EditFilePage,
                     new Dictionary<string, object>() 
                       {
                         {
                           "filename", fileName
                         }
                       });
                  }
               }); 
            }
#endregion
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.27.244