0

I'm a bit new to XAML and Caliburn.Micro. I ran into a "weird" issue.

I'm making a Windows Phone 8 application with Caliburn.Micro and I have a ListBox with items, which then includes another inner ListBox with some detail lines.

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,5,12,5" >
        <ListBox x:Name="CurrentStunts"
                 cal:Message.Attach="[Tap]=[Action OpenStuntOnWeb(CurrentStunts.SelectedItem)]">
            <ListBox.ItemTemplate>
                <DataTemplate>
                        <Grid DataContext="{Binding}" Background="#FFCC00" Margin="0,10,0,10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="45"/>
                            <ColumnDefinition Width="*" MinWidth="345"/>
                            <ColumnDefinition Width="40"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <StackPanel Grid.Column="0">
                             <StackPanel.Background>
                                <ImageBrush ImageSource="{Binding StuntDetails.Channel,Converter={StaticResource ChannelIconConverter}}"  Stretch="Uniform" AlignmentY="Top"/>
                             </StackPanel.Background>
                        </StackPanel>
                        <StackPanel Grid.Column="1" Orientation="Vertical">
                            <TextBlock Text="{Binding Title}" Foreground="#CC0000" TextWrapping="Wrap"/>
                            <TextBlock Text="{Binding SubTitle}" Foreground="#336699" TextWrapping="Wrap"/>
                            <ListBox ItemsSource="{Binding DetailLines}">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding}" Foreground="#336699" FontSize="14" TextWrapping="Wrap"/>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>

                        </StackPanel>
                        <StackPanel Grid.Column="2" Orientation="Vertical">
                            <TextBlock Text="{Binding StuntDetails.NewPrice}" Foreground="#CC0000" />
                                <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="1*"/>
                                        <RowDefinition Height="1*"/>
                                    </Grid.RowDefinitions>
                                    <TextBlock HorizontalAlignment="Left" Text="{Binding StuntDetails.OldPrice}" VerticalAlignment="Top" Grid.RowSpan="2" Foreground="#336699"/>
                                    <Border BorderThickness="0,0,0,1" BorderBrush="#336699" >
                                    </Border>
                                </Grid>
                        </StackPanel>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>

The code in the viewmodel with cal:Message.Attach="[Tap]=[Action OpenStuntOnWeb(CurrentStunts.SelectedItem)]" is the following:

        public void OpenStuntOnWeb(StuntSummary currentItem)
    {
        if (currentItem != null)
        {
            var uri = stuntManager.BuildStuntLink(currentItem.StuntDetails);

            var webBrowserTask = new WebBrowserTask();
            webBrowserTask.Uri = uri;
            webBrowserTask.Show();
        }
    }

The problem is when booting the app and clicking anywhere on the inner ListBox (DetailLines) the parameter "currentItem" in the method public void OpenStuntOnWeb(StuntSummary currentItem) is null. When clicking on the outer ListBox CurrentStunts (such as on Title or SubTitle) it works fine and the parameter is filled in.

Even stranger is when you click on the outer ListBox and then click on the inner ListBox it also works fine and the parameter is filled in. It is only when first clicking on the inner ListBox that "currentItem" is null.

I'm stuck on this so any help would be greatly appreciated.

Carlos Landeras
  • 11,025
  • 11
  • 56
  • 82

1 Answers1

0

The issue here is that you are responding to the Tap event. The tap event is most likely broadcast after the SelectedItem is set but since you are tapping on an inner control you aren't selecting any item from the outer listbox - you still get the Tap but not a SelectedItem (you might want to trySelectionChanged instead).

When tapping on the inner listbox, the outer listbox is responding to the Tap event but since you've not tapped the outer, selection isn't set and therefore a null value is passed to the method.

Edit:

Ok using CM SpecialValues and the code I posted here: RadMenu and RadMenuItem Caliburn.Micro

You can extract the DataContext of the item that originally invoked the event (the ListBoxItem initiates the event and bubbles it to the ListBox which fires it's Tap event so the OriginalSource for the routed event will be the ListBoxItem hopefully - either way the item should have the right DataContext!)

Then you can just use

cal:Message.Attach="[Event Tap] = [Action DoSomething($originalsourcedatacontext)]"

So to summarise:

Add this to CM bootstrapper (Configure method usually best place)

MessageBinder.SpecialValues.Add("$originalsourcedatacontext", (context) =>
{
    if (context.EventArgs is EventArgs)
    {          
        var e = context.EventArgs as EventArgs;

        // If the control is a FrameworkElement it will have a DataContext which contains the bound item
        var fe = e.OriginalSource as FrameworkElement;

        if (fe != null)
            return fe.DataContext;
    }

    return null;
});
Community
  • 1
  • 1
Charleh
  • 13,749
  • 3
  • 37
  • 57
  • Hello, Thanks for the reply. I gave it a shot but no luck. It now will only open when clicking the outer ListBox. Previously once the outer ListBox was clicked I could press back and press another item even in the inner ListBox and it would work. Also SelectionChanged posed another problem: when people click it, they press back and click it again, the event will logically not fire. – user2346814 May 03 '13 at 14:26
  • Can you post some screenshots of the layout and detail the expected behaviour a bit more? I'm trying to work out what you really want it to do, and since you are binding to `ItemsSource` each item is already contained in the `DataContext` of child controls, so maybe you can move the `Tap` event to lower down, or take the datacontext of the tapped item? Might be possible to use CMs `SpecialValues` to get the datacontext of the tapped item - I answered another question before with an example, let me see if I can find it – Charleh May 03 '13 at 14:38
  • Found it - info in here: http://stackoverflow.com/questions/15913380/radmenu-and-radmenuitem-caliburn-micro/15921851#15921851 - If you were to use that and grab the `DataContext` from the sender of the action you could extract the bound item and that would automatically get passed to the method - I'll edit the answer a bit – Charleh May 03 '13 at 14:46
  • Hey Charleh. Thanks a lot for your help ! In the meantime I've realized I'm an idiot :) I had a sublist of items that was just plain text and I also had an object specifically to hold my view object. So what I did not was just string all the text together in 1 "details" property of my view object and just display that with a textblock. I'm going to mark your reply as answer though, it might help someone who has a better reason to have a sublist :) Thanks again ! – user2346814 May 03 '13 at 15:16