2014-09-20 2 views
2

En essayant de rendre un canevas sur un autre fil. Voici ma tentative:Comment faire pour rendre un visuel sur un autre thread

// returns path to exported image 
private string exportToImage(double width, double height, Visual visual) 
{ 
    var filename = string.Format(@"{0}.png", Guid.NewGuid()); 
    var tempFile = Path.Combine(tempDir, filename); 
    Rect rect = new Rect(0, 0, width, height); 
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, 
     (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default); 

    Thread RENDER_THREAD = new Thread(() => 
    { 
     this.Dispatcher.Invoke((Action)(() => 
     { 
      rtb.Render(visual); 
     })); 
    }); 
    RENDER_THREAD.Start(); 

    //endcode as PNG 
    BitmapEncoder pngEncoder = new PngBitmapEncoder(); 
    pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); 

    //save to memory stream 
    System.IO.MemoryStream ms = new System.IO.MemoryStream(); 

    pngEncoder.Save(ms); 
    ms.Close(); 
    System.IO.File.WriteAllBytes(tempFile, ms.ToArray()); 
    return tempFile; 
} 

Je ne reçois aucune erreur mais le résultat rendu est une image vide. L'instance de rtb dans le thread principal et le nouveau thread est-elle la même?

Modifier: Voici ma dernière tentative.

je crée le fil sur un événement MouseUp:

var curLayer = GetItemsPanel((canvasDataBinding.ItemContainerGenerator.ContainerFromIndex(Binding_LayersListView.SelectedIndex))); 
Thread RASTERIZE_THREAD = new Thread(() => { exportToImage(curLayer.ActualWidth, curLayer.ActualHeight, curLayer); }); 
RASTERIZE_THREAD.Start(); 

et voici ma méthode le nouveau thread utilise:

void exportToImage(double width, double height, Visual visual) 
{ 
    var filename = string.Format(@"{0}.png", Guid.NewGuid()); 
    var tempFile = Path.Combine(tempDir, filename); 
    Rect rect = new Rect(0, 0, width, height); 
    RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, 
     (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default); 

    this.Dispatcher.Invoke(new Action(() => 
    { 
     rtb.Render(visual); 
    })); 

    //endcode as PNG 
    BitmapEncoder pngEncoder = new PngBitmapEncoder(); 
    pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); 

    //save to memory stream 
    System.IO.MemoryStream ms = new System.IO.MemoryStream(); 

    pngEncoder.Save(ms); 
    ms.Close(); 
    System.IO.File.WriteAllBytes(tempFile, ms.ToArray()); 
} 

Alors pourquoi est-il me dire que Le thread appelant ne peut pas accéder cet objet parce qu'un thread différent le possède sur rtb.Render(visual)? Je n'appelle pas exportToImage d'ailleurs, alors pourquoi Dispatcher n'est-il pas associé au thread que j'ai créé?

Modifier: J'avais besoin de créer le RebderTargetBitmap à l'intérieur de Dispatcher.Invoke().

+1

deux problèmes! utiliser le répartiteur dans le thread rend effectivement le thread inutile, d'autre part, il n'y a aucune garantie que le thread se termine avant que le résultat ne soit utilisé ici 'BitmapFrame.Create (rtb)' – pushpraj

+0

Je pense que je comprends. Donc dois-je créer un nouveau thread sur la méthode exportToImage, puis utiliser le répartiteur dans la méthode? – Infodayne

+0

cela prend-il trop de temps à rendre? Si non alors le fil est juste un frais généraux, si oui alors l'effort vaut la peine d'investir. deuxièmement, la méthode est synchrone car elle a besoin d'un fichier rendu à retourner, donc rendre la fonction de rendu asynchrone n'aidera pas car la méthode doit attendre jusqu'à ce que le rendu soit terminé. dans ce cas, la méthode devrait renvoyer void. – pushpraj

Répondre

2

est ici un code comment vous pouvez rendre le même dans un fil et faire async

idée

est

  • sérialiser le visuel dans la chaîne de XAML
  • créer un thread STA
  • alors laissez le fil désérialiser le même.
  • hôte visuel désérialisée dans un contrôle de contenu, et arrangeons etc.
  • dernier mais non le moindre, la rendre (prise de votre code)

ce code dans ne dépend pas de Dispatcher il est donc capable d'exécuter async complet

void exportToImage(double width, double height, Visual visual) 
    { 
     //this line can not be inside a thread because different thread owns the visual 
     string visualData = XamlWriter.Save(visual); 

     Thread t = new Thread(() => 
      { 
       var filename = string.Format(@"{0}.png", Guid.NewGuid()); 
       var tempFile = System.IO.Path.Combine("c:\\", filename); 

       Rect rect = new Rect(0, 0, width, height); 

       //create a host control to host the visual 
       ContentControl cc = new ContentControl(); 
       cc.Content = XamlReader.Parse(visualData); 
       //call Arrange to let control perform layout (important) 
       cc.Arrange(rect); 

       RenderTargetBitmap rtb = new RenderTargetBitmap((int)rect.Right, 
        (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default); 

       rtb.Render(cc); 

       //endcode as PNG 
       BitmapEncoder pngEncoder = new PngBitmapEncoder(); 
       pngEncoder.Frames.Add(BitmapFrame.Create(rtb)); 

       //save to memory stream 
       System.IO.MemoryStream ms = new System.IO.MemoryStream(); 

       pngEncoder.Save(ms); 
       ms.Close(); 
       System.IO.File.WriteAllBytes(tempFile, ms.ToArray()); 
      }); 
     //STA is necessary for XamlReader.Parse, and proper rendering 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(); 
    } 

J'ai testé avec succès ce code pour rendre quelques visuels de test. mais les résultats peuvent varier en fonction de la façon dont vous utilisez. comme une estimation de liens & images raster peuvent avoir quelques problèmes, mais dans mes tests, il a travaillé comme un charme.

+0

Je l'ai eu pour travailler sur certains de mes visuels, mais je ne peux pas l'obtenir pour travailler sur une toile avec plusieurs enfants (contrôles d'image). J'ai lu cet article http://social.msdn.microsoft.com/forums/windowsapps/en-us/b1aef630-bad2-41cc-b413-be6b12548e90/save-canvas-and-its-children-to-xaml-file et Je ne pense pas que je peux sauver ma toile. Des idées? – Infodayne

+0

pour Canvas vous devrez peut-être définir la hauteur et la largeur explicitement sur le canevas, car la hauteur par défaut largeur de la toile est 0. est-il possible pour vous de partager un échantillon de travail de votre code, puis-je essayer. – pushpraj

+0

J'ai essayé de simplement mon code autant que je le pouvais, voici l'application. http://www68.zippyshare.com/v/88730897/file.html Vous avez seulement besoin de regarder MainWindow.xaml et Xenoblend.cs – Infodayne

Questions connexes