I seem to be unable to figure out how to Draw graphics in a Windows phone app in C#.
I want to Draw e.g. a line. In old school Windows forms i add an event handler to the Windows paint event. And then use a GDI+ Graphics object. But there is no paint event in any controls?
So how do i draw a line on a canvas in a Windows phone app?
I think I need to clarify.
I want to create dynamic graphics and I want to use C#.
I want an update frequency arround 30 fps and I only need a few graphics elements approximately 100.
if you need a line, use the Line class:
<Page xmlns="whatever">
<Grid>
<Line X1="0" Y1="0" X2="10" Y2="10" Stroke="Blue" StrokeThickness="2"/>
</Grid>
</Page>
Other than that, refer to MSDN.
Forget whatever procedural paradigms you might have learned in archaic technologies. Modern technologies are declarative.
You start by adding Canvas to the form and then .Add() graphical objects to the canvas children - it makes the object scaled for you by the engine, which is kind of neat. Usually looks like this:
line = new **Line**();
line.Stroke = Brushes.Yellow;
line.X1 = 0;
line.Y1 = 0;
line.X2 = 100;
line.Y2 = 100;
line.StrokeThickness = 2;
yourCanvas.Children.**Add(line)**;
Just drop the **s from the code - they are for attention grabbing.
Actually, as I drew dynamic hypercubes, I have never used the XAML version, but if you need a static structure or even substructure XAML is the way to go. As I understand, Children.Add() dynamically creates node in the parsed XAML tree, that .NET keeps in memory. If you can not take slight performance hit for dynamicly positioned graphics that WPF imposes, you will have to stick with DirectX or OpenGL for better performance.
If you want low level access to draw 2D or 3D like when using DirectX you can take a look to SharpDX
There are some samples for Windows Phone in Github like:
MiniCube: Display a rotating cube in a DrawingSurfaceBackgroundGrid
Related
EDIT:
They removed the CompositionImage in the last build ...
I would like to know the difference between Win2D CanvasBitmap and Microsoft.UI.Composition CompositionImage.
In both case I was able to display images but I don't really know/understand the difference between the two approach.
The CanvasBitmap approach:
XAML:
<xaml:CanvasControl Draw="OnDraw" />
Code:
private void Onraw(CanvasControl sender, CanvasAnimatedDrawEventArgs e)
{
var image = await CanvasBitmap.LoadAsync(...);
e.DrawingSession.DrawImage(...);
}
The CompositionImage approach:
XAML:
<Grid x:Name="Host" />
Code:
ContainerVisual rootVisual =
(ContainerVisual)ElementCompositionPreview.GetContainerVisual(this.Host);
Compositor compositor = rootVisual.Compositor;
CompositionGraphicsDevice device = compositor.DefaultGraphicsDevice,
CompositionImage image = device.CreateImageFromUri(...);
ImageVisual content = Compositor.CreateImageVisual();
content.Image = image;
rootVisual.Children.InsertAtTop(content);
What's the difference? What is the best approach?
To put thing in the context, I have an application that displays a lot of small images. I need the app to be low on memory and to draw fast the images.
Thanks,
Adrien.
Win2D is basically a simple 2D wrapper over directx - if you create a canvas you have a directx context where you can draw in.
The composition APIs let you mingle with the layer between XAML and DirectX - this is a bit higher level than Win2D, and also the "canvas" is the app itself :)
So the Win2D CanvasBitmap is like Image in XAML, while the CompositionImage is more like ImageSource, because you can only use it in a visual or as an effect source.
Technically you can use whichever you want or is easier for you. Keep in mind that the Composition APIs are only available in Windows 10 after TH2 (1511) version, so if you want to support RTM (build 10240) use a Win2D solution.
It depends...
A CanvasControl and a ImageVisual are extremely similar in terms of rendering cost. In terms of CPU cost the ImageVisual is more lightweight.
However, you can display large numbers of CanvasBitmaps inside a single CanvasControl (or CanvasAnimatedControl) - so if you're looking to display something like a 2D game made up of many hundreds of bitmaps then Win2D is probably the way to go.
If you want to take advantage of Composition's animation system and mix images with XAML components then Composition is probably the way to go.
Currently I have an image on a canvas that can I freely move around in my application on which I have 6 layers of DrawingVisuals rendered, but it seems to be pretty slow. I'm using a RenderTargetBitmap to render the visuals on. Is there a faster way to display the visuals on the image or on any other framework element I can freely move on the canvas?
xaml:
<Canvas>
<Image Height="505" HorizontalAlignment="Left" Margin="0,0,0,0" Name="_MapImage" Stretch="Fill" VerticalAlignment="Top" Width="700" MouseLeftButtonDown="_MapImage_MouseDown" MouseWheel="_MapImage_MouseWheel" MouseLeftButtonUp="_MapImage_MouseUp" MouseMove="_MapImage_MouseMove" />
</Canvas>
code:
_renderRoutesBitmap = new RenderTargetBitmap((int)(_MapImage.Width), (int)(_MapImage.Height), 96, 96, PixelFormats.Default);
for (int i = 6; i < 8; ++i)
{
if((layerCode / (int)Math.Pow(10,i) % 2) == 1)
_renderRoutesBitmap.Render(_layers[i]); //takes too much time
}
_RouteImage.Source = _renderRoutesBitmap;
I had to do a similar thing some time ago where I had to write my own GIS application. I had to draw thousands and thousands of drawing visuals, my findings were that RenderTargetBitmap is not a good choice for bitmap operations as it suffers from not using graphical hardware acceleration.
Silverlight has a more suitable class; WriteableBitmap which allows your application to directly write into the GPU buffer. The only issue with this is that it is only available for Silverlight. If you want to stick to using a bitmap for your operations then you can use the equivalent of WriteableBitmap for WPF which is WriteableBitmapEx available here.
As you have only 6 drawing visuals in total, I suggest you move to using a higher level UI element such as Shapes and such.
Display Visual objects using Render is not a good solution. If you have a Canvas and you want to render your visual you don't need to convert visuals into a bitmap, also if you if you make the conversion your image cannot be scaled by any amount without degrading quality (feature of the vector graphics). There is another possibility: create your own Canvas. I currently use this method, and have no problem to draw thousands of shapes. This is a very simple example:
public class DrawingCanvas : Panel
{
public List<Visual> visuals = new List<Visual>();
public void AddVisual(Visual visual)
{
if (visual == null)
return;
this.visuals.Add(visual);
base.AddVisualChild(visual);
base.AddLogicalChild(visual);
}
public void RemoveVisual(Visual visual)
{
if (visual == null)
return;
this.visuals.Remove(visual);
base.RemoveVisualChild(visual);
base.RemoveLogicalChild(visual);
}
}
Use DrawingCanvas instead of Canvas class.
Does WPF have an equivalent to this?
ImageAttributes ia = new ImageAttributes();
ia.SetColorMatrix(new ColorMatrix { Matrix33 = 0.5f }, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
In other words, can I adjust the transparency of a WPF ImageSource (or any other drawing related class e.g. BitmapImage), or is GDI the best choice here?
I don't intend to draw the image onto a window, so I think that rules out using the Image class(?)
(My line of thought with trying to use WPF instead of GDI is primarily because I'm under the impression that with WPF I can have hardware acceleration, but from what I've seen so far, it seems that it's only applying to a very limited subset of image manipulation)
check out WriteableBitmapEx, which provides a set of extension methods for manipulating bitmaps in memory with WPF and Silverlight.
There is nothing in WPF per-se that will allow you to do want, but the WPF/Silverlight class WriteableBitmap provides a fast, low-level API to drawing/manipulating and is native to WPF.
WriteableBitmapEx then builds on this by providing extension methods to do GDI-like operations. There is a Convolute function which allows convolving an image with a matrix, so you could do the above. Note WB-Ex is a silverlight library but they also provide an unmaintained WPF version which although incomplete, can be extended to keep up to date with the Silverlight version.
Other than that I'd suggest rolling your own. If all you wish to do is modify the opacity to 0.5f then rather than convolve I'd suggest writing a specific function to do that based on the above examples
An image has an opacity field, which is a value between 1.0 (completely opaque) to 0.0 (invisible)
For example:
<Image
Height="107"
Margin="367,0,473,83"
Source="Images/4.png"
Stretch="Fill"
VerticalAlignment="Bottom" Opacity="0.5"/>
Following your comments I created a sample project which can modify the alpha channel quickly using WriteableBitmapEx as a basis and uploaded to my Company Blog. You'll have to forgive the formatting, its a new website and still has some glitches to be ironed out! The download link is on the page titled BitmapAlphaChannel
You're correct in saying WPF's hardware acceleration is only applied to a limited subset of features. For instance, all layout is done on the CPU, tessellation on the CPU, only the final rendering is done on the GPU. The result is WPF in my experience is slower than GDI+, certainly GDI. However it can be pushed in the right direction to do what you want, in the speed you want it to!
I am trying to write a small interactive game-like application, where I need to have a Draw method that's gonna draw on screen, but can't figure out how to structure the method for WPF.
If this was Winforms, I could use:
public void Draw (Graphics g)
{
}
But for a WPF Window, what should I have on it in the xaml (currently only have a Grid), and what should this Draw method receive as an argument?
First I want to do it like this to get it working, then I can think about how to make it more WPF, etc. But now I am more interested in getting this to work.
Typically, you "draw" in WPF in a completely different manner.
In Windows Forms/GDI, the graphics API is an immediate mode graphics API. Each time the window is refreshed/invalidated, you explicitly draw the contents using Graphics.
In WPF, however, things work differently. You rarely ever directly draw - instead, it's a retained mode graphics API. You tell WPF where you want the objects, and it takes care of the drawing for you.
The best way to think of it is, in Windows Forms, you'd say "Draw a line from X1 to Y1. Then draw a line from X2 to Y2. Then ...". And you repeat this every time you need to "redraw" since the screen is invalidated.
In WPF, instead, you say "I want a line from X1 to Y1. I want a line from X2 to Y2." WPF then decides when and how to draw it for you.
This is done by placing the shapes on a Canvas, and then letting WPF do all of the hard work.
When there are just too many objects to be drawn very quickly (huge Visual Tree) another option would be to use a WriteableBitmap. Just use the Pixels property to set the pixels and/or use the Render method to draw UIElements.
I preffer to use OnRender method like in this example:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawRectangle(null, new Pen(Brushes.Black, 2), new Rect(0, 0, ActualWidth, Height));
}
To Implement a Draw loop type behavior in WPF you can use the CompositionTarget.Rendering event. This is raised once per frame when the WPF drawing system is painting frames.
As others have pointed out this is not very WPF friendly but it will work and can be used to get more immediate drawing behavior out of a WPF app.
In most cases you would use a single root canvas and update say the Canvas position of an element on the CompositionTarget.Rendering event.
For example to make a ellipse fly all over the screen do this:
In your XAML (For a Window that is 640 by 480 in size):
<Canvas x:Name="theCanvas">
<Ellipse x:Name="theEllipse" Height="10" Width="10" Fill="Black" />
</Canvas>
In your Code behind for the Window that the above XAML is in (Make sure to add a reference to System.Windows.Media in order to see the CompsitionTarget object :
public static Random rand = new Random();
public View()
{
InitializeComponent();
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, System.EventArgs e)
{
double newLeft = rand.Next(0, 640);
double newTop = rand.Next(0, 480);
theEllipse.SetValue(Canvas.LeftProperty,newLeft);
theEllipse.SetValue(Canvas.TopProperty, newTop);
}
Yow should add a Canvas (or change the Grid for a Canvas) and then draw over it. Here is Microsoft tut on drawing over a canvas
Also, I don't know how related is this other question to yours, but you might want to check it out.
I'm using XNA, and I've run into a little problem. I need to support multiple layers, each with a distinct z order (I call these "viewports"). A picture is worth a thousand words, so here's what it should look like:
http://www.charlesstrahan.com/so_files/viewports.png
There are several things to notice here. Sprites do not render outside of their viewport, as you can see with Sprite B. Also, notice how the viewports are rendered - it's very similar to "layers" in Photoshop. Although Sprite C is has a z order of -1000, C still renders above Sprite A because its viewport's z-order is a greater than A's viewport's z-order.
There's one last detail that I couldn't display very well in the above picture. Each viewport needs to optionally render a color over its region of the screen - you could think of it as a "tinting" affect.
I'm completely at a loss when it comes to doing this the best way in XNA, so I could really use a short snippet of C#/VB.NET code that demonstrates this in action. Any help would be greatly appreciated.
You can easily do this with RenderTargets :-) There are lots of resources on how to use them on the web (example).
If you're just starting yoru project though, consider installing the XNA 4.0 (in CTP right now via the windows phone SDK). It's gotten a lot easier in the new version ... from an article that Shawn Hargreaves put out recently, RenderTarget changes in XNA Game Studio 4.0.
List<Texture2D> textures = new List<Texture2D>();
for (int i = 0; i < 100; i++)
{
RenderTarget2D rt = new RenderTarget2D(...);
GraphicsDevice.SetRenderTarget(rt);
DrawCharacterAnimationFrame(i);
GraphicsDevice.SetRenderTarget(null);
textures.Add(rt);
}
And the "tinting" feature you were asking about is braindead easy with this because you can just call the Clear method when you go to render each render target with whatever color you want.
You can just sort your objects into groups then render each group in order with clipping
device.ClipPlanes[0].Plane = plane;
device.ClipPlanes[0].IsEnabled = true;