Documentation : Files - Windows Phone 7

This example shows how to share binary data between multiple users and devices.

Application consists of only one model, which has one file field. Main screen of application contains list of model entities and two buttons. User can add new entity and synchronize with mobeelizer cloud. There are two users in the system and there's a possibility to switch user on application life time.

Design Your App

To complete this example we need to create new application in Mobeelizer App Designer called Files.

Next we will create model. We have to go to Models section, create new one and call it FileSyncEntity.

Last thing to do to finish this step is creating new field with file type called photo. Note that you shoud use default model credentials (CRUD operations avaliable for everyone). 

For this example we leave conflict resolving setting in overwrite state. 
Next section to configure is 'Groups & Roles', by default there is one group called 'users' and one device category 'mobile' and this two together create role 'users-mobile'. This default configuration is perfect for our example and you don't have to change it. 
When everything is done in Create mode, deploy our application to test environment, and create two users with passwords:

  • a - usera
  • b - userb

Last thing to do in App Designer is download configured template with default settings.

Use the Mobeelizer SDK

Open downloaded project in Microsoft Visual Studio (Windows Phone SDK 7.1 must be installed on your computer). There is a few files generated in our project, applicatiom.xml, app.config (read more) and 'Model/File.cs' - our model class.

Create app skeleton

In our example we will use MVVM pattern. To make it, create two folders in solution explorer - 'View' and 'ViewModel'. Next, move 'MainPage.xaml' file to 'View' folder and change path to navigation page in 'WMAppManifet.xml' file - DefaultTask node - to 'View/MainPage.xaml'.

You also need to add this files into project:

  • userAIcon.fw.png - User a icon, will be displayed in entities list item. Add it into project root folder and set build action property to Content.
  • userBIcon.fw.png - User b icon, will be displayed in entities list item. Add it into project root folder and set build action property to Content.
  • OwnerNameToIconConverter.cs - Converter which converts owner name into path to user icon. Add it into ViewModel folder. 

Prepare main screen

Now we have already created skeleton of our project, next things to do is creating View and ViewModel. As described before our main screen contains list of model entities and two buttons to add new entity and synchronize data. Lets create it in our MainPage.xaml file. 

First of all we need to create grid with 3 rows, first row will contain control to switch between users, second will contain list of entities and last one will contain 'sync' and 'add' buttons. 
In code below you can see that ListBox items are bind to Entities list, and buttons are bind to commands responsible for synchronization and adding new entities.

View/MainPage.xaml
...
<Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!--TitlePanel contains the name of the application and page title-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="MOBEELIZER TUTORIAL" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="Files" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>
        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="100" />
                <RowDefinition Height="*" />
                <RowDefinition Height="100"/>
            </Grid.RowDefinitions>
            <Grid Grid.Row="0">
                <!-- Place for control to switch users -->
            </Grid>
            <ListBox Grid.Row="1" ItemsSource="{Binding Entities}">
				<!-- TODO: item template -->
            </ListBox>
            <StackPanel Grid.Row="2" Orientation="Horizontal">
                <Button Content="Add" Width="230" Command="{Binding AddEntityCommand}" CommandParameter="{Binding Entities}"/>
                <Button Content="Sync" Width="230" Command="{Binding SyncCommand}" CommandParameter="{Binding Entities}"/>
            </StackPanel>
        </Grid>
    </Grid>
 
...

Ok, let's create ViewModel for our xaml code. In ViewModel folder we need to create MainPageViewModel class. This class will contains properties which we already bind to our View.

ViewModel/MainPageViewModel.cs
public class MainPageViewModel
{
	 	public MainPageViewModel()
 		{
            Entities = new ObservableCollection<FileSyncEntity>(); 		
		}
		public ICommand AddEntityCommand { get; set; }
 		public ICommand SyncCommand { get; set; }
 		
		public ObservableCollection<FileSyncEntity> Entities { get; set; }
}

To connect our View with just created ViewModel, open MainPage.xaml.cs file and implement MainPage class constructor like this:

View/MainPage.xaml.cs
...
        public MainPage()
        {
            InitializeComponent();
            this.DataContext = new MainPageViewModel();
        }
...

Create switch user control

Before we start implementing add entity command and sync command we have to complete switching users things. We will create separated control for that. (If you already created previous example you can just copy it). In View folder create new Windows Phone User Control and name it SwitchUserControl. This control will contains grid with two columns, first column will present information which user is currently loged in and second will contains button to switch user.

View/SwitchUserControl.xaml
...
	<Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="150"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="{Binding CurrentUser, StringFormat='Current user is: {0}'}" Style="{StaticResource PhoneTextLargeStyle}" VerticalAlignment="Center"/><TextBlock Text="{Binding CurrentUser, StringFormat='Current user is: {0}'}"/>
        <Button Grid.Column="1" Content="Switch" Command="{Binding SwitchUser}"/>
    </Grid> 
...

Code below is ViewModel definition for just created control. There are two properties, first one contains information about currently logged in user and second one is a command to switch users. Class implements INotifyPropertyChanged interface to support notification about value changes of properties.

ViewModel/SwitchUserControlViewModel.cs
   	public class SwitchUserControlViewModel : INotifyPropertyChanged
    {
	    public static String CurrentlyLoggendInUser;
        public SwitchUserControlViewModel()
        {
            // TODO 
        }
        public String CurrentUser
        {
            get
            {
                return CurrentlyLoggendInUser;
            }
        }

        public ICommand SwitchUser { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

And again remember to connect ViewModel and View. You need to also create our new control in MainPage grid row. 

View/SwitchUserControl.xaml.cs
...
        public SwitchUserControl()
        {
            this.DataContext = new SwitchUserControlViewModel();
            InitializeComponent();
        }
...
View/MainPage.xaml
...
<Grid Grid.Row="0">
	<localControls:SwitchUserControl />
</Grid>
...

When View and ViewModel are almost done it is time to write some buisness logic. We will start with SwitchUserCommand, this command will be responsible for switching between users and also have to notify other classes that user was switched. Create new class in Model folder and implement it like in code below. You can see that there are two events UserSwitched and CanExecuteChanged, first one notify about switching user, second will notify View that execution lock may change. There are also four methods: 'CanExecute' which returns execution lock value, 'Execute' which contains logging in code, 'RaiseCanExecuteChanged' to rise 'CanExecuteChanged' event and 'Mobeelizer_SyncStatusChanged' which is triggered when synchronization status change.

Model/SwitchUserCommand.cs
public class SwitchUserCommand : ICommand
{
    private bool canSwitchUser = true;
    private const String USER_A_PASSWORD = "usera";
    private const String USER_B_PASSWORD = "userb";
    private const String USER_A_LOGIN = "a";
    private const String USER_B_LOGIN = "b";

    public event EventHandler CanExecuteChanged;
    public static event EventHandler UserSwitched;

	public SwitchUserCommand()
    {
        Mobeelizer.SyncStatusChanged += new MobeelizerSyncStatusChangedEventHandler(Mobeelizer_SyncStatusChanged);
    }

    public bool CanExecute(object parameter)
    {
        return canSwitchUser;
    }

    public void Execute(object parameter)
    {
       canSwitchUser = false;
       RaiseCanExecuteChanged();
       switch (SwitchUserControlViewModel.CurrentlyLoggendInUser)
       {
          case USER_A_LOGIN:
               Mobeelizer.Login(USER_B_LOGIN, USER_B_PASSWORD, error =>
               {
                    if (error == null)
                    {
                        SwitchUserControlViewModel.CurrentlyLoggendInUser = USER_B_LOGIN;
                    }
                    else
                    {
                        SwitchUserControlViewModel.CurrentlyLoggendInUser = String.Empty;
                    }
                    EventHandler handler = UserSwitched;
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                    Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        canSwitchUser = true;
                        RaiseCanExecuteChanged();
                    }));
                });
                break;
            default:
            case USER_B_LOGIN:
                    Mobeelizer.Login(USER_A_LOGIN, USER_A_PASSWORD, error =>
                    {
                        if (error == null)
                        {
                            SwitchUserControlViewModel.CurrentlyLoggendInUser = USER_A_LOGIN;
                        }
                        else
                        {
                            SwitchUserControlViewModel.CurrentlyLoggendInUser = String.Empty;
                        }
                        EventHandler handler = UserSwitched;
                        if (handler != null)
                        {
                            handler(this, EventArgs.Empty);
                        }
                        Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            canSwitchUser = true;
                            RaiseCanExecuteChanged();
                        }));
                    });
                break;
        }
     }

    public void RaiseCanExecuteChanged()
    {
       EventHandler handler = CanExecuteChanged;
       if (handler != null)
       {
            handler(this, EventArgs.Empty);
       }
    }

    void Mobeelizer_SyncStatusChanged(MobeelizerSyncStatus status)
    {
        if (status == MobeelizerSyncStatus.FINISHED_WITH_SUCCESS || status == MobeelizerSyncStatus.FINISHED_WITH_FAILURE
           || status == MobeelizerSyncStatus.NONE)
        {
           canSwitchUser = true;
        }
        else
        {
            canSwitchUser = false;
        }
        Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            RaiseCanExecuteChanged();
        }));
    }
}

This code is pretty simple, when user click on switch user button, Execute method will be invoked, while method is executing, execution lock is set, when loging in is finished 'UserSwitched' event will be raised. User can't change while synchronization process is in progress, this functionality is implemented in 'Mobeelizer_SyncStatusChanged' method - when synchronization is in progress, lock flag is up. 

Last thing to do in switching user code is implementing SwichUserControlViewModel constructor. 

ViewModel/SwitchUserControlViewModel.cs
...
public SwitchUserControlViewModel()
{
    SwitchUserCommand command = new SwitchUserCommand();
    SwitchUserCommand.UserSwitched += (object sender, EventArgs e) =>
        {
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                RaisePropertyChanged("CurrentUser");
            }));
        };
    this.SwitchUser = command;
    this.SwitchUser.Execute(null);
}
...

Get current user entities

Nice, we can login to mobeelizer and switch between users. It is time to get current user entities. Go back to MainPageViewModel class, in constructor add new method to UserSwitched event and fill entities list when user will be switched.
ViewModel/MainPageViewModel.cs
...
 		public MainPageViewModel()
        {
            Entities = new ObservableCollection<FileSyncEntity>();
            SwitchUserCommand.UserSwitched += new System.EventHandler(UserSwitched);
        }

        void UserSwitched(object sender, System.EventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                GetEntitiesForCurrentUser();
            }));
        }

        private void GetEntitiesForCurrentUser()
        {
            Entities.Clear();
            if (Mobeelizer.IsLoggedIn)
            {
                using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
                {
                    foreach (FileSyncEntity entity in transaction.GetModelSet<FileSyncEntity>())
                    {
                        Entities.Add(entity);
                    }
                }
            }
        }
...

Add entity into database

Our entities list is now visible, it is time to implement AddEntityCommand. We will use PhotoChooserTask class to do that. Create new class in Model folder, called AddEntityCommand and take a look on code below. 

Model/AddEntityCommand.cs
 	public class AddEntityCommand : ICommand
    {
        public bool CanExecute(object parameter)
        {
            return Mobeelizer.IsLoggedIn;
        }


        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            ObservableCollection<FileSyncEntity> entities = (ObservableCollection<FileSyncEntity>)parameter;
            PhotoChooserTask photoChooserTask = new PhotoChooserTask();
            photoChooserTask.Completed += (object sender, PhotoResult e) =>
            {
                if (e.ChosenPhoto != null)
                {
                    using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
                    {
                        FileSyncEntity entity = new FileSyncEntity();
                        entity.PhotoFile = Mobeelizer.CreateFile("photo", e.ChosenPhoto);
                        transaction.GetModelSet<FileSyncEntity>().InsertOnSubmit(entity);
                        transaction.SubmitChanges();
                        Deployment.Current.Dispatcher.BeginInvoke( new Action( () =>
                        {
                            entities.Add(entity);
                        }));
                    }
                }
            };
            photoChooserTask.Show();
        }


        public void RaiseCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

It is easy, right? In Execute method we are creating new PhotoChoserTask and defining that when task is completed new FileSyncEntity object will be added to given entities list. There is one thing more, CanExecute method returns true only if user is loged in to Mobeelizer, this is because there is no access to database for not authorized users. 

Now create object of just created class in MainPageViewModel constructor, and raise CanExecuteChange events when user change.

ViewModel/MainPageViewModel.cs
...
public MainPageViewModel()
{
    Entities = new ObservableCollection<SimpleSyncEntity>();
    AddEntityCommand = new AddEntityCommand();
    SwitchUserCommand.UserSwitched += new System.EventHandler(UserSwitched);
}

void UserSwitched(object sender, System.EventArgs e)
{
    Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        (AddEntityCommand as AddEntityCommand).RaiseCanExecuteChanged();
        GetEntitiesForCurrentUser();
    }));
}
...

Synchronize with mobeelizer

It is time for synchronization now, it will be as easy as adding new entity to list. Create new class SyncCommand in Model folder and implement it like this:

Model/SyncCommand.cs
  	public class SyncCommand : ICommand
    {
        private bool canSyncFlag = true;

        public bool CanExecute(object parameter)
        {
            return Mobeelizer.IsLoggedIn && canSyncFlag;
        }
        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            ObservableCollection<FileSyncEntity> entities = (ObservableCollection<FileSyncEntity>)parameter;
            canSyncFlag = false;
            RaiseCanExecuteChanged();
            Mobeelizer.Sync(error =>
            {
                Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    if (error == null)
                    {
                        entities.Clear();
                        if (Mobeelizer.IsLoggedIn)
                        {
                            using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
                            {
                                foreach (FileSyncEntity entity in transaction.GetModelSet<FileSyncEntity>())
                                {
                                    entities.Add(entity);
                                }
                            }
                        }
                    }
                    canSyncFlag = true;
                    RaiseCanExecuteChanged();
                }));
            });
        }

        public void RaiseCanExecuteChanged()
        {
            EventHandler handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

You can synchronize your data only if user is logged in and if other sync process is not in progress. This condition is defined in CanExecute method. Let's take a look at Execute method. It is actually just calling Mobeelizer sync method. When synchronization finished without any errors given entities list is refreshed, and that is all. 

And again, create object of our new classes in MainPageViewModel constructor, and raise CanExecuteChange events when user change.

ViewModel/MainPageViewModel.cs
...
        public MainPageViewModel()
        {
            Entities = new ObservableCollection<FileSyncEntity>();
            AddEntityCommand = new AddEntityCommand();
            SyncCommand = new SyncCommand();
            SwitchUserCommand.UserSwitched += new System.EventHandler(UserSwitched);
        }

        void UserSwitched(object sender, System.EventArgs e)
        {
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                (AddEntityCommand as AddEntityCommand).RaiseCanExecuteChanged();
                (SyncCommand as SyncCommand).RaiseCanExecuteChanged();
                GetEntitiesForCurrentUser();
            }));
        }
...

Display photos on the list

Great, application is almost ready, and you can test it now. List of entities is visible but there is only information about entites class name. Let's create some xaml code to show added photos and theirs owners. You have to note that IMobeelizerFile contains stream to file saved in phone storage, but Image control takes BitmapImage object as a Source parameter. Because of that we need to create value converter that will convert IMobeelizerFile to BitmapImage. 

ViewModel/MobeelizerFileToBitmapImageConverter.cs
    public class MobeelizerFileToBitmapImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            IMobeelizerFile file = value as IMobeelizerFile;
            BitmapImage bitmap = new BitmapImage();
            bitmap.CreateOptions = BitmapCreateOptions.BackgroundCreation;
            using (var stream = file.GetStream())
            {
                bitmap.SetSource(stream);
            }
            return bitmap;
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Put this class in ViewModel folder. 

While converter is ready let's take a look at MainPage.xaml file. First of all we need to add our converter to page resources (Remember to add ViewModel namespace in page root attribute). 

View/MainPage.xaml
<phone:PhoneApplicationPage 
	...
    xmlns:mobeelizerConverters="clr-namespace:MobeelizerConverters"
    xmlns:converters="clr-namespace:...ViewModel" >

    <phone:PhoneApplicationPage.Resources>
        <mobeelizerConverters:OwnerNameToIconConverter x:Key="OwnerConverter"/>
        <converters:MobeelizerFileToBitmapImageConverter x:Key="MobeelizerFileToBitmapImageConverter"/>
    </phone:PhoneApplicationPage.Resources>
	...
</phone:PhoneApplicationPage>

Secondly, create ListBox items template using our converter.

View/MainPage.xaml
...
            <ListBox Grid.Row="1" ItemsSource="{Binding Entities}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="2" Height="80" Width="430">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="40"/>
                            </Grid.ColumnDefinitions>
                            <Image Height="80" Margin="2" Source="{Binding PhotoFile, Converter={StaticResource MobeelizerFileToBitmapImageConverter}}" HorizontalAlignment="Left"/>
                            <Image Source="{Binding Owner, Converter={StaticResource OwnerConverter}}" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
...

Files synchronization is ready, you can test it now on as many devices as you can find. You can also browse your data in App Designer using Data Browser tool. 

Conclusion

To conclude, this is the simple example of using file synchronization with Mobeelizer. Our model consists of only one field. We can create sample records and synchronize with Mobeelizer cloud.

Attachments: