Telerik UI for Windows Phone by Progress

This topic will demonstrate how to use one context menu, but populate it with different menu items based on arbitrary criteria.

Declaring RadContextMenu

We will explore a usage similar to that of RadContextMenu that is shown in the Different Menus example in our examples application. It consists of a jump list, with a few records divided in groups. One context menu will be defined for the whole jump list and on opening, the contex menu will be populated with a list of commands based on the type of the highlighted jump list item: group, music or video. The first thing we have to do is to declare the XAML for the use case. It looks like this:

CopyXAML
<Grid x:Name="LayoutRoot">
    <telerikDataControls:RadJumpList x:Name="jumpList"
                                     Grid.Row="1"
                                     Margin="-12, 0, 0, 0">
        <telerikPrimitives:RadContextMenu.ContextMenu>
            <telerikPrimitives:RadContextMenu Opening="OnMenuOpening"
                                              x:Name="menu"/>
        </telerikPrimitives:RadContextMenu.ContextMenu>

        <telerikDataControls:RadJumpList.GroupHeaderTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Rectangle Width="90"
                               Height="90"
                               Fill="{StaticResource PhoneAccentBrush}"/>
                    <TextBlock Text="{Binding}" FontFamily="{StaticResource PhoneFontFamilySemiBold}"
                               FontSize="{StaticResource PhoneFontSizeSmall}"
                               Margin="10 ,-6, 0, 0"
                               Foreground="#339933"/>
                </StackPanel>
            </DataTemplate>
        </telerikDataControls:RadJumpList.GroupHeaderTemplate>

        <telerikDataControls:RadJumpList.ItemTemplate>
            <DataTemplate>
                <Grid Margin="60, 0, 0, 20">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>

                    <StackPanel Grid.Column="1"
                                HorizontalAlignment="Left"
                                VerticalAlignment="Top"
                                Margin="12, 0, 0, 0">
                        <TextBlock Text="{Binding Path=Title}" FontSize="{StaticResource PhoneFontSizeLarge}"
                                   Margin="0, -10, 0, 5"/>
                        <TextBlock FontSize="{StaticResource PhoneFontSizeSmall}"
                                   Foreground="#339933"
                                   Text="{Binding Path=AdditionalInfo}"/>
                    </StackPanel>
                </Grid>
            </DataTemplate>
        </telerikDataControls:RadJumpList.ItemTemplate>
    </telerikDataControls:RadJumpList>
</Grid>

With this in place we now need to implement the dynamic logic that will update RadContextMenu to have an appropriate data source for the currently highlighted item. We populate our jump list with data in our page's constructor and proceed to update RadContextMenu in the OnMenuOpening event handler:

CopyC#
public partial class MainPage : PhoneApplicationPage
{
    List<ICommand> movieCommands = new List<ICommand>()
    {
        new WatchTrailerCommand(),
        new BuyCommand(),
        new ShareCommand()
    };

    List<ICommand> musicCommands = new List<ICommand>()
    {
        new PlayMusicCommand(),
        new MoreFromArtistCommand(),
        new BuyCommand(),
        new ShareCommand()
    };

    List<ICommand> groupCommands = new List<ICommand>()
    {
        new BuyAllCommand()
    };

    public MainPage()
    {
        InitializeComponent();
        RadContextMenu.SetFocusedElementType(this.jumpList, typeof(RadDataBoundListBoxItem));

        List<ArtRecord> data = new List<ArtRecord>();
        data.Add(new ArtRecord() { Title = "For Liberty: Ron Paul", Type = ArtRecord.Movie, AdditionalInfo = "Genre: Documentary" });
        data.Add(new ArtRecord() { Title = "Sea Prince and the Fire Child", Type = ArtRecord.Movie, AdditionalInfo = "Genre: Anime & Animation" });
        data.Add(new ArtRecord() { Title = "Brand New Day", Type = ArtRecord.Music, AdditionalInfo = "Sting" });
        data.Add(new ArtRecord() { Title = "Ten Summoner's Tales", Type = ArtRecord.Music, AdditionalInfo = "Sting" });

        this.jumpList.GroupDescriptors.Add(new GenericGroupDescriptor<ArtRecord, string>((record) => record.Type));
        this.jumpList.ItemsSource = data;
    }

    private void OnMenuOpening(object sender, ContextMenuOpeningEventArgs e)
    {
        RadDataBoundListBoxItem item = e.FocusedElement as RadDataBoundListBoxItem;
        if (item == null)
        {
            e.Cancel = true;
            return;
        }

        if (item.Content is DataGroup)
        {
            this.menu.ItemsSource = this.groupCommands;
            return;
        }

        ArtRecord record = item.Content as ArtRecord;
        if (record.Type == ArtRecord.Movie)
        {
            this.menu.ItemsSource = this.movieCommands;
        }
        else
        {
            this.menu.ItemsSource = this.musicCommands; 
        }
    }
}

Notice in the OnMenuOpening event handler how RadContextMenu is dynamically bound to a different command list based on the type of the focused element. If the focused element is a group we use one list, if it is a music record another list, if it is a movie we use yet another list. And this is it, very simple and usable. Just give RadContextMenu an IEnumerable of ICommand and it just works. For the sake of completeness, the rest of the code is below. First the implementation of ArtRectord, then CommandBase on which all other commands are based and the actual concrete commands that implement the business logic for each menu item.

CopyC#
public class ArtRecord
{
    public static readonly string Movie = "MOVIES AND DVD";
    public static readonly string Music = "MUSIC ALBUMS";

    public string Type
    {
        get;
        set;
    }

    public string Title
    {
        get;
        set;
    }

    public string AdditionalInfo
    {
        get;
        set;
    }
}
CopyC#
public abstract class CommandBase<T> : ICommand where T : class
{
    public bool CanExecute(object parameter)
    {
        T typedParam = parameter as T;
        if (typedParam == null)
        {
            return false;
        }

        return this.CanExecuteCore(typedParam);
    }

    public event System.EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        T typedParam = parameter as T;

        if (this.CanExecuteCore(typedParam))
        {
            this.ExecuteCore(typedParam);
        }
    }

    protected virtual bool CanExecuteCore(T param)
    {
        return true;
    }

    protected abstract void ExecuteCore(T param);
}
CopyC#
public abstract class ArtRecordCommand : CommandBase<RadDataBoundListBoxItem>
{
    protected override bool CanExecuteCore(RadDataBoundListBoxItem param)
    {
        return param.Content is ArtRecord;
    }

    protected override void ExecuteCore(RadDataBoundListBoxItem param)
    {
        this.ProcessRecord(param.Content as ArtRecord);
    }

    protected abstract void ProcessRecord(ArtRecord record);
}

public class WatchTrailerCommand : ArtRecordCommand
{
    protected override void ProcessRecord(ArtRecord record)
    {
        Debug.WriteLine("Playing trailer for: " + record.Title);
    }

    public override string ToString()
    {
        return "watch the trailer";
    }
}

public class BuyCommand : ArtRecordCommand
{
    protected override void ProcessRecord(ArtRecord record)
    {
        BuyArtRecrod(record);
    }

    public static void BuyArtRecrod(ArtRecord record)
    {
        Debug.WriteLine("Buying record: " + record.Title);
    }

    public override string ToString()
    {
        return "buy";
    }
}

public class ShareCommand : ArtRecordCommand
{
    protected override void ProcessRecord(ArtRecord record)
    {
        Debug.WriteLine("Sharing record: " + record.Title);
    }

    public override string ToString()
    {
        return "share";
    }
}

public class PlayMusicCommand : ArtRecordCommand
{
    protected override void ProcessRecord(ArtRecord record)
    {
        Debug.WriteLine("Playing record: " + record.Title);
    }

    public override string ToString()
    {
        return "play music";
    }
}

public class MoreFromArtistCommand : ArtRecordCommand
{
    protected override void ProcessRecord(ArtRecord record)
    {
        Debug.WriteLine("Getting more for artist: " + record.AdditionalInfo);
    }

    public override string ToString()
    {
        return "more from the same artist";
    }
}

public class BuyAllCommand : CommandBase<RadDataBoundListBoxItem>
{
    protected override bool CanExecuteCore(RadDataBoundListBoxItem param)
    {
        return base.CanExecuteCore(param) && param.Content is DataGroup;
    }

    protected override void ExecuteCore(RadDataBoundListBoxItem param)
    {
        DataGroup artRecordGroup = param.Content as DataGroup;

        foreach (object item in artRecordGroup.Items)
        {
            BuyCommand.BuyArtRecrod(item as ArtRecord);
        }
    }

    public override string ToString()
    {
        return "buy";
    }
}