2014-09-20 5 views
2

Im пытается сделать холст в другом потоке. Вот моя попытка:Как визуализировать визуализацию в другом потоке

// 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; 
} 

У меня нет ошибок, но полученный результат является пустым изображением. Является ли экземпляр rtb в основном потоке, а новый поток тем же?

Редактировать: Вот моя последняя попытка.

Я создаю нить на событие MouseUp:

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

и вот мой метод новый поток использует:

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

Так почему же это говорит мне, что Вызывающий поток не может получить доступ к этот объект, потому что другой поток владеет им на rtb.Render(visual)? Я не вызываю exportToImage из другого места, так почему Диспетчер не связан с созданным мной потоком?

Редактировать: Мне нужно было создать RebderTargetBitmap внутри Dispatcher.Invoke().

+1

две проблемы! использование диспетчера в потоке эффективно делает поток бесполезным, во-вторых, нет гарантии, что поток завершится до того, как результат будет использован здесь. «BitmapFrame.Create (rtb)» – pushpraj

+0

Я думаю, что понимаю. Так должен ли я создать новый поток в методе exportToImage, а затем использовать диспетчер внутри метода? – Infodayne

+0

Делать это слишком долго? если нет, то поток - это просто накладные расходы, если да, то усилия стоит инвестировать. во-вторых, метод является синхронным, так как ему нужен возвращенный файл рендеринга, поэтому сделать функцию рендеринга async не поможет, поскольку метод должен ждать завершения рендеринга. в этом случае метод должен возвращать void. – pushpraj

ответ

2

вот код, как вы можете сделать то же самое в потоке и сделать его асинхронной

Вся идея

  • сериализации зрительная в XAML строки
  • создать STA тему
  • затем разрешите десериализацию потоков.
  • хозяин десериализованное визуальный в элементе управления содержимым, и не Устройте т.д.
  • последнее, но не в последнюю очередь, сделать его (из кода)

этот код в не зависит от Dispatcher так в состоянии запускать полный асинхронный

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

Я успешно протестировал этот код, чтобы визуализировать некоторые визуальные эффекты. но результаты могут различаться в зависимости от того, как вы используете. поскольку привязки догадок & растровых изображений могут иметь некоторые проблемы, однако в моих тестах это работало как шарм.

+0

Я получил его для работы над некоторыми моими изображениями, но я не могу заставить его работать на холсте с несколькими дочерними элементами (элементами управления изображениями). Я прочитал этот пост http://social.msdn.microsoft.com/forums/windowsapps/en-us/b1aef630-bad2-41cc-b413-be6b12548e90/save-canvas-and-its-children-to-xaml-file и Я не думаю, что могу сохранить свой холст. Есть идеи? – Infodayne

+0

для Canvas вам может потребоваться установить высоту и ширину на холсте, потому что ширина по умолчанию ширины холста равна 0. Возможно ли, что вы можете поделиться рабочей моделью своего кода, могу ли я попробовать. – pushpraj

+0

Я пробовал просто мой код столько, сколько мог, вот приложение. http://www68.zippyshare.com/v/88730897/file.html Вам нужно всего лишь посмотреть MainWindow.xaml и Xenoblend.cs – Infodayne

Смежные вопросы