Documentation : Simple sync - Windows Phone 7

This example shows a simple way to share data between multiple users and devices.

Application consists of only one entity, which has one field. Main screen of application contains table with entities. User can add new entity and synchronize with mobeelizer cloud. There are two users on purpose. During runtime one can change users to observe synchronization.

Design Your App

Create new application in App Designer called SimpleSync.

Secondly, create new model called SimpleSyncEntity with text field - title.

For now leave conflict resolving property on overwrite. By default, there is one group - users, device category - mobile and role - users-mobile define. Deploy application to test environment. On test environment create two users with passwords:

  • a - usera
  • b - userb

Finally, download configured template for Windows Phone project. Use default configuration.

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. 
  • movies.xml - Generated list of movies, put it in the project and set build action value to 'content'.

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.cs
<phone:PhoneApplicationPage 
	... 
    xmlns:mobeelizerConverters="clr-namespace:MobeelizerConverters"
	...
	>
    <phone:PhoneApplicationPage.Resources>
        <mobeelizerConverters:OwnerNameToIconConverter x:Key="OwnerConverter"/>
    </phone:PhoneApplicationPage.Resources>

    <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="Simple Sync" 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">
                <localControls:SwitchUserControl />
            </Grid>
            <ListBox Grid.Row="1" ItemsSource="{Binding Entities}">
                <ListBox.ItemTemplate>
                    <DataTemplate >
                        <Grid Margin="1" Height="50" Width="430">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="40"/>
                            </Grid.ColumnDefinitions>   
                            <TextBlock Text="{Binding Title}" FontSize="32" VerticalAlignment="Center"/>
                            <Image Source="{Binding Owner, Converter={StaticResource OwnerConverter}}" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </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>
</phone:PhoneApplicationPage>

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<SimpleSyncEntity>();
    }

    public ICommand AddEntityCommand { get; set; }

    public ICommand SyncCommand { get; set; }

    public ObservableCollection<SimpleSyncEntity> 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. 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"/>
        <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>
...

WhenView 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<SimpleSyncEntity>();
    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 (SimpleSyncEntity entity in transaction.GetModelSet<SimpleSyncEntity>())
            {
                Entities.Add(entity);
            }
        }
    }
} 
...

Add entity into database

Our entities list is now visible, it is time to implement AddEntityCommand. We will use predefined values to generate new entities. This is an xml file with list of movie titles, download it, put in the project and set build action value to 'content'. Create new class in Model folder, called AddEntityCommand and take a look on code below. There is one thing more, XDocument class is in System.Xml.Linq namespace, remember to add this component to project references. 

 Create new class in Model folder, called AddEntityCommand and take a look on code below. 

Model/AddEntityCommand.cs
public class AddEntityCommand : ICommand
{
    private List<String> movieTitles;
    private Random rand = new Random();
    public AddEntityCommand()
    {
        XDocument movies = XDocument.Load("movies.xml");
        this.movieTitles = new List<string>();
        foreach (XElement title in movies.Root.Element("movieTitles").Elements("item"))
        {
            movieTitles.Add(title.Value);
        }
    }
    public bool CanExecute(object parameter)
    {
        return Mobeelizer.IsLoggedIn;
    }
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter)
    {
        ObservableCollection<SimpleSyncEntity> entities = (ObservableCollection<SimpleSyncEntity>)parameter;
        using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
        {
            SimpleSyncEntity entity = new SimpleSyncEntity();
            entity.Title = movieTitles[rand.Next(0, movieTitles.Count - 1)];
            transaction.GetModelSet<SimpleSyncEntity>().InsertOnSubmit(entity);
            transaction.SubmitChanges();
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                entities.Add(entity);
            }));
        }
    }
    public void RaiseCanExecuteChanged()
    {
        EventHandler handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

It is easy, right? In constructor we are loading movie titles from xml file and storing them in list. When Execute method is triggered we are getting random value from list, creating new entity and saving it in database. Take a look at CanExecute method, it 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<SimpleSyncEntity> entities = (ObservableCollection<SimpleSyncEntity>)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 (SimpleSyncEntity entity in transaction.GetModelSet<SimpleSyncEntity>())
                            {
                                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 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();
    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();
    }));
}
...

Congratulations our example is completed. You can test it now on as many devices as you can find.

Conclusion

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

Attachments: