HowTo determine the Anchor Point of the System.Windows.Media.DrawingContext? - wpf

How to determine the Anchor Point of the System.Windows.Media.DrawingContext? exactly like RenderTransformOrigin in Image WPF Control.
Dim InImage As New BitmapImage(New Uri("Image Path"))
Dim DrawingGroup As New DrawingGroup
Dim DrawingContext As DrawingContext = DrawingGroup.Open
DrawingContext.PushTransform(New RotateTransform(53))
DrawingContext.DrawImage(InImage, New Rect(0, 0, 500, 500))
DrawingContext.Close()
I want to Render Image using Several Anchor Points.

I think you mean Anchor Point as the point around which the rotating is performed. So you can set that point right via the RotateTransform. It has a pair of properties namely CenterX and CenterY:
...
Dim Rotating As New RotateTransform(53)
Rotating.CenterX = Some_Value_For_X
Rotating.CenterY = Some_Value_For_Y
DrawingContext.PushTransform(Rotating)
...

Related

How can I draw a Polyline onto an Image (WPF)

I've tried a few different approaches to this, but can't seem to get a combination that works.
Creating WPF app in C#, Visual Studio.
System.Windows.Shapes.Polyline works really nicely to draw into a Canvas in real-time, but I want to be able to draw in higher resolution onto a non-visual component that I can then render onto an Image.
If I create a Polyline on a Canvas that's visible in the UI, this works fine:
// Make rendertarget size of full page
RenderTargetBitmap rtb = new RenderTargetBitmap((int)wPage, (int)hPage, 96, 96, PixelFormats.Default);
// Render the polyline
rtb.Render(lineVirt);
// Apply to background image
imgBG.Source = rtb;
But if I create a Polyline on a Canvas that's not visible in the UI, then nothing renders to the image. This is probably fair enough. My guess is that the component recognises that it's not visible and therefore doesn't bother to render.
I've considered putting the Canvas somewhere in the UI buried under other controls, but that seems like a horrible kind of hack.
Essentially, all I need is a clean and fast way to draw a multi-point line of a specified width and color onto an Image. I thought that Polyline would work well, but only seems to work in a visible container.
What are my options?
You do not need a rendered Canvas or any other visible Panel at all.
Just use basic drawing primitives available at the Visual layer.
The DrawGeometry method below draws a Geometry onto a BitmapSource, using the bitmap's rendered size, i.e. the size that takes its DPI into account, and returns the resulting BitmapSource.
public static BitmapSource DrawGeometry(
BitmapSource source, Pen pen, Geometry geometry)
{
var visual = new DrawingVisual();
var rect = new Rect(0, 0, source.Width, source.Height);
using (var dc = visual.RenderOpen())
{
dc.DrawImage(source, rect);
dc.DrawGeometry(null, pen, geometry);
}
var target = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
target.Render(visual);
return target;
}
In order to draw in the bitmap's pixel units and hence ignore its DPI, modify the method like this:
var rect = new Rect(0, 0, source.PixelWidth, source.PixelHeight);
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(new ImageBrush(source), null, rect);
dc.DrawGeometry(null, pen, geometry);
}
The following method uses the above to draw a polyline as an IEnumerable<Point>.
public static BitmapSource DrawPolyline(
BitmapSource source, Pen pen, IEnumerable<Point> points)
{
var geometry = new PathGeometry();
if (points.Count() >= 2)
{
var figure = new PathFigure { StartPoint = points.First() };
figure.Segments.Add(new PolyLineSegment(points.Skip(1), true));
geometry.Figures.Add(figure);
}
return DrawGeometry(source, pen, geometry);
}
It would be used like
var source = new BitmapImage(new Uri(...));
var pen = new Pen
{
Brush = Brushes.Blue,
Thickness = 2,
};
var points = new List<Point>
{
new Point(100, 100),
new Point(1000, 100),
new Point(1000, 1000),
new Point(100, 1000),
new Point(100, 100),
};
image.Source = DrawPolyline(source, pen, points);
Your canvas needs a size, so someone or something has to Arrange it. That might already be enough to get it to render, but the only reliable way of rendering arbitrary visuals to a bitmap is to actually place them in the visual tree of a window that's displayed and thus laid out by WPF. You can then render to the bitmap in a deferred task at ContextIdle priority to ensure that layout is complete.

HowTo Get Rendered Image from WPF Image Control

Each WPF Image Control has RenderTransform Property that sets Scale, Skew, Rotate and many more Transformation to Images. After Calling RenderTransform Property, How to get Rendered Image into the BitmapImage or RenderTargetBitmap Class or Any other class?
This is my Code:
Dim InImage As New BitmapImage(New Uri("My Image Path"))
Dim TG As New TransformGroup
TG.Children.Add(New RotateTransform(190))
Dim MyImg As New Image
MyImg.Source = InImage
MyImg.RenderTransform = TG
'Here i need get Transformed Image into a BitmapImage or RenderTargetBitmap Variable or Any other class variable.
It's a pity that TransformedBitmap does not support rotation with angle other than any multiple of 90 degs (that means we can only rotate 90, 180, 270, ...). Thinking about which objects we can put in a bitmap and apply some Transform? Well we have DrawingGroup, DrawingVisual, ImageBrush, UIElement and via DrawingContext.
Using DrawingGroup, we have to put in an ImageDrawing and apply the transform via the Transform property of the DrawingGroup. Then we have to use a DrawingImage, set the Drawing property to the DrawingGroup.
Using DrawingVisual, we have to open a DrawingContext, use the DrawImage method after pushing some transform. Then we may have to use RenderTargetBitmap by passing the DrawingVisual in the Render method.
Using UIElement (like your idea about using Image control as the medium), we have to render the image on the UIElement. So an Image control suits this best. Every UIElement has a Transform property allowing us to add whatever transform. At last we also have to use a RenderTargetBitmap by passing the UIElement in the Render method.
Using ImageBrush, we have to set the ImageSource property to a BitmapImage. Then we can use the Transform or RelativeTransform property to apply some transform. After that we have to use a DrawingImage. Create a simple GeometryDrawing using the ImageBrush as its Brush, and a RectangleGeometry as its Geometry. Finally we just need to set this GeometryDrawing to the Drawing property of the DrawingImage. The output is the DrawingImage. I would like to use this approach to write the code here:
Dim InImage As New BitmapImage(New Uri("My Image Path"))
Dim ImgBrush As New ImageBrush(InImage)
ImgBrush.Viewport = New Rect(0.1,0.1,0.8,0.8)
ImgBrush.ViewportUnits = BrushMappingMode.RelativeToBoundingBox
Dim Rotating As New RotateTransform(190)
Rotating.CenterX = 0.5
Rotating.CenterY = 0.5
ImgBrush.RelativeTransform = Rotating
Dim ImgSize As New Rect(0,0,300,400)
Dim DrawImage As New DrawingImage()
DrawImage.Drawing = New GeometryDrawing(ImgBrush, null, ImgSize)
Note about the "My Image Path". I've found out that using a Relative image would not work if you don't have any Image folder at the same level with the built exe file. If you don't want to deploy some image folder together with your application, you can add your image as a Resource. To refer to an image added as a Resource in code behind, you have to use a special kind of path:
pack://application:,,,/Your_Relative_Image_Path
Note that to be sure your image is added as a Resource, try right clicking on the image (under the Projects treeview), select Properties in the popup menu, then look into the Build Action field, it should be Resource.
Also note about the ImgBrush.Viewport, setting it appropriately will prevent the image from being cut off (that's because the transformed image is rotated). It depends on the ImgSize and how much degree you rotate.

VisualBrush size and stretch problems

I would like to export a grid (whit all his children) to a PNG.
The problem is that some of these children are outside of the grid.
Here is my code:
VisualBrush sourceBrush = new VisualBrush(MyGrid);
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(Math.Floor(exportWidth), Math.Floor(exportHeight))));
drawingContext.Close();
}
RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)Math.Floor(exportWidth), (int)Math.Floor(exportHeight), 96, 96, PixelFormats.Default);
renderTarget.Render(drawingVisual);
The resulting image is blurred if at least one of the children is outside the grid.
The exportHeight and exportWidth values are calculated upstream, relative to the position of the grid's children.
If all children are inside the grid, the picture is clear.
I think this is because of the VisualBrush original size that cannot be changed.
Do you know a way to fix it ?
EDIT :
I do not call renderTarget.Render(MyGrid); because it does not take in charge children who are outside the grid (Children whose top or left value is negative).
Have you tried?
MyGrid.ClipToBounds = true;

WPF Margin Thickness

Background:
I'm loading images into a Stack Panel (called MainStack) displayed horizontally (for argument sake, 10 images) with only room for 4 images in view. As I load the images from the List I'm setting the width of each to 300 so they're all in the same size box.
I want to move the images from right to left using the Stack Panel's Margin (left) property. I want the appearance of scrolling left by exactly the same amount of the width of each image (looped with 4 second delay) until the last image is in view. Here's my code for the Margin animation:
Dim result As New Storyboard
Dim animation As New ThicknessAnimation
animation.From = MainStack.Margin
animation.EasingFunction = New PowerEase() With {.EasingMode = EasingMode.EaseInOut, .Power = 3}
animation.To = New Thickness(-300, 0, 0, 0)
animation.Duration = New Duration(TimeSpan.FromSeconds(1.5))
Storyboard.SetTarget(animation, MainStack)
Storyboard.SetTargetProperty(animation, New PropertyPath("Margin"))
result.Children.Add(animation)
result.Begin()
Strange thing is happening. The Stack Panel is moving to the left but only by about half the width of the image.
What is going on?!?
/* edit */
As per H.B. suggestion, I've tried to implement a TranslateTransform but not having much success.
Can anyone see any problems with this code?
Dim translatePosition = New Point(300, 0)
RenderTransform = New TranslateTransform()
Dim d As New Duration(New TimeSpan(0, 0, 0, 1, 30))
Dim x As New DoubleAnimation(translatePosition.X, d)
Storyboard.SetTarget(x, MainStack)
Storyboard.SetTargetProperty(x, New PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"))
Dim sb As New Storyboard()
sb.Children.Add(x)
sb.Begin()
Nothing seems to be happening.
Ben
I think you should try putting your entire stackpanel in a canvas and just animating the Canvas.Left property to scroll the images.
Another option for you is to use a horizontal ListBox, then you can animate the ScrollViewer. If you want to try it this way, here's a link that may help: WPF - Animate ListBox.ScrollViewer.HorizontalOffset?.

Using RenderTargetBitmap with a Viewport3D

I use Viewport3d and display my 3D Elements with inherited UIElement3D classes... Now I want to make different images of my viewport (different views). That means I have to make a new viewport3d at runtime and apply my specific view for the image on it..
see the following code:
' Start creating Image
Dim bitmap As New RenderTargetBitmap(800,
800,
96,
96,
PixelFormats.Default)
'--------------------
'Render xamlViewport works perfect
bitmap.Render(xamlViewport)
Using stream As FileStream = New FileStream("C:\\temp\\test_xamlviewport.png", FileMode.Create)
Dim encoder As New PngBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap))
encoder.Save(stream)
End Using
'---------------------
'Render local Viewport don't work
Dim oViewport As New Viewport3D
oViewport.Height = 800
oViewport.Width = 800
oViewport.Children.Add(New C3DCuboid())
oViewport.Camera = New PerspectiveCamera(New Point3D(4, 7, 6), New Vector3D(-4, -7, -6), New Vector3D(0, 1, 0), 45)
Dim oVisual As New ModelVisual3D
oVisual.Content = New DirectionalLight(Colors.White, New Vector3D(-1, -2, -3))
oViewport.Children.Add(oVisual)
oViewport.Measure(New Size(800, 800))
oViewport.Arrange(New Rect(New Size(800, 800)))
bitmap = New RenderTargetBitmap(800,
800,
96,
96,
PixelFormats.Default)
bitmap.Render(oViewport)
Using stream As FileStream = New FileStream("C:\\temp\\test_localviewport.png", FileMode.Create)
Dim encoder As New PngBitmapEncoder
encoder.Frames.Add(BitmapFrame.Create(bitmap))
encoder.Save(stream)
End Using
The first picture (test_xamlviewport.png) is shown correctly, but the second picture where I create my own Viewport3D Object and print it is empty (test_localviewport.png).
How can I force to Render my UIElement3D Objects or why it doesn't work if i make a new Viewport vs print the existing XAML viewport instance?
Here you find a sample solution which reproduce my problem
http://cid-df67ca3f85229bd1.office.live.com/self.aspx/Development/WpfApplication2.zip
Regards
Roland

Resources