i have no previous experience in plotting in winforms, in one form i want to plot ecg. or lets say a sin wave or any wave function in a specific area, but what i am doing is e.c.g.. rest of the form will be normal form with buttons and labels,
can anybody be nice enough to through in a tutorial
:)
You have few choices, you can write your own control, that will process data and render it. For more complicated plots, that can be a bit complicated, but the basics are always the same, setting X and Y values ranges and then just draw a line using GDI going from left to right, nothing fancy.
As this can get a bit complicated for more advanced features, you could use some charting controls, I'd read this post or check codeproject.com, I remember, that I saw few attempts to write some decent charting controls, which are open source, new articles will probably be coded in WPF, but you should find something older as well.
Edit:
Some links that you can find useful: Graph plotting lib that's main goal is to simulate ECG or another graph plotting lib
You need to create a custom control.
public class MyECGDrawer : Control{}
In it, you override the OnPaint event
protect override OnPaint(PaintEventArgs pe ){}
Then in the paint function, you draw your graphics the way you want it, let's say sin(x)
// refresh background
pe.Graphics.FillRectangle( Brushes.White, 0, 0, Width, Height );
int prevX = -1, prevY = -1;
for(int x = 0; x < Width; x++ )
{
if( prevX >= 0 )
{
pe.Graphics.DrawLine( Pens.Black, prevX, prevY, x, Math.sin(x) );
}
prevX = x;
prevY = Math.sin(x);
}
To force the ECG to redraw, you call the .Invalidate() function on the control. You should be able to drag and drop the control in your form from the designer.
In the end, the class would look like
public class MyECGDrawer : Control{}
In it, you override the OnPaint event
public class MyECGDrawer : Control
{
protect override OnPaint(PaintEventArgs pe )
{
// refresh background
pe.Graphics.FillRectangle( Brushes.White, 0, 0, Width, Height );
int prevX = -1, prevY = -1;
for(int x = 0; x < Width; x++ )
{
if( prevX >= 0 )
pe.Graphics.DrawLine( Pens.Black, prevX, prevY, x, Math.sin(x) );
prevX = x;
prevY = Math.sin(x);
}
}
}
I wrote up the following and tested it. It seems to do what you want, but note that it is simply plotting sin(x) in a loop with no delay - i.e. the plot for sin(x) streams off the left edge so fast you can hardly see it. You can, however, put a break on any line inside the loop and then step through the loop with F5 to see it work slowly - presumably your streaming ECG data will only arrive at some fixed speed so this should not be a problem in your implementation.
In the following, monitor is a PictureBox on a winforms form. Everything else is local.
private void drawStream(){
const int scaleX = 40;
const int scaleY = 40;
Point monitorTopLeft = new Point(0, 0);
Point MonitorTopLeftMinus1 = new Point(-1, 0);
int halfX = monitor.Width / 2;
int halfY = monitor.Height / 2;
Size size = new Size(halfX + 20, monitor.Height);
Graphics g = monitor.CreateGraphics();
g.TranslateTransform(halfX, halfY);
g.ScaleTransform(scaleX, scaleY);
g.Clear(Color.Black);
g.ResetClip();
float lastY = (float)Math.Sin(0);
float y = lastY;
Pen p = new Pen(Color.White, 0.01F);
float stepX = 1F / scaleX;
for (float x = 0; x < 10; x += stepX) {
g.CopyFromScreen(monitor.PointToScreen(monitorTopLeft), MonitorTopLeftMinus1, size, CopyPixelOperation.SourceCopy);
y = (float)Math.Sin(x);
g.DrawLine(p, -stepX, lastY, 0, y);
lastY = y;
}
}
Some additional info that may be helpful:
The origin in a picture box starts
out at the top left corner.
TranslateTransform allows you to
translate (i.e. move) the origin.
In the example, I translate it by
half the picture box's width and
half its height.
ScaleTransform changes the magnification of the picturebox - note that it even magnifies the width of the pen used to draw on the picturebox - this is why the pen's width is set to 0.01.
CopyFromScreen performs a bitblt. Its source point is relative to the screen, the destination is relative to the picturebox and the size of the rectangle to move disregards any transforms (like the scale and translation transforms we added).
Notice that the X coordinates in the DrawLine method are -stepx and 0. All drawing basically occurs right on the y axis (i.e. x = 0) and then CopyFromScreen moves the drawn portion to the left so that it "streams" off to the left.
Unless you are doing this as a learning experience, you may want to consider looking at the free Microsoft Chart Controls for .NET available here.
http://www.microsoft.com/downloads/details.aspx?FamilyID=130f7986-bf49-4fe5-9ca8-910ae6ea442c&displaylang=en#QuickInfoContainer
That being said, I would offer the following guidelines if you want to roll your own.
Create a user control to encapsulate the plot rendering rather than render directly on the form.
In your control, expose properties to get/set the data you wish to render and add any other properties you want to control the rendering (scaling, panning, colors, etc.)
In you control, either override the OnPaint method or create an event handler for the Paint event. These methods will have a PaintEventArgs object passed to them, which contains a Graphics object as a property. The methods of the Graphics object are used to render points, lines, etc onto the control when it needs to be painted. Most of the drawing operations require either a pen (outlines / lines) or a brush (filled areas). You can use stock objects for these operations (e.g. Pens.Black or Brushes.Red) or you can create your own (see documentation). If you create you own objects, make sure you dispose of them after using them (e.g. using the "using" statement or by calling Dispose).
There are a couple good books on GDI+. I suggest picking one up if you are going in deep.
Related
I'm doing an application with a splash screen.
I've an image an I'd like to put below a progress bar like :
Example
I've succeeded to make the bitmap transparent.
But, now, the image is behind the progress bar
Now
Is there a way to get the image in front of the progress bar ?
Thank you.
F.
Code :
public partial class Form1 : Form
{
Bitmap m_l;
public Form1()
{
InitializeComponent();
m_l = Properties.Resources.LU;
m_l.MakeTransparent(Color.Transparent);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(m_l, new Rectangle(new Point(0, -40), new Size(200, 264))); progressBar1.Refresh();
}
}
(sorry, for missuing the answer function but the answer is to long for a comment)
#TaW
seems like you didnt quite understand the approach, so I will try to explain it in more detail
OP asked if he can make a transparant Image over another control (a progressbar)
I assumed this transparent Image is inside a PictureBox, you seem to assume some other control
to position the control, if my assumption is correct the picturebox, infront of the progress bar all he has to do is right click and click "Bring to Front" on the PictureBox
and there you have it a "transparent" PictureBox infront of a progressbar - but as you mentioned in your answer we cannot stop there since the "transparent" isnt what I expected, but obviously you knew - its this "parent background color picking" that WinForms does and we end up with a not fully transparent image infront of the ProgressBar but instead one with a gray Background
Now the posted url comes in place:
http://www.richardhyland.com/diary/2009/05/26/how-to-truely-make-a-picturebox-background-transparent/
This is the code provided, and explained in that url:
public static System.Drawing.Drawing2D.GraphicsPath Transparent(Image im)
{
int x;
int y;
Bitmap bmp = new Bitmap(im);
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
Color mask = bmp.GetPixel(0, 0);
for (x = 0; x <= bmp.Width - 1; x++)
{
for (y = 0; y <= bmp.Height - 1; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
bmp.Dispose();
return gp;
}
With this we can achieve a fully transparent Picture box infront of a Progress bar.
So without this Code, we have this:
But with that Code:
Notice, this approach has some downsides:
doesn't work perfectly - as you can see gray pixels around the edges of the image
performs poorly on big Images - since getting each pixel with GetPixel is "challange"
(Please, ignore the fact that the image shows "JPG" and I am talking about transparent Images - this was just the first image Google search presented me and yes, the file is a transparent png)
You can accomplish this using a PictureBox with a Region.
Add a PictureBox to your form. This will hold the image. Position it to overlap the ProgressBar as you would like. Set the Image property to your overlay image.
In the form constructor we're then going to set the Region of this PictureBox. The region defines the shape of the control. We're going to set the Region equal to the non-transparent parts of the image.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
pictureBox1.Region = CreateRegion(Properties.Resources.LU);
}
private static Region CreateRegion(Bitmap maskImage)
{
// We're using pixel 0,0 as the "transparent" color.
Color mask = maskImage.GetPixel(0, 0);
GraphicsPath graphicsPath = new GraphicsPath();
for (int x = 0; x < maskImage.Width; x++)
{
for (int y = 0; y < maskImage.Height; y++)
{
if (!maskImage.GetPixel(x, y).Equals(mask))
{
graphicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
return new Region(graphicsPath);
}
}
Much of this code came from here
There are 5 6 options:
You could set the ProgressBar's BackColor to Transparent. Result:
Translation:
Invalid property value. This control does not support a transparent backcolor.
This will hold true for a subclass as well.
You could nest a transparent Panel with the image as its BackgroundImage. Result:
As you can see, despite the panel and most of the image being transparent, the progressbar still underlays it with a rectangle in its own BackColor; which, see above, can't be transparent.
You could overlay with the panel.
Result:
Looks similar. But this time the backcolor is the original background of wherever the panel was before overlaying it. This is the way winforms fakes transparency; this faked transpareny will only work with nested controls, but not with all..
You could draw your image in the progressbar's Paint event. Problem: It doesn't have one. And if you subclass it it will not work for you.
To sum it up: all those attempts fail; the conclusion is simple: ProgressBar is an animated control that won't support any messing with it.
Last option: Write your own. You can subclass a Panel or Label and write your own progressbar. Many folks who wanted to have a custom look, have done this and you can find many ready made examples.
Upate: Looks like you can have a 6th option, which will work if and only if you don't need semi-transparency, like anti-aliasing etc..: You can create a GraphicsPath to create a Region which will mask some control with the image.. So while my example will not work, OP's image may look quite OK.
as ways, people are developers but seams to have a really weak logic, post code and link the rest to a site that can die at any time, here the 2 missing line of the answer
public static System.Drawing.Drawing2D.GraphicsPath Transparent(Image im)
{
int x;
int y;
Bitmap bmp = new Bitmap(im);
System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath();
Color mask = bmp.GetPixel(0, 0);
for (x = 0; x <= bmp.Width - 1; x++)
{
for (y = 0; y <= bmp.Height - 1; y++)
{
if (!bmp.GetPixel(x, y).Equals(mask))
{
gp.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
bmp.Dispose();
return gp;
use:
System.Drawing.Drawing2D.GraphicsPath gp = Resources.Images.Transparent(pictureBox1.Image);
pictureBox1.Region = new System.Drawing.Region(gp);
I'm programming a tetris clone for my C# school project. I am using Microsoft Visual Studio 2012. The game itself is implemented as a two dimensional array of blocks(List of Lists of blocks) and every block has its own texture (bmp image). I am drawing the whole array onto a PictureBox control and this is where the problem starts. When updating the image on the PictureBox (moving/rotating the active shape) the game slightly lags. I tried to draw on a Panel control instead but the result was the same. I have a rough idea what might cause the lag but I don't know exactly how to get rid of it.
This is the draw method of the game "grid":
public void Draw(Graphics g)
{
Brush brush;
Font font = new System.Drawing.Font( "Arial", 5);
for (int i = 0; i < Width; i++)
for (int j = 0; j < Height; j++)
{
brush = new TextureBrush(Blocks[i][j].Texture);
if (Blocks[i][j].Occupied==true)
g.FillRectangle(brush, i * 20, j * 20, i * 20 + Blocks[i][j].Texture.Width, j * 20 + Blocks[i][j].Texture.Height);
}
}
This is the draw method of the active tetromino:
public void Draw(Graphics g)
{
Brush brush = new TextureBrush(Blocks[0].Texture);
foreach (FullBlock b in Blocks)
g.FillRectangle(brush, b.x * 20, b.y * 20,b.Texture.Width, b.Texture.Height);
}
The game itself then use both of them (double buffering attempt):
public void GameDraw(PictureBox p)
{
Graphics g = Graphics.FromImage(gb);
gameGrid.Draw(g);
PlayingShape.Draw(g);
p.Image = gb;
p.Refresh();
}
where "gb" is a private Bitmap variable I create just once in the class constructor (to reduce (unsuccessfully) the lag).
The GameDraw method is called whenever the state of the game is changed (e.g. moving/rotating the active tetromino and every "gravity" tick)
You need Double buffering, which you did not set. Quoting MSDN:
Double buffering uses a memory buffer to address the flicker problems
associated with multiple paint operations. When double buffering is
enabled, all paint operations are first rendered to a memory buffer
instead of the drawing surface on the screen
You can enable it using Control.DoubleBuffered property
No need for picture box, add your own control:
using System.Drawing;
using System.Windows.Forms;
namespace TetrisGame
{
public sealed class TetrisControl : Control
{
private TheBlockType[][] blocks = ...;
protected override void OnPaint(PaintEventArgs e)
{
//draw your stuff here direct to the control, no buffers in the middle
//if that causes flickering, turn on double buffering, but don't bother doing it yourself
//this is your existing draw method:
Draw(e.Graphics);
}
}
}
Then on every tick or movement, do not call paint, just invalidate the control:
tetris.Invalidate();
Also, think out of the box... Rather than doing a full grid scan, you could make each of your shapes part of a linked-list and redraw them based on their position in the grid... Until your grid completely fills, you'd be doing less scanning.
Or, consider only redrawing what you need to redraw, i.e. a block drops near the top, no need to completely redraw the full grid.
Optimisation is what separates us from the animals. Apart from the platypus, who is an optimal creature.
I have a canvas with some images, and i'm also using DrawGeometry, to draw a circle that is filling when the time is passing.
This is how i draw the circle in my DrawingContext:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
MyUtils.RenderProgressClock(drawingContext, clockPosition, 50, gameTime / totalTime);
}
And calling the InvalidateVisual(); to call it.
But Doing this my circle is behind my images and i cant see it, how can i draw it infront of them?
Im totally new to WPF and its giving me a hard Time....
This is the other method code as requested:
private static PathGeometry GetClockGeometry(Point position, double percentage, double radius)
{
const double innerFactor = 0.90;
double innerRadius = radius * innerFactor;
PathGeometry pie = new PathGeometry();
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = new Point(0, -innerRadius);
pathFigure.IsClosed = true;
if (percentage > kMaxClockPercentage)
{
percentage = kMaxClockPercentage;
}
double angle = 360.0 * percentage;
// Starting Point
LineSegment inOutLine = new LineSegment(new Point(0, -radius), true);
// Arc
ArcSegment outerArc = new ArcSegment();
outerArc.IsLargeArc = angle >= 180.0;
outerArc.Point = new Point(Math.Cos((angle - 90) * Math.PI / 180.0) * radius, Math.Sin((angle - 90) * Math.PI / 180.0) * radius);
outerArc.Size = new Size(radius, radius);
outerArc.SweepDirection = SweepDirection.Clockwise;
LineSegment outInLine = new LineSegment(new Point(outerArc.Point.X * innerFactor, outerArc.Point.Y * innerFactor), true);
ArcSegment innerArc = new ArcSegment();
innerArc.IsLargeArc = angle >= 180.0;
innerArc.Point = pathFigure.StartPoint;
innerArc.Size = new Size(innerRadius, innerRadius);
innerArc.SweepDirection = SweepDirection.Counterclockwise;
pathFigure.Segments.Add(inOutLine);
pathFigure.Segments.Add(outerArc);
pathFigure.Segments.Add(outInLine);
pathFigure.Segments.Add(innerArc);
pie.Transform = new TranslateTransform(position.X, position.Y);
pie.Figures.Add(pathFigure);
return pie;
}
OK, now that I understand a little better what is going on, I see that my initial answer won't directly work for you. However, I also see that you have a bit of a problem.
Just the general nature of the way OnRender works means that what you draw is always going to end up behind the images and whatnot that you add to the window.
Add to that the fact that you're putting all this drawing code for a specific feature (the progress clock) into the window itself, and this solution feels a little off.
You might want to explore some alternatives.
A simple one would be to create a UserControl to draw the Clock. That UserControl could have a DependencyProperty for the % that it should be filled. You could use your (roughly) same OnRender code in the UserControl or you could do it some other fancy ways (I'm sure there's some way to do it in all XAML, though I don't know it off the top of my head). Then you just put that clock into the window like all your other images/controls.
You could also do it creating a CustomControl, though that takes a little bit more knowledge about WPF and Resources and whatnot to understand how it works. Since you're new to WPF, that might be a bit much right now.
You need to show us how your circle is added to the Canvas.
WPF is a retained drawing system, so the order the controls appear in it's visual-tree dictates their stacking order.. OnRender() really means AccumulateDrawingObjects() as it doesn't directly draw, it just creates a set of objects to draw.
Also, you don't need to InvalidateVisual() if an object is staying the same size, as it causes a very expensive re-layout.
More efficient ways to re-render are to use a DependencyProperty with AffectsRender... Or to create a DrawingGroup, add it to the DrawingContext during OnRender(), then anytime later you can DrawingGroup.Open() to change the drawing commands in the DrawingGroup.
I've modified the SuperContextMenuStrip found at CodeProject to meet some of my projects needs. I'm using it as a tooltip for map markers on a GMap.NET Map Control. Here is a sample of what it looks like:
What I would like to do is pretty this up a little by making it look more like a bubble. Similar to an old Google Maps stytle tooltip:
I've spent some time searching on control transparency and I know this isn't an easy thing. This SO question in particular illustrates that.
I have considered overriding the OnPaint method of the SuperContextMenuStrip to draw a background of the GMap.NET control that is underneath the SuperContextMenuStrip, but even that would fail in cases where the marker is hanging off the GMap.NET control:
What is the correct way to create the type of transparency I am looking for?
In Windows Forms, you achieve transparency (or draw irregularly shaped windows) by defining a region. To quote MSDN
The window region is a collection of pixels within the window where
the operating system permits drawing.
In your case, you should have a bitmap that you will use as a mask. The bitmap should have at least two distinct colors. One of these colors should represent the part of the control that you want to be transparent.
You would then create a region like this:
// this code assumes that the pixel 0, 0 (the pixel at the top, left corner)
// of the bitmap passed contains the color you wish to make transparent.
private static Region CreateRegion(Bitmap maskImage) {
Color mask = maskImage.GetPixel(0, 0);
GraphicsPath grapicsPath = new GraphicsPath();
for (int x = 0; x < maskImage.Width; x++) {
for (int y = 0; y < maskImage.Height; y++) {
if (!maskImage.GetPixel(x, y).Equals(mask)) {
grapicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
return new Region(grapicsPath);
}
You would then set the control’s Region to the Region returned by the CreateRegion method.
this.Region = CreateRegion(YourMaskBitmap);
to remove the transparency:
this.Region = new Region();
As you can probably tell from the code above, creating regions is expensive resource-wise. I'd advice saving regions in variables should you need to use them multiple times. If you use cached regions this way, you'd soon experience another problem. The assignment would work the first time but you would get an ObjectDisposedException on subsequent calls.
A little investigation with refrector would reveal the following code within the set accessor of the Region Property:
this.Properties.SetObject(PropRegion, value);
if (region != null)
{
region.Dispose();
}
The Region object is disposed after use!
Luckily, the Region is clonable and all you need to do to preserve your Region object is to assign a clone:
private Region _myRegion = null;
private void SomeMethod() {
_myRegion = CreateRegion(YourMaskBitmap);
}
private void SomeOtherMethod() {
this.Region = _myRegion.Clone();
}
What I am looking to do sounds really simple, but no where on the Internet so far have I found a way to do this in DotNet nor found a 3rd party component that does this either (without spending thousands on completely unnecessary features).
Here goes:
I have a jpeg of a floor tile (actual photo) that I create a checkerboard pattern with.
In dotnet, it is easy to rotate and stitch photos together and save the final image as a jpeg.
Next, I want to take that final picture and make it appear as if the "tiles" are laying on a floor for a generic "room scene". Basically adding a 3D perspective to make it appear as if it is actually in the room scene.
Heres a website that is doing something similar with carpeting, however I need to do this in a WinForms application:
Flor Website
Basically, I need to create a 3D perspective of a jpeg, then save it as a new jpeg (then I can put an overlay of the generic room scene).
Anyone have any idea on where to get a 3rd party DotNet image processing module that can do this seemingly simple task?
It is not so simple because you need a 3D transformation, which is more complicated and computationally expensive than a simple 2D transformation such as rotation, scaling or shearing. For you to have an idea of the difference in the math, 2D transformations require 2 by 2 matrices, whereas a projection transformation (which is more complicated than other 3D transforms) requires a 4 by 4 matrix...
What you need is some 3D rendering engine in which you can draw polygons (in a perspective view) and them cover them with a texture (like a carpet). For .Net 2.0, I'd recommend using SlimDX which is a port of DirectX that would allow you to render polygons, but there is some learning curve. If you are using WPF (.Net 3.0 and up), there is a built in 3D canvas that allows you to draw textured polygons in perspective. That might be easier/better to learn than SlimDX for your purposes. I'm sure that there is a way to redirect the output of the 3D canvas towards a jpeg...
You might simplify the problem a lot if you don't require great performance and if you restrict the orientation of the texture (eg. always a horizontal floor or always a vertical wall). If so, you could probably render it yourself with a simple drawing loop in .Net 2.0.
If you just want a plain floor, your code would look like this. WARNING: Obtaining your desired results will take some significant time and refinement, specially if you don't know the math very well. But on the other hand, it is always fun to play with code of this type... (:
Find some sample images below.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace floorDrawer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ResizeRedraw = DoubleBuffered = true;
Width = 800;
Height = 600;
Paint += new PaintEventHandler(Form1_Paint);
}
void Form1_Paint(object sender, PaintEventArgs e)
{
// a few parameters that control the projection transform
// these are the parameters that you can modify to change
// the output
double cz = 10; // distortion
double m = 1000; // magnification, usually around 1000 (the pixel width of the monitor)
double y0 = -100; // floor height
string texturePath = #"c:\pj\Hydrangeas.jpg";//#"c:\pj\Chrysanthemum.jpg";
// screen size
int height = ClientSize.Height;
int width = ClientSize.Width;
// center of screen
double cx = width / 2;
double cy = height / 2;
// render destination
var dst = new Bitmap(width, height);
// source texture
var src = Bitmap.FromFile(texturePath) as Bitmap;
// texture dimensions
int tw = src.Width;
int th = src.Height;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
double v = m * y0 / (y - cy) - cz;
double u = (x - cx) * (v + cz) / m;
int uu = ((int)u % tw + tw) % tw;
int vv = ((int)v % th + th) % th;
// The following .SetPixel() and .GetPixel() are painfully slow
// You can replace this whole loop with an equivalent implementation
// using pointers inside unsafe{} code to make it much faster.
// Note that by casting u and v into integers, we are performing
// a nearest pixel interpolation... It's sloppy but effective.
dst.SetPixel(x, y, src.GetPixel(uu, vv));
}
// draw result on the form
e.Graphics.DrawImage(dst, 0, 0);
}
}
}