Здравствуйте!
Есть Canvas (WPF), заполняемый в процессе работы программы (в том числе изменяется и размер). Стоит задача экспортировать его в файл BMP/PNG.
Гугл помог найти несколько вариантов решения, но все они приводят к получению черного BMP файла или прозрачного PNG файла (замечу, что фон Canvas'a задан белым).
Например, код:
public static void SaveCanvasToFile(Canvas surface, string filename)
{
// Save current canvas transform
//Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
// Get the size of canvas
Size size = new Size(surface.Width, surface.Height);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
surface.UpdateLayout();
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// Create a file stream for saving imageusing (FileStream outStream = new FileStream(filename, FileMode.Create))
{
// Use png encoder for our data
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
}
Здравствуйте, czz, Вы писали:
czz> // Get the size of canvas czz> Size size = new Size(surface.Width, surface.Height); czz> // Measure and arrange the surface czz> // VERY IMPORTANT czz> surface.Measure(size); czz> surface.Arrange(new Rect(size)); czz> surface.UpdateLayout();
Вместо всего этого правильнее вызвать InvalidateMeasure и UpdateLayout. И далее использовать ActualWidth и ActualHeight канваса для создания битмэпа.
czz> // Create a render bitmap and push the surface to it czz> RenderTargetBitmap renderBitmap = czz> new RenderTargetBitmap( czz> (int)size.Width, czz> (int)size.Height, czz> 96d, czz> 96d, czz> PixelFormats.Pbgra32); czz> renderBitmap.Render(surface);
Думаю, здесь стоит тормознуть и загрузить битмэп в Image, чтобы быть уверенным в отсутствии ошибки в самом экспорте в файл.
Сделал — не помогло. При загрузке в Image получается прозрачный Image
Здравствуйте, MxMsk, Вы писали:
MM>Здравствуйте, czz, Вы писали:
MM>Вместо всего этого правильнее вызвать InvalidateMeasure и UpdateLayout. И далее использовать ActualWidth и ActualHeight канваса для создания битмэпа.
MM>Думаю, здесь стоит тормознуть и загрузить битмэп в Image, чтобы быть уверенным в отсутствии ошибки в самом экспорте в файл.
Интересно, что этот код, оказывается, нормально ест другую канву. Но чем она принципиально отличается от нужной, мне не понятно.
Та, которую алгоритм ест имеет Auto размеры, а нужная фиксированные, но изменяемые кодом вручную.
Вообще опишу задачу, чтобы предотвратить ненужные костыли.
Программа рисует WPF'ом различные графики на нескольких закладках TabControl'a. Задача — всех их экспортировать. Размеры Canvas'ов на закладках — Auto, поэтому, пока закладка не будет хоть раз открыта, на соответствующей ей канве ничего рисовать нельзя (неизвестны размеры, а даже если известны рендера все равно не будет, как я понимаю, пока не откроешь вкладку. Отлавливаю момент первого ресайза чтобы нарисовать изображение).
Ну вот я подумал что бы экспортировать сделаю канву на текущей закладке и проведу цикл "отрисовка-экмпорт" для всех графиков .
У меня есть ощущение, что куда-то закрались сильные костыли. Как лучше поступить?)
А во, заметил, что если целевую канву подвинуть в верхний левый угол, то вроде работает. Делается снимок как бы левого верхнего куска окна с размерами канвы. Из-за чего это может быть?
Здравствуйте, czz, Вы писали:
czz>Вообще опишу задачу, чтобы предотвратить ненужные костыли. czz>Программа рисует WPF'ом различные графики на нескольких закладках TabControl'a. Задача — всех их экспортировать. Размеры Canvas'ов на закладках — Auto, поэтому, пока закладка не будет хоть раз открыта, на соответствующей ей канве ничего рисовать нельзя (неизвестны размеры, а даже если известны рендера все равно не будет, как я понимаю, пока не откроешь вкладку. Отлавливаю момент первого ресайза чтобы нарисовать изображение). czz>Ну вот я подумал что бы экспортировать сделаю канву на текущей закладке и проведу цикл "отрисовка-экмпорт" для всех графиков . czz>У меня есть ощущение, что куда-то закрались сильные костыли. Как лучше поступить?)
В TabControl-е есть засада в том, что контент невыбранных вкладок не входит в дерево элементов. Это значит, что часть биндингов и ресурсов может просто не действовать, пока вкладка неактивна. Далее, ты уверен, что элементы вкладки действительно лежат на Канвасе, а не просто расположены в соседней ветви дерева? Приведи хотя-бы примерный код, каким образом определяется содержимое вкладки, где там размещается Канвас.
Я не работаю с WPF, но могу отметить, что такое обычно происходит при попытке рендерить в контекст(канву), в котором выбрана монохромная битовая карта. Откуда Canvas взята и что в ней за карта ?
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Я не работаю с WPF, но могу отметить, что такое обычно происходит при попытке рендерить в контекст(канву), в котором выбрана монохромная битовая карта. Откуда Canvas взята и что в ней за карта ?
Тут такая нестыковочка. Canvas в WPF — это не то, что в большинстве остальных Фреймворков. Можно считать, что это просто аналог Panel из Windows Forms. Так что автор пытается рендерить WPF контент в битовую карту формата Pbgra32.