views:

83

answers:

2

I will preface this by stating that I am new to WPF programming and may be making multiple errors. Any insight that can be provided to help me improve in my skills are greatly appreciated.

I am working with a WPF application and am looping through a list of objects that contain properties that describe a document that should be built on the fly and automatically printed. I am attempting to display a small grid in the interface that shows the document being built before it is printed. This serves two purposes: one, it allows the user to see work being done by the application. Two, it renders the items on the screen so that I can then have something to actually print since WPF appears to not be able to load an image for printing dynamicaly without displaying it on the screen. In my code, I am setting the various elements in the grid and setting the visibility to visible. However, the UI is not updating and the printed document doesn't look as intended since the image never shows up on the screen.

Here is the XAML that I have set up

<Grid x:Name="LayoutRoot" Background="Black">
<Grid Name="previewGrid" Grid.Row="1" Grid.Column="1" Background="White"    Visibility="Hidden">
<Canvas Name="pageCanvas" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid Name="pageGrid" Width="163" Height="211">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="81.5"></ColumnDefinition>
                    <ColumnDefinition Width="81.5"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Name="copyright" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Bottom"></TextBlock>
                <Image Name="pageImage" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></Image>
            </Grid>
        </Canvas>
.....canvas for pages 2-4 not shown but structure is the same as for pageGrid.....
</Grid>
</Grid>    
</Window>

Here is the code behind that is supposed to set the elements.

Dim bmp as new Bitmapimage
previewGrid.Visibility = Windows.Visibility.Visible
pageURI = New Uri(pageCollection(i).iamgeURL, UriKind.Absolute)
            pageGrid.Visibility = Windows.Visibility.Visible
            bmp.BeginInit()
            bmp.UriSource = cardURI
            bmp.EndInit()
            pageImage.Source = bmp
            copyright.Text = copyrightText
            cardPreviewGrid.UpdateLayout()
' More code that prints the visual element pageCanvas
 previewGrid.Visibility = Windows.Visibility.Hidden

The code in codebehind loops through a number of times depending on how many different documents the user prints. Basically it builds a visual element for a page, prints an XPS version of it and then builds the next page and prints it, etc. Once all pages have been processed, the job is actually sent to the printer. The only purpose of this application is to let the user print these documents so there is not other task that they can do in the application while the documents print. I thought that putting this task in a background thread would help to update the UI but since I am trying to manipulate items directly on the UI thread it would appear that this option won't work for me. What am I doing wrong here and how can I improve the code so that I can get the behavior that I am trying to achieve?

A: 

I can't speak to the issues of printing, but your UI's not going to get updated at all while the UI thread is executing this code. It can't; it's busy executing this code.

If you want to dynamically update the UI while a long-running task is executed, you need to execute the task on a background thread and periodically use Invoke() to update objects on the UI thread - or, for a saner, easier to manage experience, use a BackgroundWorker.

There may be some totally different issue going on as well; I can't really tell from what you've posted. But I'm pretty sure that's a big part of your problem.

Edit

Jeez, maybe I should read your entire question, not just the bits of it that initially get my attention.

Okay, I don't know how viable this approach will be in your situation, but: The BackgroundWorker can raise events as its task runs; if you call ReportProgress, it will raise the ProgressChanged event. The ProgressChanged event runs on the UI thread, and I believe that the DoWork method blocks and waits until the event handlers are done.

What's a little odd about your situation is that the background process isn't really doing anything. Your DoWork method would basically just keep calling ReportProgress until it was done.

Another way to do this would be to use a timer. Put the images that you want to update the UI (or objects that load the images) into a queue, and set a timer so that its Tick event handler processes the first item in the queue. The event handler would stop the timer, process the current work item, and then start the timer again with an interval of a tenth of a second. The end result would be that after every image was processed, the UI would have a tenth of a second to process any pending UI events - like drawing the image on the screen.

Robert Rossney
A: 

Robert,

Thanks for the update. I looked at using a BackgroundWorker thread but since the work being done is actually updating the UI in order to create a visual element that I then turn into an XPS document, I'm a little at a loss as to how to do this work in a BackgroundWorker thread as the UI elements are owned by the UI thread and the BackgroundWorker can't directly modify the elements owned by the UI thread. The last time I did any serious programming was back in the days of VB6 so I'm behind the times and trying to come up to speed on new languages/methodologies. I figured that this is mainly a threading issue but none of the examples I'm seeing address what I'm trying to do, i.e., loading an image into a grid in an iterative manner. Do you or anyone else have a good resource for more information on how to use the BackgroundWorker thread?

BMcBride
See my edit; it may be helpful.
Robert Rossney
Your edit was helpful. I went back and dug deeper into the BackgroundWorker thread and realized that the best way to do this may be to download the image on the background worker thread and then pass the BitmapImage to the main UI thread on the ReportProgress or Completed events of the BackgroundWorker thread. I have written up a smaller test app to do this but I'm running into an issue where the reportprogress and the completed events are not firing. Any idea as to why when I call ReportProgress that the event doesn't fire or get handled?
BMcBride