Обновление изображения окна WPF из пункта меню, но не в цикле while

Ладно, это настоящая головная боль:

Если я выбираю элемент меню, который приводит к тому, что изображение, которое составляет все окно (writeableBitmap), имеет несколько пикселей, нарисованных на нем, оно делает это и отображается правильно.

Однако, если я добавлю цикл while (скажем, для 5 циклов) к тому же методу, рисунок на растровом изображении НЕ ОТОБРАЖАЕТСЯ до тех пор, пока цикл не будет завершен, а затем правильно отобразится 5-е перерисованное растровое изображение.

Итак, есть ли какое-то «автоматическое обновление», которое происходит с окном, когда пункт меню выбран, но пропускается в цикле while?

Подробнее. Это отлично работает (выводит «чистое» изображение, рисует на нем что-то, отображает его):

// This brings in a 'clean' image
writeableBitmap = new WriteableBitmap(CleanVegMap);
image.Source = writeableBitmap;
// This makes a bunch of draws on the bitmap
DrawDinos2d();

Это, однако, «уходит» на 10 секунд, а затем отображает только последнее (т.е. 5-е) изображение:

int z = 0;
while (z < 5){
z++;
   // This brings in a 'clean' image
   writeableBitmap = new WriteableBitmap(CleanVegMap);
   image.Source = writeableBitmap;
   // This makes a bunch of draws on the bitmap
   DrawDinos2d();
}

Новая идея: возможно ли, что 5 «нарисованных» writeableBitmap каким-то образом кэшируются в памяти системой?

Пробовал использовать Диспетчер (как показано ниже):

                Dispatcher.Invoke((Action)delegate
            {               
                writeableBitmap = new WriteableBitmap(CleanVegMap);
                image.Source = writeableBitmap;
                DrawDinos2d();
            });

То же самое (пропадает на 10 секунд и потом показывает только последнее изображение.

Еще одна подсказка: я просто поместил MessageBox в цикл внизу каждого цикла, и, как я почему-то подозревал, он правильно «слепил» перерисованный экран. Как-то:

 System.Windows.MessageBox.Show("Glarp!");

этот звонок «разбудил» систему. Опять же, есть идеи?


person zetar    schedule 18.07.2013    source источник
comment
У вас есть код, который вы можете показать нам?   -  person PoweredByOrange    schedule 18.07.2013
comment
Там тонна кода... к сожалению, не думаю, что это поможет... выложу фрагмент   -  person zetar    schedule 18.07.2013
comment
Просто добавил некоторый (надеюсь, релевантный) код.   -  person zetar    schedule 18.07.2013
comment
OMG.. Какого черта вы делаете попиксельно в WPF??   -  person Federico Berasategui    schedule 18.07.2013
comment
Потому что я рисую только около 40 пикселей при каждой перерисовке. У вас есть идея получше?   -  person zetar    schedule 18.07.2013
comment
Вы пытались поместить тело цикла в Dispatcher? Исходя из вашего вопроса, это то, что кажется вам ответом.   -  person Gayot Fow    schedule 18.07.2013
comment
Что такое Диспетчер? Никогда не слышал об этом. Я все еще думаю, что есть какая-то недействительность/перерисовка, которая может вызвать это, но я ее не нашел.   -  person zetar    schedule 18.07.2013
comment
Да, только что попробовал поместить тело цикла (а затем и весь метод) внутрь диспетчера. Все то же самое.   -  person zetar    schedule 18.07.2013
comment
Нет, вы помещаете его в диспетчер окна, а не в диспетчер WriteableBitmap. Отправка к ближайшему элементу, насколько это возможно. И асинхронно...   -  person Gayot Fow    schedule 18.07.2013
comment
Я действительно не понимаю, что вы имеете в виду. Я гуглю, асинхронный пример WPF Window Dispatch и ничего не придумываю.   -  person zetar    schedule 19.07.2013
comment
Как мне вызвать метод «асинхронно из диспетчера окна»? Я не могу найти ссылку или пример для этого.   -  person zetar    schedule 19.07.2013
comment
Я добавил фрагмент кода. Это должно помочь вам познакомиться с Dispatcher и в целом, что будет полезно для вас, потому что вы работаете с растровыми изображениями.   -  person Gayot Fow    schedule 19.07.2013
comment
Нет проблем с опозданием. Я знаю, что ты в Великобритании, так что я подумал, что ты ушла на пенсию на вечер.   -  person zetar    schedule 19.07.2013


Ответы (1)


Что произошло, когда вы вставили MessageBox в обработку и получили ожидаемые результаты, так это то, что поток пользовательского интерфейса имел шанс «догнаться», пока MessageBox был открыт. Таким образом, создается «иллюзия», что использование MessageBox внезапно заставило его работать, но за кулисами это были просто потоки, разбирающиеся в себе и очищающие свои очереди инструкций.

Чтобы создать тот же эффект программно, вы можете обновить растровое изображение с помощью такого метода (ETA: требуется .NET 4.5 Framework)...

    public void UpdateBitmap()
    {
        WriteableBitmap writeableBitmap = new WriteableBitmap
                                (100, 100, 96, 96, PixelFormats.Bgr32, null);
        writeableBitmap.Dispatcher.InvokeAsync(() =>
            {
                Console.WriteLine("work goes here");
            });
    }

Это запускает операцию асинхронно в потоке, с которым связан диспетчер растрового изображения, и дает пользовательскому интерфейсу возможность наверстать упущенное. В зависимости от полезной нагрузки вашего метода 'DrawDinos2d' вам ВОЗМОЖНО придется перенести обработку в фоновый поток и передавать ее потоку пользовательского интерфейса по частям. Но сначала начните с этого подхода.

ETA: в среде .NET 4.0 аналогия приведенному выше выглядит так...

    public void UpdateBitmap()
    {
        object[] objs = new object[] {null};
        WriteableBitmap writeableBitmap = new WriteableBitmap(
             100, 100, 96, 96, PixelFormats.Bgr32, null);
        writeableBitmap.Dispatcher.BeginInvoke((SendOrPostCallback)delegate
            {
                Console.WriteLine(@"work goes here");
            }, objs);
    }

Документация гласит: «Выполняет указанный делегат асинхронно с указанными аргументами в потоке, в котором был создан System.Windows.Threading.Dispatcher.».

person Gayot Fow    schedule 18.07.2013
comment
Выдает эту ошибку: Ошибка 1 «System.Windows.Threading.Dispatcher» не содержит определения для «InvokeAsync», и не удалось найти метод расширения «InvokeAsync», принимающий первый аргумент типа «System.Windows.Threading.Dispatcher». (вам не хватает директивы using или ссылки на сборку?) Я добавил ссылку на System.ServiceModel - person zetar; 19.07.2013
comment
@zetar Ага! Перейдите в свойства своего проекта и переключите параметр «Целевая платформа» на .NET Framework 4.5 и дайте мне знать. Асинхронность была добавлена ​​в версии 4.5 и не появлялась в предыдущих версиях. - person Gayot Fow; 19.07.2013
comment
Я использую Visual Studio 2010. В нем есть только .NET Framework 4. Если я нажму «Установить другие фреймворки»… я попаду сюда: msdn.microsoft.com/en-US/vstudio/hh487282.aspx и самый высокий фреймворк, который я могу загрузить, это .NET 4.0.3. Где 4,5? - person zetar; 19.07.2013
comment
Я ТОЛЬКО ПРОЧИТАЛ (здесь: stackoverflow.com/questions/12390175/) Вы должны использовать Visual Studio 2012, чтобы использовать .NET 4.5. Я использую VS 2010. - person zetar; 19.07.2013
comment
Какой позор. Я изменил свой ответ, включив в него аналог 4.0. Проверьте это и посмотрите, работает ли это для вас, и дайте мне знать. - person Gayot Fow; 19.07.2013
comment
Возникает ошибка времени выполнения, когда я пытаюсь рисовать пиксели другим методом (вызывая поток... другой поток)... Я попытаюсь переместить Draw из UpdateBitmap. Нет... это не работает... есть проблемы с рисованием изображения, находящегося в другом потоке. Есть идеи? - person zetar; 19.07.2013
comment
Кажется, он не рисует правильное растровое изображение... или все еще находится в фоновом режиме. - person zetar; 19.07.2013
comment
не забудьте перекодировать код примера обратно в исходный... writeableBitmap = new WriteableBitmap(CleanVegMap); - person Gayot Fow; 19.07.2013