Dynamic PopupMenu from RiaServices

Jul 13, 2011 at 6:04 PM
Edited Jul 14, 2011 at 10:59 AM

Having problems to create what you see here - http://gomerstemme.dnsalias.com/#Shows

When I hover over the buttons I have created a menu that displays related slideshowtitles. This is at the moment done static but I want to do it from a riaservice in SimpleMVVM app (http://simplemvvmtoolkit.codeplex.com/).

Anyone? How  do I do that?

Looks like this at the moment

 <Button x:Name="btn2010" Content="2010" Grid.Row="0" Grid.Column="1" Style="{StaticResource Beveled}" />
 <my:PopupMenu x:Name="___PopupMenu20010_" HoverElements="btn2010" ContentTemplate="{StaticResource DataTemplate1}" Background="{StaticResource BlackBrush}">
      <ListBox Style="{StaticResource TopMenuStyle1}" ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
          <my:PopupMenuItem VerticalSeparatorVisibility="Collapsed" Header="{Binding LocalizationResources.Carmen}" Style="{StaticResource PopupMenuTextStyle}" Click="onShowClick" Tag="80"/>
          <my:PopupMenuItem VerticalSeparatorVisibility="Collapsed" Header="{Binding LocalizationResources.Othello}" Style="{StaticResource PopupMenuTextStyle}" Click="onShowClick" Tag="81"/>
           <my:PopupMenuItem VerticalSeparatorVisibility="Collapsed" Header="{Binding LocalizationResources.Meistersinger}" Style="{StaticResource PopupMenuTextStyle}" Click="onShowClick" Tag="82" />
       </ListBox>
  </my:PopupMenu>

 

Kennet

Jul 15, 2011 at 6:30 AM
Edited Jul 20, 2011 at 2:21 AM

Hi Kennet,

Well I can't really advise you on the SimpleMVVM best practices since I haven't tried it yet but here is something close enough to what your are trying to achieve:

<ScrollViewer VerticalScrollBarVisibility="Auto" Canvas.ZIndex="1">
    <StackPanel Background="Black">
        <ItemsControl ItemsSource="0123456789">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <Button Name="btnYear" Content="{Binding}" FontSize="200" Margin="10"/>
                        <my:PopupMenu HoverElementSelector="btnYear" >
                            <ListBox>
                                <my:PopupMenuItem Header="{Binding}" VerticalSeparatorVisibility="Collapsed"/>
                            </ListBox>
                        </my:PopupMenu>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <toolkit:WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </StackPanel>
</ScrollViewer>

 Note that you can also choose to place the menu outside the data template as shown below:

<ScrollViewer VerticalScrollBarVisibility="Auto" Background="Black">
    <ItemsControl Name="itcYears" ItemsSource="0123456789">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Name="btnYear" Content="{Binding}" FontSize="50" Margin="10"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <toolkit:WrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>
<my:PopupMenu Name="mnuShows" HoverElementSelector="btnYear.*" InheritDataContext="True" >
    <ListBox ItemsSource="{Binding}">
        <my:PopupMenuItem Header="{Binding}" VerticalSeparatorVisibility="Collapsed"/>
    </ListBox>
</my:PopupMenu>

Note the use of the ".*" selector suffix. This tells the menu to match all elements that start with "btnYear". Finally you have to set UseTriggerElementDataContext to true to associate the datacontext with the selected item while opening the menu.

The only big deal with this setup is that you're only creating one menu for all the generated buttons. So its up to you to choose which one suits you better.

As for the ria services story you will have to create a class that will include the properties Year and Shows(of type List<string>) and create a service method that will return a list of those objects from you db. The purpose of this is to let you bind the button content to Year and the PopupMenuItem header to Shows, from yet another data template inside the ListBox(which I've omitted). 

I hope you got the big picture already. Feel free to ask me if you have any questions.

NB: You must have noticed that I used the HoverElementSelector property which I recently introduced in the latest change set. You can just rename it back to HoverElements however but I strongly recommend that you update your code accordingly because it contains a few bugs fixes related to the sample itself.

Kind regards

Ziad

Jul 16, 2011 at 7:39 AM
Edited Jul 16, 2011 at 12:06 PM

Hello Ziad and thanks a lot.

I did earlier on get the first listbox working (year) but not the second one (shows) - following your example.

And I can't get it to work with the menu outside of the data template.

Kennet

Jul 16, 2011 at 3:54 PM
Edited Jul 16, 2011 at 3:54 PM

Please verify the following for Shows:

1. It is of type List<string>.

2. It is filled with some sample data prior to sending to your view.

And again if you are using the menu outside the template you will want to upgrade your assembly to the latest version available here.

Good luck

Ziad

Jul 16, 2011 at 11:31 PM
Edited Jul 17, 2011 at 1:54 PM

Strange!

Done allt that. Got the data and downloaded latest version.

Displays all data when I use plain listboxes.

(There´s no problem showing the Year buttons).

Can you show me a codesample for both?

Kennet

 

 

Jul 20, 2011 at 2:05 AM
Edited Jul 20, 2011 at 2:23 AM


Hi Kennet,

Sorry for the lag. Ok this should work fine if your binding objects include the properties Year(int) and Shows(List<string>): 

<ScrollViewer VerticalScrollBarVisibility="Auto" Background="Black">
    <ItemsControl Name="itcYears" ItemsSource="0123456789">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Name="btnYear" Content="{Binding Year}" FontSize="50" Margin="10"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <toolkit:WrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>
<my:PopupMenu Name="mnuShows" HoverElementSelector="btnYear.*" InheritDataContext="True" >
    <ListBox ItemsSource="{Binding Shows}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <my:PopupMenuItem Header="{Binding}" VerticalSeparatorVisibility="Collapsed"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</my:PopupMenu>

Again make sure you get the latest bits else your code won't compile.

Ziad

Jul 20, 2011 at 4:20 PM

Hello and thanks!

Haven't tried this out yet.

The Year and Slideshow classes are returned as ObservableCollections.

Kennet

Jul 20, 2011 at 5:31 PM
Edited Jul 20, 2011 at 5:33 PM

Hello again Ziad!

It seems that the popupmenu isn't receiving events from the buttons.

The plain listbox I use as a reference shows the related shows for each year but the popupmenu never shows.

The year button is inside a listbox to be able to use Selection events.

Regards

Kennet

 

Jul 20, 2011 at 5:51 PM

The above xaml did work nicely here though.

It would help a lot if I got a peek at your xaml. That would spare me a lot of guess work!

Jul 20, 2011 at 6:06 PM
Edited Jul 20, 2011 at 6:10 PM



<navigation:Page x:Class="bengtgomer.se.Views.AlbumsView" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           mc:Ignorable="d"
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" 
                 xmlns:fxui="clr-namespace:SilverlightFX.UserInterface;assembly=SilverlightFX" 
                 xmlns:fxeffects="clr-namespace:SilverlightFX.UserInterface.Effects;assembly=SilverlightFX" 
                 xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
                 xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" 
                 xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" 
                 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
                 xmlns:my="clr-namespace:SL4PopupMenu;assembly=SL4PopupMenu" 
                 xmlns:local="clr-namespace:bengtgomer.se.Converters" d:DesignWidth="640" d:DesignHeight="480"
    DataContext="{Binding Source={StaticResource Locator}, Path=AlbumsViewModel}"
    Title="AlbumsView Page">
    
    <navigation:Page.Resources>
        <DataTemplate x:Key="AlbumsItemTemplate" >
            <Border HorizontalAlignment="Center" VerticalAlignment="Center" Cursor="Hand" RenderTransformOrigin="0.5,0.5">
                <Border.RenderTransform>
                    <ScaleTransform />
                </Border.RenderTransform>
                <fxui:Interaction.Behaviors>
                    <fxui:HoverEffect>
                        <fxeffects:Resize ScaleXRatio="1.05" ScaleYRatio="1.05" Duration="0:0:.25" />
                    </fxui:HoverEffect>
                </fxui:Interaction.Behaviors>
                <Button x:Name="btnAlbum" Content="{Binding Title}"  Style="{StaticResource Beveled}" />
            </Border>
        </DataTemplate> 
        <ItemsPanelTemplate x:Key="AlbumItemsPanel">
            <fxui:TilePanel TileHeight="65" TileWidth="200" UseAnimatedLayout="True" />
        </ItemsPanelTemplate>
        <DataTemplate x:Key="SlideshowItemTemplate">
            <TextBlock Text="{Binding Title}"/>
        </DataTemplate>
    </navigation:Page.Resources>
    
    <Grid x:Name="LayoutRoot">
        <Grid x:Name="grdContent" Width="600" Background="Transparent">
            <Grid.RowDefinitions>
                <RowDefinition Height="0.531*"/>
                <RowDefinition Height="0.469*"/>
            </Grid.RowDefinitions>
            <ListBox x:Name="lstAlbums"
                     Grid.Row="0"
                ItemsSource="{Binding Albums}" 
                ItemTemplate="{StaticResource AlbumsItemTemplate}" 
                Style="{StaticResource AutoWrap}" 
                ItemsPanel="{StaticResource AlbumItemsPanel}"
                SelectedItem="{Binding SelectedAlbum, Mode=TwoWay}"
                HorizontalAlignment="Center" Width="Auto" Background="Transparent" >
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <ei:CallMethodAction 
                            TargetObject="{Binding}"
                            MethodName="LoadSlideshowsByAlbum"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                </ListBox>
            <ListBox x:Name="lstSlideshows"
                     Grid.Row="1"
                    ItemsSource="{Binding Slideshows}"
                     ItemTemplate="{StaticResource SlideshowItemTemplate}"
                         SelectedItem="{Binding SelectedSlideshow,Mode=OneWay}">

            </ListBox>
            <my:PopupMenu Name="mnuShows" HoverElementSelector="btnAlbum.*" InheritDataContext="True">
                <ListBox ItemsSource="{Binding Slideshows}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <my:PopupMenuItem Header="{Binding Title}" VerticalSeparatorVisibility="Collapsed"/>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </my:PopupMenu>
        </Grid>

        <Grid>
            <!-- Bind IsBusy to IsBusy -->
            <toolkit:BusyIndicator Name="isBusyIndicator" Height="80" Width="200" 
                IsBusy="{Binding IsBusy}" Margin="152,39,148,-39" />
        </Grid>
    </Grid>
</navigation:Page>

Jul 20, 2011 at 6:28 PM
Edited Jul 20, 2011 at 6:45 PM

Oh, I missed something actually!

You need to call the UpdateTriggers method each time you bind your ListBox but this is only required if does not form part of the page load process.

I will probably automate this, via some DataContext change detection mechanism, in later releases though.

Regards

Ziad

Jul 20, 2011 at 6:47 PM

OK!

Is it UpdateSourceTrigger you mean?

And is it the Listbox binding for the popupmenuitem you refer to or...?

Kennet

Jul 20, 2011 at 7:06 PM

mnuShows.UpdateTriggers();

Jul 20, 2011 at 7:57 PM

Sorry!

Done that

No change

Regards

Kennet

Jul 20, 2011 at 9:31 PM
Edited Jul 20, 2011 at 10:00 PM

I haven't mentioned it before but you can also set the ListBox itself as target. The PopupMenu uses the inner item whenever it encounters a Selector based control.

This does not let you target individual items within the template but from what I saw in your code this shouldn't be an issue.

So in your case the HoverElementSelector property must be set to "lstAlbums".

Regards

Ziad

Jul 20, 2011 at 9:55 PM
Edited Jul 20, 2011 at 10:06 PM

Thank you for spending time on this issue.

When I use the Listbox as target and change from HoverElementSelector to RightClickElementSelector the items shows up in the popupmenu.

But only RightElementSelector is working??

Kennet

Jul 20, 2011 at 10:49 PM
Edited Jul 20, 2011 at 10:52 PM

{DELETED]

Jul 20, 2011 at 11:02 PM

Hmm...the hover event handler seemingly doesn't like ListBoxes anymore and this is what's been going on since the beginning IMO.

Thanks for pointing that out Kennet:)

Will let you know as soon as I get it fixed.

Regards

Ziad

Jul 23, 2011 at 2:31 AM
Edited Jul 23, 2011 at 2:31 AM

Hi Kennet,

Can you please tell me if the updated code fixed it?

Ziad

Jul 23, 2011 at 8:24 AM
Edited Jul 23, 2011 at 10:15 AM

Hello Ziad!

 

First none of the ElementSelectors were working. Occasionally the busyindicator showed (to get slideshow data) but nothing returned.

But...

...when I removed the mnuShows.UpdateTrigger() and  InheritDataContext="True" it seemed to be quite OK.

But now and then the busyindicator stays onscreen an no related slideshow returns but that seems to have to do with selecteditem/selectionchanged on the listbox and the button

 

Kennet

Jul 23, 2011 at 11:33 AM
Edited Jul 23, 2011 at 11:40 AM

Hi again,

I've included a sample using a ListBox in Demo1.xaml which is basically what you are trying to achieve.

It is working fine actually. Any idea on what might be causing the issue in your implementation?

Also note that if you target the buttons directly the menu will not manage the item selection of the ListBox.

To enable this just use the ListBox itself as target.

Ziad

Jul 23, 2011 at 12:05 PM
Edited Jul 23, 2011 at 12:26 PM

Hello again and thank you Ziad

I'm using the listbox as target and buttons are showed three in the each listbox row (it seems) via the ItemsPanelTemplate and...

        private void lstAlbums_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Set SelectedAlbum on view model
            if (viewModel.Albums != null &&
                lstAlbums.SelectedIndex > 0)
            {
                viewModel.SelectedAlbum = viewModel.Albums[lstAlbums.SelectedIndex];
                viewModel.LoadSlideshowsByAlbum();
            }
        }

...viewModel.LoadSlideshowsByAlbum() returns the same related slideshow data  for each row (three buttons) depending on with button I hover on first.

(I'm not using InheritDataContext)

Kennet

Jul 23, 2011 at 1:11 PM
Edited Jul 23, 2011 at 2:12 PM

After testing it seems that if I wait for a while before hovering over another button and/or clicking outside of the listbox correct related data is loaded.

And this

        <ItemsPanelTemplate x:Key="AlbumItemsPanel">
            <fxui:TilePanel TileHeight="65" TileWidth="200" UseAnimatedLayout="True" />
        </ItemsPanelTemplate>

is causing the problem

By using

        <ItemsPanelTemplate x:Key="AlbumItemsPanel">
            <toolkit:WrapPanel
                 Width="600" />
        </ItemsPanelTemplate>

it works

 

Kennet

Jul 23, 2011 at 11:11 PM
Edited Jul 23, 2011 at 11:13 PM

Ok, I modified the code to support any ItemsPanelTemplate you might fancy.

Jul 24, 2011 at 10:51 AM

Thank you so much

Kennet