none
Создание вьюхи WPF в бэкграунде RRS feed

  • Вопрос

  • Не знаю, туда или не туда пишу, но быть может кто поможет.

    Ситуация следующая.
    Проект на WPF.
    Есть форма. На ней отнаследованная Canvas.
    В Canvas этот надо запихнуть векторную графику.
    Графику эту создаю в отдельном классе, её и представляющий (отнаследован класс от DrawingVisual).
    При выполнении в одном потоке всё нормально - по нажатию кнопки обновляю класс вьюхи, и его в канву добавляю.
    Проблема как обычно - при большом количество векторов вьюха обнавляется долка, интерфейс виснет.
    Что ж, пытаюсь в потоке запустить. Пробовал и BackgroundWorker и обычные Thread с делегатами.
    Собственно проблема:
    во вьюхе есть строчка
    DrawingContext dc = RenderOpen();  //класс отнаследован от DrawingView по прежнему.
    Так вот, если я создаю класс в потоке формы, но в потоке пытаюсь получить контекст - получаю
     System.InvalidOperationException was unhandled
      Message="Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток."
    Если же создаю вьюху в потоке, то не могу её добавить в коллекцию элементов канвы, ибо опять таки потоки разные. Хотя и возвращается она через RunWorkerCompletedEventArgs.Result.
    Пробовал даже засовывать вьюху в статик какой-нибудь, и из статика забирать - тот же эффек.

    Это какая то особенность DrawingVisual?
    В том же приложении нормально использую другие потоки для других операций - всё гладко.
    • Перемещено Siddharth Chavan 1 октября 2010 г. 19:44 MSDN Forums Consolidation (От:Общая Архитектура)
    • Перемещено Abolmasov Dmitry 27 октября 2010 г. 11:12 (От:Разное)
    27 июля 2009 г. 19:56

Ответы

Все ответы

  • А если попробовать через Dispatcher сделать обращения к этому объекту?


    Program Manager, Microsoft Corporation
    6 августа 2009 г. 12:26
  • Тоже столкнулся с этой проблемой, просидел к ряду почти сутки, и пока нашёл только одно решение:

    Векторную графику, которая рисуется в отдельном потоке, можно отрендерить в изображение:

    DrawingVisual drawingVisual = RenderVectorGraphic();
    
    RenderTargetBitmap rtb = new RenderTargetBitmap(
       (int) Root.RenderSize.Width, (int) Root.RenderSize.Height, 96, 96, PixelFormats.Pbgra32);
    rtb.Render(drawingVisual);
    rtb.Freeze();
    В окне для отображения используем любой элемент, у которого есть Child (в данном случае Root - у меня это имя Border-а)
    А потом, через Dispatcher.BeginInvoke передаём rtb (ну или через args у BackgroundWorker-а) и уже там пишем:

    Image img = new Image();
    img.Stretch = Stretch.None;
    img.Source = rtb;
    Root.Child = img;
    Далеко не идеальный конечно вариант, но зато выходит переложить в background-поток достаточно крупную часть вычислений.

    Был ещё вариант с импользованием HostVisual. Суть вкратце была в том, что в окне создаются Wrapper-ы, которые хранят HostVisual, владельцами которых являются другие потоки.. Но у меня так не получилось. http://blogs.msdn.com/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx Там есть пример, исходники. Может и поможет.

    Есть подозрение, что можно попробывать при создании графики не закрывать DrawingContext, и передавать его и drawingVisual в место, в котом всё выводится в интерфейс, а уже в том месте и писать:

    drawingVisual =someDrawingVisual;
    drawingContext = someDC;
    drawingContext.Close();

    Но этот вариант я не проверял.
    23 декабря 2009 г. 9:35
  • По архитектуре WPF, обращение к методам и свойствам классов, отнаследованных от DispatcherObject (а это почти все классы WPF) возможно только из основного треда программы. Исключение делается для отнаследованных от Freezable, если они нахоятся в "замороженном" (т.е. "только для чтения") состоянии. Это нужно иметь в виду. Обращение к объектам следует производить через Dispatcher.Invoke(...) или Dispatcher.BeginInvoke(...).

     

    11 августа 2010 г. 0:11