Saturday, January 5, 2013

How to make context menu work for windows phone?

How to make context menu work for windows phone?

I am using ContextMenu from Windows Phone Control toolkit. Wondering how do I know which list item in the list is pressed? It seems I can know which context menu is selected but I have no way to know which list item is operated on. Please help. Thanks!

        <DataTemplate x:Key="ListItemTemplate">             <StackPanel Grid.Column="1" VerticalAlignment="Top">                 <TextBlock Tag="{Binding Index}"  Text="{Binding SName}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" />               <toolkit:ContextMenuService.ContextMenu>                 <toolkit:ContextMenu>                   <toolkit:MenuItem Header="Add to playlist" Click="Move_Click"/>                 </toolkit:ContextMenu>               </toolkit:ContextMenuService.ContextMenu>             </StackPanel>          private void Move_Click(object sender, RoutedEventArgs e)     {         String name = (string)((MenuItem)sender).Header;         // how to know which index of the item is targeted on     } 

Answers & Comments...

Answer: 1

The answer is: MVVM. Don't use the event registration in code-behind rather have a command be invoked in the ViewModel. First, Instead of binding to a Data object, bind to a ViewModel (or a ViewModel representing a single list item if you're in a list). Then instead of using the Click event, have your ViewModel expose a Command that can be invoke directly on the databound VM.

Here's a simplified example from my United Nations News OSS WP7 app: (XAML, C#)

<DataTemplate x:Key="ArticleItemDataTemplate">     <StackPanel>         <toolkit:ContextMenuService.ContextMenu>             <toolkit:ContextMenu>                 <toolkit:MenuItem Command="{Binding NavigateToArticle}" Header="read article"/>                 <toolkit:MenuItem Command="{Binding ShareViaEmail}" Header="share via email"/>                 <toolkit:MenuItem Command="{Binding ShareOnFacebook}" Header="share on facebook"/>                 <toolkit:MenuItem Command="{Binding ShareOnTwitter}" Header="share on twitter"/>             </toolkit:ContextMenu>         </toolkit:ContextMenuService.ContextMenu>         <TextBlockText="{Binding Title}">     </StackPanel> </DataTemplate> 
public ICommand ShareOnTwitter {     get     {         return new RelayCommand(() =>              IoC.Get<ISocialShareService>().ShareOnTwitter(ShareableOnSocialNetwroks));     } }  public ICommand ShareOnFacebook {     get     {         return new RelayCommand(() =>             IoC.Get<ISocialShareService>().ShareOnFacebook(ShareableOnSocialNetwroks));     } }  public ICommand ShareViaEmail {     get     {         return new RelayCommand(() =>             IoC.Get<ISocialShareService>().ShareViaEmail(ShareableOnSocialNetwroks));     } } 

And here's another simplified sample of that same idea used in my Neurons WP7 OSS project: (XAML, C#)

    <DataTemplate x:Key="YouTubeVideoItem">         <Grid>             <Button >                 <toolkit:ContextMenuService.ContextMenu>                     <toolkit:ContextMenu IsZoomEnabled="False">                         <toolkit:MenuItem Command="{Binding NavigateToVideo}" Header="play video" />                         <toolkit:MenuItem Command="{Binding ViewInBrowser}" Header="open in browser" />                         <toolkit:MenuItem Command="{Binding SendInEmail}" Header="share via email" />                         <toolkit:MenuItem Command="{Binding FacebookInBrowser}" Header="share on facebook" />                         <toolkit:MenuItem Command="{Binding TweetInBrowser}" Header="share on twitter" />                     </toolkit:ContextMenu>                 </toolkit:ContextMenuService.ContextMenu>                 <Custom:Interaction.Triggers>                     <Custom:EventTrigger EventName="Click">                         <GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding NavigateToVideo}"/>                     </Custom:EventTrigger>                 </Custom:Interaction.Triggers>                 <StackPanel Orientation="Horizontal">                     <Image Height="90" Source="{Binding ImageUrl}" />                     <TextBlock Width="271" Text="{Binding Title}" />                 </StackPanel>             </Button>         </Grid>     </DataTemplate> 
    public ICommand ViewInBrowser     {         get         {             return new RelayCommand(() =>                 TaskInvoker.OpenWebBrowser(this.ExternalLink.OriginalString)             );         }     }      public ICommand TweetInBrowser     {         get         {             return new RelayCommand(() =>                 IoC.Get<IMessenger>().Send(new NavigateToMessage(PageSources.WebBrowser, TwitterUri)));         }     }      public ICommand FacebookInBrowser     {         get         {             return new RelayCommand(() =>                 IoC.Get<IMessenger>().Send(new NavigateToMessage(PageSources.WebBrowser, FacebookUri)));         }     } 
by : JustinAngelhttp://stackoverflow.com/users/81687

Answer: 2

I would also recommend MVVM, but it can be simplified. No need to go to the extreme of having a ViewModel for every object. The key is to bind the command to the DataContext of your ItemsControl (eg ListBox).

Let's assume that your ItemTemplate is for a ListBox, the ListBox has it's ItemsSource property bound to your ViewModel. Your xaml would look like this:

<ListBox x:Name="SongsListBox" ItemsSource="{Binding Songs}">     <ListBox.ItemTemplate>         <DataTemplate >             <StackPanel >                 <TextBlock Text="{Binding SName}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" />                 <toolkit:ContextMenuService.ContextMenu>                    <toolkit:ContextMenu>                        <toolkit:MenuItem Header="Add to playlist" Command="{Binding DataContext.AddToPlaylistCommand, ElementName=SongsListBox}" CommandParameter="{Binding}"/>                    </toolkit:ContextMenu>                 </toolkit:ContextMenuService.ContextMenu>             </StackPanel>                       </DataTemplate>     </ListBox.ItemTemplate> </ListBox> 

Your ViewModel, would then have the property Songs, that is a collection of your model object. It also has an ICommand AddToPlaylistCommand. As I've said before, my favorite implementation of ICommand is the DelegateCommand from the PNP team.

public class ViewModel : INotifyPropertyChanged {     public ViewModel()     {         Songs = new ObservableCollection<Songs>();         AddToPlaylistCommand = new DelegateCommand<Song>(AddToPlaylist);     }     public ICollection<Songs> Items { get; set; }     public ICommand AddToPlaylistCommand  { get; private set; }      private void AddToPlaylist(Song song)     {         // I have full access to my model!         // add the item to the playlist     }      // Other stuff for INotifyPropertyChanged } 
by : Shawn Kendrothttp://stackoverflow.com/users/1054961




No comments:

Post a Comment

Send us your comment related to the topic mentioned on the blog