I am creating a Window in another thread that is labeled STA. There are several controls and images in this window.
I than go in and close this window and open another window in the main thread of the user interface, I have a print dialog and use the following code to get FixedDocumentSequence :
var tempFileName = System.IO.Path.GetTempFileName(); File.Delete(tempFileName); using (var xpsDocument = new XpsDocument(tempFileName, FileAccess.ReadWrite, CompressionOption.NotCompressed)) { var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); writer.Write(this.DocumentPaginator); } using (var xpsDocument = new XpsDocument(tempFileName, FileAccess.Read, CompressionOption.NotCompressed)) { var xpsDoc = xpsDocument.GetFixedDocumentSequence(); return xpsDoc; }
In line:
writer.Write(this.DocumentPaginator);
I get an InvalidOperationException from an internal call to VerifyAccess , this is a StackTrace:
bei System.Windows.Threading.Dispatcher.VerifyAccess() bei System.Windows.Threading.DispatcherObject.VerifyAccess() bei System.Windows.Media.Imaging.BitmapDecoder.get_IsDownloading() bei System.Windows.Media.Imaging.BitmapFrameDecode.get_IsDownloading() bei System.Windows.Media.Imaging.BitmapSource.FreezeCore(Boolean isChecking) bei System.Windows.Freezable.Freeze(Boolean isChecking) bei System.Windows.PropertyMetadata.DefaultFreezeValueCallback(DependencyObject d, DependencyProperty dp, EntryIndex entryIndex, PropertyMetadata metadata, Boolean isChecking) bei System.Windows.Freezable.FreezeCore(Boolean isChecking) bei System.Windows.Media.Animation.Animatable.FreezeCore(Boolean isChecking) bei System.Windows.Freezable.Freeze() bei System.Windows.Media.DrawingDrawingContext.DrawImage(ImageSource imageSource, Rect rectangle, AnimationClock rectangleAnimations) bei System.Windows.Media.DrawingDrawingContext.DrawImage(ImageSource imageSource, Rect rectangle) bei System.Windows.Media.DrawingContextDrawingContextWalker.DrawImage(ImageSource imageSource, Rect rectangle) bei System.Windows.Media.RenderData.BaseValueDrawingContextWalk(DrawingContextWalker ctx) bei System.Windows.Media.DrawingServices.DrawingGroupFromRenderData(RenderData renderData) bei System.Windows.UIElement.GetDrawing() bei System.Windows.Media.VisualTreeHelper.GetDrawing(Visual reference) bei System.Windows.Xps.Serialization.VisualTreeFlattener.StartVisual(Visual visual) bei System.Windows.Xps.Serialization.ReachVisualSerializer.SerializeTree(Visual visual, XmlWriter resWriter, XmlWriter bodyWriter) bei System.Windows.Xps.Serialization.ReachVisualSerializer.SerializeObject(Object serializedObject) bei System.Windows.Xps.Serialization.DocumentPageSerializer.SerializeChild(Visual child, SerializableObjectContext parentContext) bei System.Windows.Xps.Serialization.DocumentPageSerializer.PersistObjectData(SerializableObjectContext serializableObjectContext) bei System.Windows.Xps.Serialization.ReachSerializer.SerializeObject(Object serializedObject) bei System.Windows.Xps.Serialization.DocumentPageSerializer.SerializeObject(Object serializedObject) bei System.Windows.Xps.Serialization.DocumentPaginatorSerializer.PersistObjectData(SerializableObjectContext serializableObjectContext) bei System.Windows.Xps.Serialization.DocumentPaginatorSerializer.SerializeObject(Object serializedObject) bei System.Windows.Xps.Serialization.XpsSerializationManager.SaveAsXaml(Object serializedObject) bei System.Windows.Xps.XpsDocumentWriter.SaveAsXaml(Object serializedObject, Boolean isSync) bei System.Windows.Xps.XpsDocumentWriter.Write(DocumentPaginator documentPaginator)
Since StackTrace makes a call to BitmapSource/BitmapDecoder , I was thinking of trying to remove the Images and set the in-place control source to null
<Image Source={x:Null} />
After I did this with all my images, my code worked smoothly and there were no more exceptions.
I tried to create an individual solution to solve this problem with the following:
public class CustomImage : Image { public CustomImage() { this.Loaded += CustomImage_Loaded; this.SourceUpdated += CustomImage_SourceUpdated; } private void CustomImage_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e) { FreezeSource(); } private void CustomImage_Loaded(object sender, System.Windows.RoutedEventArgs e) { FreezeSource(); } private void FreezeSource() { if (this.Source == null) return; var freeze = this.Source as Freezable; if (freeze != null && freeze.CanFreeze && !freeze.IsFrozen) freeze.Freeze(); } }
But I still get the error. Preferably, I am looking for a solution that works with all images in my WPF application.
I hope I made my clear, as it is rather strange to explain using two threads and a random exception at some point.
Edit: After some additional testing, I can now present you with a reproducible application with this problem, hope it becomes clearer with this.
You need 3 windows, 1 folder and 1 image. In my case its MainWindow.xaml Window1.xaml Window2.xaml
Images is the name of the folder, and there is an image in it with the name "plus.png".
MainWindow.xaml:
<StackPanel Orientation="Vertical"> <StackPanel.Resources> <Style TargetType="{x:Type Button}"> <Setter Property="Margin" Value="0,0,0,5"></Setter> </Style> </StackPanel.Resources> <Button Content="Open Window 1" Click="OpenWindowInNewThread" /> <Button Content="Open Window 2" Click="OpenWindowInSameThread" /> </StackPanel>
MainWindow.xaml.cs:
private void OpenWindowInNewThread(object sender, RoutedEventArgs e) { var th = new Thread(() => { SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher)); var x = new Window1(); x.Closed += (s, ec) => Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background); x.Show(); System.Windows.Threading.Dispatcher.Run(); }); th.SetApartmentState(ApartmentState.STA); th.IsBackground = true; th.Start(); } private void OpenWindowInSameThread(object sender, RoutedEventArgs e) { var x = new Window2(); x.Show(); }
Window1.xaml:
<StackPanel Orientation="Horizontal"> <ToggleButton Template="{StaticResource PlusToggleButton}" /> </StackPanel>
Window1.xaml.cs: There is no code there just a constructor ...
Window2.xaml:
<StackPanel Orientation="Horizontal"> <ToggleButton Template="{StaticResource PlusToggleButton}" /> <Button Content="Print Me" Click="Print"></Button> </StackPanel>
Window2.xaml.cs:
public void Print(object sender, RoutedEventArgs e) { PrintDialog pd = new PrintDialog(); pd.PrintVisual(this, "HelloWorld"); }
App.xaml:
<ControlTemplate x:Key="PlusToggleButton" TargetType="{x:Type ToggleButton}"> <Image Name="Image" Source="/WpfApplication1;component/Images/plus.png" Stretch="None" /> </ControlTemplate>
Steps to play:
- In MainWindow, click the "Open Window 1" button.
- A window will appear in the second user interface, close this window.
- Click the "Open Window 2" button
- A window will appear in the main user interface.
- Press the "Print Me" button, the application should crash
Hopefully now itβs easier to help me.
Edit2:
Missing piece of code added, sorry for this error.
Other information that can help solve the problem is that when you click the buttons in the reverse order - first window 2 and window 1 - and than trying to print, the exception will fire, so I still think that it caches some image problem when the image is first loaded into the main print based on UI-Thread, if it does not complete.