2015-01-16 10 views
5

Tôi đang tạo một Cửa sổ trong một chủ đề khác được đánh dấu STA cửa sổ này có một vài điều khiển và hình ảnh.Xử lý ảnh trong WPF không phải là chủ đề an toàn?

tôi hơn đi và đóng cửa sổ này và mở một cửa sổ khác trong giao diện người dùng chính Chủ đề trong này tôi có một hộp thoại in và sử dụng đoạn mã sau để có được một 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; 
} 

Trên dòng:

writer.Write(this.DocumentPaginator); 

tôi nhận được một InvalidOperationException từ một cuộc gọi nội bộ để VerifyAccess, đây là 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) 

Kể từ khi StackTrace hiện một số cuộc gọi đến BitmapSource/BitmapDecoder, tôi nghĩ về việc cố gắng để loại bỏ các hình ảnh và thiết lập nguồn của tại chỗ Hình ảnh điều khiển null

<Image Source={x:Null} /> 

Sau khi tôi đã làm điều này với tất cả hình ảnh của tôi mã của tôi đã chạy trơn tru và không có ngoại lệ nào khác được kích hoạt.

tôi đã cố gắng để thực hiện một customimage để giải quyết vấn đề này như sau:

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(); 
    } 
} 

Nhưng tôi vẫn nhận được lỗi. Ưu tiên tôi đang tìm kiếm một giải pháp hoạt động trên tất cả các hình ảnh trong ứng dụng WPF của tôi.

Hy vọng tôi đã tự làm rõ, vì điều này khá kỳ quặc để giải thích với 2 chủ đề và một ngoại lệ ngẫu nhiên tại một số điểm.

Edit: Sau một số thử nghiệm hơn nữa bây giờ tôi có thể trình bày cho bạn một ứng dụng tái sản xuất với vấn đề trước mắt, hy vọng rõ ràng của nó với điều này.

Bạn cần 3 cửa sổ, 1 thư mục và 1 hình ảnh. Trong trường hợp của tôi MainWindow.xaml Window1.XAML Window2.xaml nó

Images là tên của thư mục, và trong đó là một hình ảnh được gọi là "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: Không có mã nào trong đó chỉ có hàm tạo ...

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> 

Các bước để tạo lại:

  1. Trong MainWindow hãy nhấp vào nút có nội dung "Mở cửa sổ 1".
  2. Cửa sổ sẽ bật lên trong Giao diện người dùng thứ hai thứ hai, đóng cửa sổ này.
  3. Nhấp vào nút nói "Open Window 2"
  4. Một cửa sổ sẽ bật lên trong chính UI-Thread
  5. Nhấn nút nói "In Me", ứng dụng sẽ sụp đổ

Hy vọng của nó bây giờ dễ dàng hơn để giúp tôi.

Edit2:

Added phần đang mất tích, xin lỗi về sai lầm này.

Thông tin khác có thể giúp giải quyết sự cố là khi bạn nhấp vào các nút theo thứ tự đảo ngược - cửa sổ thứ nhất 2 và cửa sổ 1 - và hơn cố gắng in không có ngoại lệ nào sẽ kích hoạt, vì vậy tôi vẫn tin rằng một số hình ảnh của nó vấn đề bộ nhớ đệm, rằng khi hình ảnh đầu tiên được tải vào giao diện người dùng chủ đề giao diện người dùng chính nếu nó không thành công.

+0

Bạn có thể cố gắng để đóng băng những hình ảnh ban đầu ở nơi đầu tiên. Thay vì tải chúng từ một Uri, bạn sẽ tự lấy chúng từ web (ví dụ như một WebClient) và khởi tạo một BitmapImage từ một MemoryStream chứa bộ đệm đã tải xuống. Bạn có thể ngay lập tức đóng băng chúng sau khi tạo BitmapImage. Tất nhiên bạn cũng có thể làm điều này trong một chủ đề khác. – Clemens

+0

Làm cách nào để tôi có thể cố định hình ảnh gốc? Điều duy nhất tôi làm là tôi chỉ định hình ảnh trong XAML với , tôi không sử dụng bất kỳ WebClient hoặc tải hình ảnh một cách không đồng bộ, tôi chỉ đơn thuần là có một cửa sổ trong một chuỗi riêng biệt hiển thị hình ảnh, khi tôi đóng cửa sổ đó và quay lại giao diện người dùng chính và cố gắng in cùng một hình ảnh bị treo. Tôi có xóa hình ảnh bằng {x: Null} mọi thứ hoạt động không. –

+0

@Clemens quên [at] trong nhận xét cuối cùng của tôi, vui lòng xem thông báo của tôi về câu hỏi này –

Trả lời

3

Bạn đang sử dụng đối tượng giao diện người dùng được tạo bởi Window1 trên Window2.

Về cơ bản, các phần của ControlTemplate được chia sẻ giữa các chủ đề, trong đó không nên xảy ra (cụ thể là BitmapImage, như tôi đã đọc từ callstack của bạn).

Bạn explictly có thể nói rằng không chia sẻ:

<ControlTemplate x:Key="PlusToggleButton" 
        TargetType="{x:Type ToggleButton}" 
        x:Shared="False"> 
+0

Xin vui lòng nói rằng đây là một lời nói dối, xin vui lòng không nói rằng tất cả tôi cần làm là thiết lập một boolean và tất cả mọi thứ là tốt ... Và tôi giữ đập đầu của tôi chống lại bảng trong gần một tuần ... Gona kiểm tra nó đúng sớm ... –

+0

Hoạt động tốt, gona đọc chủ đề này nếu tôi có thêm câu hỏi tôi sẽ quay lại. Hiện tại tôi tự hỏi về vấn đề hiệu suất nếu tôi chỉ ngẫu nhiên đặt x: Shared = "false" trên mọi điều khiển tôi có?!? –

+0

Tôi đã đọc https://msdn.microsoft.com/en-us/library/aa970778(v=vs.110).aspx và tôi không thực sự hiểu tại sao ngoại lệ không xuất hiện khi mở Cửa sổ 2?!? Nếu tôi hiểu nó một cách chính xác nó sẽ xảy ra, bởi vì cửa sổ 2 cũng truy cập vào cùng một tài nguyên và xây dựng các nút/hình ảnh vậy tại sao chỉ in ấn các trường hợp ngoại lệ?!? Bạn có bất cứ lời giải thích cho nó? –

3

Ngoại lệ cho thấy bạn đang cố gắng truy cập một điều khiển (về cơ bản là một lớp học có nguồn gốc từ DispatcherObject) bên ngoài chuỗi tạo ra nó!

Rất khó để đề xuất sửa mã dựa trên giải thích về chuỗi của bạn. Nhưng một quy tắc đơn giản là đảm bảo rằng bạn tạo một điều khiển bất khả tri của giao diện người dùng trong chuỗi giao diện người dùng và cũng thực hiện tương tự trong khi truy cập các thuộc tính của điều khiển đó.

nhìn vào mã

this.DocumentPaginator 

accessor Khách sạn này dường như được vi phạm truy cập chủ đề (có nghĩa là, Propery này đang được truy cập bởi một sợi mà không tạo ra nó).

Bạn có thể sử dụng đoạn mã sau để chạy bộ truy xuất tài sản trên thread UI (và bạn cũng sẽ cần phải chắc chắn rằng đối tượng đó được tạo ra trên thread UI)

Application.Current.Dispatcher.Invoke(
    new Action(() => { 
//Your code/method name here 
} 
)); 

Nếu khái niệm này là mới cho bạn, giá trị đọc này MSDN trang

đây là MSDN tham chiếu cho VerifyAccess

+0

Cảm ơn bạn đã "thông tin" của mình. Tôi sợ nói với bạn nhưng tất cả những gì bạn nói là điều tôi đã biết. Đó cũng là điều đầu tiên tôi thử gói phương thức in bên trong Dispatcher Invoke. Tôi cũng kiểm tra những gì CheckAccess trả về và nó luôn luôn trả về true, cho tất cả các phần tử mà tôi có thể tìm thấy theo cách đệ quy trong VisualTree. Sau khi tôi loại bỏ nguồn của hình ảnh với {x: Null} mọi thứ hoạt động tốt, vì vậy gợi ý của tôi là WPF tải hình ảnh trong chuỗi STA thứ hai và Giao diện người dùng chính thay vì cố gắng tải nó từ Cache và có ngoại lệ. Đó là lý do tại sao tôi đã thử cách tiếp cận trên của tôi. –

+0

Và tôi không truy cập bất kỳ hình ảnh trong mã, hoặc xử lý chúng trong bất kỳ cách nào điều duy nhất xảy ra là tôi hiển thị chúng trong giao diện người dùng hoặc in chúng, tất cả những gì tôi làm là trong XAML của tôi , và như tôi đã đề cập một khi tôi thay đổi nó thành mọi thứ đều ổn. –

+0

Vui lòng xem bản chỉnh sửa của tôi. –

-2

với chỉnh sửa hiện tại, vấn đề này là chắc chắn nhất vì "Print", Click handler, In tôi nút.Tôi đã thử mã này trong một dự án mới và tôi không thể làm cho nó sụp đổ, do đó, rất có thể lỗi trong chức năng In.

+0

Bạn không thể làm cho nó sụp đổ, bởi vì tôi quên để thêm các sự kiện in, bây giờ của nó bao gồm xin vui lòng có một cái nhìn một lần nữa, sẽ đánh giá cao nó. –

Các vấn đề liên quan