Копирование изображения FrameworkElement
Поиск простого решения
Иногда появляется необходимость сохранить изображение произвольного FrameworkElement-а в виде изображения. Например, Вы отобразили пользователю красивый график с множеством разнообразных трехмерных линий и хотите автоматически отправить его в виде изображения по электронной почте, либо вставить в MS Word/Excel.
Но данная задача решается не так просто, как бы это могло показаться. Когда я решал подобную задачу я столкнулся с целым рядом проблем:
- Что делать если элемент не представлен в визуальном дереве?
- Каким образом представлять фон элемента, если тот является прозрачным?
- Как правильно привести размеры элемента в соответствие с требуемыми, не исказив его содержимое?
- Почему при удалении элемента из визуального дерева метод RenderTargetBitmap.Render?
- И многое другое...
Поэтому, решив все эти проблемы, делюсь найденным решением в виде готового класса.
[more]
Класс FrameworkElementRender
[code:c#] // Класс, позволяющий представить любой FrameworkElement в виде изображения
public static class FrameworkElementRender
{
// Возвращает изображение FrameworkElement
public static BitmapSource Render(FrameworkElement renderTarget, Size availableSize, Brush backgroundBrush)
{
if (renderTarget == null)
return null;
//Перерисовываем FrameworkElement
renderTarget.Measure(availableSize);
renderTarget.Arrange(new Rect(renderTarget.DesiredSize));
//Рисуем фон и изображение элемента
DrawingVisual dw = new DrawingVisual();
using (DrawingContext dc = dw.RenderOpen())
{
dc.DrawRectangle(backgroundBrush, null, new Rect(renderTarget.DesiredSize));
dc.DrawRectangle(new VisualBrush(renderTarget), null, new Rect(renderTarget.DesiredSize));
}
//Создаем изображение
RenderTargetBitmap rtb = new RenderTargetBitmap(
(Int32)renderTarget.DesiredSize.Width,
(Int32)renderTarget.DesiredSize.Height,
96.0,
96.0,
PixelFormats.Default);
rtb.Render(dw);
return rtb;
}
// Возвращает изображение FrameworkElement
// Задает по умалчанию кисть заднего фона - белой
public static BitmapSource Render(FrameworkElement renderTarget, Size availableSize)
{
return Render(renderTarget, availableSize, Brushes.White);
}
// Возвращает изображение FrameworkElement
// Задает по умалчанию кисть заднего фона - белой
// Размер - тот, который именно FrameworkElement в данный момент
public static BitmapSource Render(FrameworkElement renderTarget)
{
if (Double.IsNaN(renderTarget.ActualWidth) || (Double.IsNaN(renderTarget.ActualHeight)))
throw new ArgumentException("Требуемый FrameworkElement не имеет размеров, либо не представлен в визуальном дереве!");
return Render(renderTarget, new Size(renderTarget.ActualWidth, renderTarget.ActualHeight));
}
}[/code]
BitmapSource и буфер обмена
Дополнительным плюсом данного решения является то, что теперь можно легко помещать полученное изображение в буфер обмена:
[code:c#]Clipboard.SetImage(FrameworkElementRender.Render(myUserControl));[/code]
Благодарности
Огромное спасибо пользователю vit_as форума rsdn.ru за помощь в нахождении решения.