Telerik blogs
This is the second post from the blog series that aims to present you with the first ever cloud-power controls for Window Phone. If you have missed the first part, here is the link.

This post will show you how RadCloudJumpList, RadCloudDataForm and RadCloudPictureUpload can help you show a list of items; add new items; edit the existing ones and enhance them with pictures.

Recap

In the previous post, we created an application called Memories which aims to allow the users to store information and pictures about important events in their lives, such as a birthday party, a New Year celebration, etc. 

If you have followed the steps and created the application while reading the post you can continue from where we left and skip the current paragraph. Otherwise, you can quickly catch-up by downloading the application in its current state from here. You also need to go to Everlive.com, create a new storage app and fill its API key on the App.xaml.cs file as explained here.

The Memories app

Our Memories application has already handled the authentication routine and our next step is to add the memories themselves. First, create a new folder in the application in Visual Studio and name it Models. Then add a new class named Memory that will define the structure of each of the memories that will be saved by our application. This type will inherit from EverliveDataItem, so that it contains all necessary properties needed by Everlive. Add the following properties that we will use in the application: Title (of type string), Details (of type string), PictureUri (of type string) and Date (of type DateTime):

Memory.cs:
public class Memory : EverliveDataItem
{
    private string title;
    private string details;
    private string pictureUri;
    private DateTime date;
 
    public Memory()
    {
        this.Date = DateTime.Now;
    }
 
    public string Title
    {
        get
        {
            return this.title;
        }
        set
        {
            if (this.title != value)
            {
                this.title = value;
                this.OnPropertyChanged("Title");
            }
        }
    }
    public string Details
    {
        get
        {
            return this.details;
        }
        set
        {
            if (this.details != value)
            {
                this.details = value;
                this.OnPropertyChanged("Details");
            }
        }
    }
    public string PictureUri
    {
        get
        {
            return this.pictureUri;
        }
        set
        {
            if (this.pictureUri != value)
            {
                this.pictureUri = value;
                this.OnPropertyChanged("PictureUri");
            }
        }
    }
    public DateTime Date
    {
        get
        {
            return this.date;
        }
        set
        {
            if (this.date != value)
            {
                this.date = value;
                this.OnPropertyChanged("Date");
            }
        }
    }
}

Now the same structure needs to be defined on the Cloud. Go to Everlive.com and login with the credentials you used to create the application in the beginning. Select the Memories application that you have created in the beginning and click on “CREATE A CONTENT TYPE” to create a type with the following structure:



Now that the model is ready, we will need a page that will show the information that a Memory object contains. Here's how our page for viewing memories will look like:



Let’s add a new page to the to the Views folder of our application and name this page ViewMemory.xaml. We will use the NavigationContext’s QueryString to pass the id of a specific Memory between pages and display its information:

ViewMemory.xaml.cs:
public partial class ViewMemory : PhoneApplicationPage
{
    private Memory memory;
 
    public ViewMemory()
    {
        InitializeComponent();
    }
 
    protected async override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
 
        if (this.NavigationContext.QueryString.ContainsKey("id"))
        {
            string id = this.NavigationContext.QueryString["id"];
            Guid guid = new Guid(id);
            EverliveApp everliveApp = CloudProvider.Current.NativeConnection as EverliveApp;
            this.memory = await everliveApp.WorkWith().Data<Memory>().GetById(guid).ExecuteAsync();
            this.DataContext = this.memory;
            if (this.memory != null)
            {
                return;
            }
        }
 
        await RadMessageBox.ShowAsync("Memory not found!");
    }
}

 If the Memory is found it is set as a DataContext of the page, so now we just need to define how this information will be displayed:

ViewMemory.xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <ScrollViewer Margin="12,0">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
                     
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                         
                <TextBlock Text="{Binding Title}" TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeExtraLarge}"/>
                         
                <TextBlock Grid.Row="1" Text="{Binding Date, StringFormat=\{0:d\}}" Foreground="{StaticResource PhoneSubtleBrush}" FontSize="{StaticResource PhoneFontSizeSmall}"/>
                         
                <Border Grid.Column="1" Width="132" Height="132" Margin="12,0,0,0" VerticalAlignment="Top" Grid.RowSpan="2">
                    <Image Stretch="UniformToFill" Source="{Binding PictureUri}"/>
                </Border>
            </Grid>
                     
            <TextBlock Grid.Row="1" Margin="0,24" Text="{Binding Details}" TextWrapping="Wrap"/>
        </Grid>
    </ScrollViewer>
</Grid>


JumpList

Our next step is to create a page that will show all of the Memory objects that the currently logged user has created. When one of them is selected, we can navigate to the ViewMemory page to display it with more details. Here's how our Memories page will look when we add some items:



To display the appointments, we will use RadCloudJumpList. Go to the Memories page and add a new instance of this control with a proper ItemTemplate:

Memories.xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <telerikCloud:RadCloudJumpList Margin="12" x:Name="jumpList" ItemTap="OnItemTap">
        <telerikCloud:RadCloudJumpList.ItemTemplate>
            <DataTemplate>
                <Grid Margin="0,0,0,24">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
 
                    <StackPanel Margin="0,-14,0,0">
                        <TextBlock MaxHeight="56" FontSize="{StaticResource PhoneFontSizeExtraLarge}" FontFamily="{StaticResource PhoneFontFamilySemiLight}" Foreground="{StaticResource PhoneForegroundBrush}" Text="{Binding Title}" TextWrapping="Wrap"/>
                        <TextBlock MaxHeight="56" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" Text="{Binding Details}" Margin="0,-4,0,4" TextWrapping="Wrap"/>
                        <TextBlock FontSize="{StaticResource PhoneFontSizeSmall}" Text="{Binding Date, StringFormat=\{0:d\}}" Margin="0,-2,0,0" Foreground="{StaticResource PhoneSubtleBrush}" TextWrapping="Wrap"/>
                    </StackPanel>
 
                    <Border Grid.Column="1" Width="132" Height="132" VerticalAlignment="Top" Margin="12,0,0,0">
                        <Image Stretch="UniformToFill" Source="{Binding PictureUri}"/>
                    </Border>
                </Grid>
            </DataTemplate>
        </telerikCloud:RadCloudJumpList.ItemTemplate>
    </telerikCloud:RadCloudJumpList>
</Grid>

We would like out list to contain the items of type Memory that are created by the currently logged user. RadCloudJumpList will handle these requirements, when we create a cloud data service. This service will contain the filter that will not allow a user to see Memories created by other users. Another requirement that we have is to navigate to the ViewMemory page when we tap on an item and pass its id to the new page. Here’s what needs to be added to the Memories page:

Memories.xaml.cs:
EverliveCloudDataService<Memory> memoriesService;
 
public Memories()
{
    InitializeComponent();
    this.InitializeCloudService();
}
 
private void InitializeCloudService()
{
    Expression<Func<Memory, bool>> expression = memory => memory.CreatedBy == CloudProvider.Current.CurrentUser.GetId();
    this.memoriesService = new EverliveCloudDataService<Memory>();
    this.memoriesService.Filter = expression;
    this.jumpList.CloudDataService = memoriesService;
}
 
private void OnItemTap(object sender, Telerik.Windows.Controls.ListBoxItemTapEventArgs e)
{
    Guid itemId = (e.Item.Content as Memory).Id;
    this.NavigationService.Navigate(new Uri(string.Format("/Views/ViewMemory.xaml?id={0}", itemId), UriKind.RelativeOrAbsolute));
}

DataForm

Now we have the list of items, we can see each of them with more details, what's left for today is to add a routine to add, edit and delete these items. We can use  RadCloudDataForm to easily create or edit an item on the same page. Here's how this page will look in the add and edit modes:



Let’s add a new page to the Views folder and name it AddOrEditMemory. Add an instance of RadCloudDataForm:

AddOrEditMemory.xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <ScrollViewer>
        <telerikCloud:RadCloudDataForm Success="MemoryDataForm_Success" Failed="MemoryDataForm_Failed" x:Name="MemoryDataForm" CurrentItem="{Binding}">
            <Grid>
                <telerikInput:DataField TargetProperty="Title">
                    <telerikInput:DataField.Validators>
                        <telerikInput:NonEmptyStringValidator />
                    </telerikInput:DataField.Validators>
                </telerikInput:DataField>
                         
                <telerikInput:DataField TargetProperty="Date">
                    <telerikInput:DataField.EditorStyles>
                        <Style TargetType="telerikInput:RadDatePicker">
                            <Setter Property="OkButtonIconUri" Value="/Assets/AppBar/OK.png"/>
                            <Setter Property="CancelButtonIconUri" Value="/Assets/AppBar/Cancel.png"/>
                        </Style>
                    </telerikInput:DataField.EditorStyles>
                </telerikInput:DataField>
                         
                <telerikInput:DataField TargetProperty="Details">
                    <telerikInput:DataField.EditorStyles>
                        <Style TargetType="telerikPrimitives:RadTextBox">
                            <Setter Property="AcceptsReturn" Value="True"/>
                            <Setter Property="MinHeight" Value="182"/>
                            <Setter Property="TextWrapping" Value="Wrap"/>
                        </Style>
                    </telerikInput:DataField.EditorStyles>
                </telerikInput:DataField>
            </Grid>
        </telerikCloud:RadCloudDataForm>
    </ScrollViewer>
</Grid>

Make sure the necessary prefixes are corretly mapped to the following namespaces:

xmlns:telerikCloud="clr-namespace:Telerik.Windows.Controls.Cloud;assembly=Telerik.Windows.Controls.Cloud"
xmlns:telerikInput="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns:telerikPrimitives="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Primitives"
xmlns:telerikDataForm="clr-namespace:Telerik.Windows.Controls.DataForm;assembly=Telerik.Windows.Controls.Input"

Add an ApplicationBar with the Save/Cancel buttons:

<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar>
        <shell:ApplicationBarIconButton Text="save" IconUri="/Assets/AppBar/Save.png" Click="OnSave_Click"/>
        <shell:ApplicationBarIconButton Text="cancel" IconUri="/Assets/AppBar/Cancel.png" Click="OnCancel_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

Load the proper Memory similarly to the way it’s loaded in the ViewMemory page and add proper event handlers for Save and Cancel buttons clicks and the Success and Failed events of RadCloudDataForm. Since we want to use this page for new memories and for editing the existing ones as well, we will check if the NavigationContext’s QueryString contains “id”. If it does, we load the respective Memory, if not – load a new one and change the title of the page:

AddOrEditMemory.xaml.cs:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    Memory memory;
 
    if (this.NavigationContext.QueryString.ContainsKey("id"))
    {
        string id = this.NavigationContext.QueryString["id"];
        Guid guid = new Guid(id);
        EverliveApp everliveApp = CloudProvider.Current.NativeConnection as EverliveApp;
        memory = await everliveApp.WorkWith().Data<Memory>().GetById(guid).ExecuteAsync();
    }
    else
    {
        this.PageTitleBlock.Text = "add";
        memory = new Memory();
    }
 
    this.DataContext = memory;
}
 
private void OnSave_Click(object sender, EventArgs e)
{
    this.MemoryDataForm.Commit();
}
 
private void OnCancel_Click(object sender, EventArgs e)
{
    this.NavigationService.GoBack();
}
 
private void MemoryDataForm_Success(object sender, EventArgs e)
{
    PhoneApplicationService.Current.State["ReloadMemories"] = true;
    this.NavigationService.GoBack();
}
 
private async void MemoryDataForm_Failed(object sender, EventArgs e)
{
    await RadMessageBox.ShowAsync("Memory can’t be saved. Please try again later.");
}

When the update is successful, we navigate back to the previous page. Now we need to provide a way to tell the list on the previous page when the data is changed and it needs to be updated. To load items from the cloud only when necessary, we will add a property to the State of the application and in the Memories page use it to reload the items if necessary:

Memories.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
 
    if (e.NavigationMode == NavigationMode.Back && PhoneApplicationService.Current.State.ContainsKey("ReloadMemories"))
    {
        PhoneApplicationService.Current.State.Remove("ReloadMemories");
        jumpList.ReloadCloudItemsAsync();
    }
}

The rest of the logic for creating and updating items is handled by RadCloudDataForm. If the item is new, it will be automatically created on the Cloud, if it is existing, it will be properly updated.

Next thing we want to do, is to add a button that will enable us to create new items and edit the existing ones. This add new memory button can be part of the Memories page application bar:

Memories.xaml:
<shell:ApplicationBar.Buttons>
    <shell:ApplicationBarIconButton Text="new" IconUri="/Assets/AppBar/Add.png" Click="OnNewMemoryButton_Click"/>
</shell:ApplicationBar.Buttons>

Memories.xaml.cs:
private void OnNewMemoryButton_Click(object sender, EventArgs e)
{
    this.NavigationService.Navigate(new Uri("/Views/AddOrEditMemory.xaml", UriKind.RelativeOrAbsolute));
}

In the ViewMemory page’s Application bar we can buttons that will allow us to edit and delete memories:

ViewMemory.xaml:
<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar>
        <shell:ApplicationBarIconButton Text="edit" IconUri="/Assets/AppBar/Edit.png" Click="OnEditButton_Click"/>
        <shell:ApplicationBarIconButton Text="delete" IconUri="/Assets/AppBar/Delete.png" Click="OnDeleteButton_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

ViewMemory.xaml.cs:
private void OnEditButton_Click(object sender, EventArgs e)
{
    this.NavigationService.Navigate(new Uri(string.Format("/Views/AddOrEditMemory.xaml?id={0}", this.memory.Id), UriKind.RelativeOrAbsolute));
}
 
private async void OnDeleteButton_Click(object sender, EventArgs e)
{
    MessageBoxClosedEventArgs args = await RadMessageBox.ShowAsync("Delete memory?", "Deleting memory", MessageBoxButtons.OKCancel);
    if (args.Result == DialogResult.OK)
    {
        EverliveApp app = CloudProvider.Current.NativeConnection as EverliveApp;
        await app.WorkWith().Data<Memory>().Delete(this.memory.Id).TryExecuteAsync();
        PhoneApplicationService.Current.State["ReloadMemories"] = true;
        this.NavigationService.GoBack();
    }
}

PictureUpload

In order to add an image element to the Memory object, we will use RadCloudPictureUpload. It will help us upload an image file and associate it with the currently created or updated Memory.

Go to the AddOrEditMemory.xaml and add RadCloudPictureUpload as a custom editor for the PictureUri property, just before the DataField for editing the Title property:

AddOrEditMemory.xaml:
<telerikInput:DataField Header="" Margin="12" TargetProperty="PictureUri">
    <telerikInput:DataField.CustomEditor>
        <telerikDataForm:CustomEditor>
            <telerikCloud:RadCloudPictureUpload telerikDataForm:CustomDataField.IsEditor="True" telerikDataForm:CustomDataField.EditorValuePath="PictureUri" EmptyContent="no image" PixelHeight="300" PixelWidth="300" Height="132" Width="132" HorizontalAlignment="Left" >
                <telerikCloud:RadCloudPictureUpload.EmptyContentTemplate>
                    <DataTemplate>
                        <Grid Width="132" Height="132" Background="Gray">
                            <TextBlock VerticalAlignment="Bottom" Margin="12" FontFamily="{StaticResource PhoneFontFamilySemiLight}" FontSize="{StaticResource PhoneFontSizeLarge}" Text="{Binding}" TextWrapping="Wrap"/>
                        </Grid>
                    </DataTemplate>
                </telerikCloud:RadCloudPictureUpload.EmptyContentTemplate>
            </telerikCloud:RadCloudPictureUpload>
        </telerikDataForm:CustomEditor>
    </telerikInput:DataField.CustomEditor>
</telerikInput:DataField>

Conclusion

And that's all for today! Now we have an application where users can create their profile, authenticate, create, read, update and delete items and enhance them with images.

You can download the Memories application with its current progress here.

You can go to our online documentation and read more about the customization options for RadCloudJumpList, RadCloudDataForm and RadCloudPictureUpload.

The next blog post from the series will show you how to add more pictures to each of the memories and visualize them in a gallery with browsing, editing and sharing capabilities. You can read it here.

If you still haven't tried Cloud Controls for Windows Phone, you can read how to download the CTP here. Last week we’ve also released an Examples application which demonstrates common usage scenarios with all of the Cloud Controls.


TodorPhoto
About the Author

Todor Petrov

Todor Petrov has been with Telerik since 2011, working on the Android and Windows Phone UI suites. He is passionate about technologies, movies and music.

 



Related Posts

Comments

Comments are disabled in preview mode.