Trying to render a ListViewItem to a bitmap inside a UserControl is crashing

389 views Asked by At

I'm working on a Universal App in which I'm trying to generate a bitmap from a selection so that I can show a fancy animation of the selected object moving from inside a SettingsFlyout, to a new location on the screen while the SettingsFlyout with the listview is transitioned away to a "details" one.

I've seen this code suggested elsewhere but it doesn't seem to work. I get a System.ArgumentException followed by "Value does not fall within the expected range." at the RenderAsync call. any Idea why that's happening? stepping through the code reveals the ListViewItem is properly found, but rendering it doesn't work. I saw another mention of ItemContainerGenerator but it generated a NullReferenceException.

private async void AnimateSelection(object sender, SelectionChangedEventArgs e)
{   
    var container =
        (sender as ListView).ContainerFromItem((sender as ListView).SelectedItem);

    var bitmap = new RenderTargetBitmap();
    await bitmap.RenderAsync(container as FrameworkElement);

    // go on to animate this by copying it to another grid
}

Anybody tried this before? What I want to do is to simple copy the Selected Item of a listview and use it on another Grid. let me know if I need to edit the question with more information.

EDIT: Upon a suggestion from another dev , I tried rendering the pageRoot stackpanel and another TextBox from the same page and that failed too. I think the issue is with the render itself - can I substitute this code with something else?

var bitmap = new RenderTargetBitmap();
await bitmap.RenderAsync(container as FrameworkElement);

EDIT: dev suggested dispatcher could be an issue, turns out dispatcher is handled differently in Universal Apps, so I tried to use it through that - no change

var container =
            (sender as ListView).ContainerFromItem((sender as ListView).SelectedItem);

        await this.dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
        {
            var bitmap = new RenderTargetBitmap();
            await bitmap.RenderAsync(container as FrameworkElement);

            var vm = this.DataContext as PersonViewModel;
            vm.TransitionImageSource = bitmap;
        });
2

There are 2 answers

2
Matt Lacey On

Without a repro of your code I can't say what's wrong but there's nothing fundamentally wrong with your approach.

The following works:

  • Create a new blank universal app.
  • Share the MainPage between both targets
  • Add this XAML:

    <StackPanel>
        <TextBlock>Some text</TextBlock>
        <Button Click="ChangeColor_OnClick">change color</Button>
        <ListBox x:Name="ListBox">
            <ListBoxItem>item 1</ListBoxItem>
            <ListBoxItem>item 2</ListBoxItem>
            <ListBoxItem>item 3</ListBoxItem>
        </ListBox>
    
        <Button Click="TakeScreenshot_OnClick">take screenshot</Button>
        <Button Click="TakeScreenshotOfSelected_OnClick">take screenshot of just selected item</Button>
        <Image x:Name="DisplayedScreenshot" Height="200" />
    </StackPanel>
    
  • Add this code behind:

    private void ChangeColor_OnClick(object sender, RoutedEventArgs e)
    {
        var rand = new Random();
    
        this.Background = new SolidColorBrush(new Color { A = 255, R = (byte)rand.Next(1, 255), G = (byte)rand.Next(1, 255), B = (byte)rand.Next(1, 255) });
    }
    
    private async void TakeScreenshot_OnClick(object sender, RoutedEventArgs e)
    {
        RenderTargetBitmap rtb = new RenderTargetBitmap();
    
        await rtb.RenderAsync(this as FrameworkElement);
    
        this.DisplayedScreenshot.Source = rtb;
    }
    
    private async void TakeScreenshotOfSelected_OnClick(object sender, RoutedEventArgs e)
    {
        RenderTargetBitmap rtb = new RenderTargetBitmap();
    
        await rtb.RenderAsync(this.ListBox.SelectedItem as UIElement);
    
        this.DisplayedScreenshot.Source = rtb;
    }
    
0
Jay Kannan On

I solved this issue by changing the SettingsFlyout to a UserControl and faking the effects of the SettingsFlyout which breaks the standard behaviour in the process. Looks like this is not really an Answer but there certainly IS a problem with SettingsFlyout and needs to be investigated thoroughly

Thanks Matt