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);
Related
I am a beginner.
Problem is:
Even if BackColor is set to "web-transparent" it is really not transparent on some element such as a WebBrowser.
Looks like the color of mother element is applied.
Is there a way to make it fully transparent?
Or maybe I need to use some other element in this case? I just need to show a text and be able to drug it with a mouse.
Have a great day!
It will be nice if setting the BackColor to transparent will affect it in "human understanding" way)
You're correct that the reason a WinForms control appears transparent is that the "the background of a transparent Windows Forms control is painted by its parent" as explained in the MS documentation. This means the parent control's background will be drawn into the Label even if there's another control (you mentioned web browser) in between.
The remedy is to change the shape of the Label control itself, by assigning a drawable Region that excludes the transparent pixels.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// Change shape of Label control, removing transparent pixels.
label1.BackColor = Color.Transparent;
Bitmap bitmap = new Bitmap(label1.Width, label1.Height);
label1.DrawToBitmap(bitmap, label1.ClientRectangle);
Region region = new Region(label1.ClientRectangle);
for (int x = 0; x < label1.Width; x++) for (int y = 0; y < label1.Height; y++)
{
if (bitmap.GetPixel(x, y).A == 0)
{
region.Exclude(new Rectangle(x, y, 1, 1));
}
}
label1.Region = region;
}
}
You say you want to drag it so be advised that a control coded this way will only detect a mouse click on the region of the control that still exists namely the text. But here's a sample solution for a transparent, draggable Label element that you can feel free to clone and experiment with.
It seems like this should be simple enough, but I'm really struggling with finding any documentation on how I can do this. I'm simply looking to crop an image to turn a square into a circle.
There is a lot of discussion about it, but I can't seem to find a good example of how to do this using UWP/Win2D.
Here is a bit of code to illustrate the issue I was trying to describe in my comments:
// draw a 10x10 grid of circles
var bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/ice.png"); // hex-shaped image is 250x220 pixels
var brush = new CanvasImageBrush(sender, bitmap);
for (var i = 0; i < 10; i++)
{
for (var j = 0; j < 10; j++)
{
//_drawingSession.FillCircle(new Vector2(i * 50, j * 50), (float)(25), Colors.Blue);
_drawingSession.FillCircle(new Vector2(i * 50, j * 50), (float)(25), brush);
}
}
The image below shows how the brush is being cut from the same x/y coordinates based on the vector where the target circle is to be drawn.
Note: the same effect occurs with FillEllipse().
You can try to use CanvasImageBrush and CanvasDrawingSession.FillEllipse Method achieve it.
private async void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
using (CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/image.jpg"))
{
CanvasImageBrush canvasImageBrush = new CanvasImageBrush(sender, bitmap);
args.DrawingSession.FillEllipse(new System.Numerics.Vector2(100f), 100, 100, canvasImageBrush);
}
}
------------ Update -------------
If you want to cut a circle out of the image source, you can configure the CanvasImageBrush.Transform property to scale the image, then cut the circle and display it on the canvas.
private async void canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,
Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args)
{
using (CanvasBitmap bitmap = await CanvasBitmap.LoadAsync(sender, "Assets/image.jpg"))
{
CanvasImageBrush canvasImageBrush = new CanvasImageBrush(sender, bitmap);
System.Numerics.Vector2 center = new System.Numerics.Vector2((float)(bitmap.Size.Width / 2),
(float)(bitmap.Size.Height / 2));
canvasImageBrush.Transform = System.Numerics.Matrix3x2.CreateScale(0.5F, center);
args.DrawingSession.FillEllipse(center, 160, 160, canvasImageBrush);
}
}
You should change some parameters in my above code to satisfy your requirement, such as the scale in the Matrix3x2.CreateScale method.
Okay, after chatting with one of the fellows on the GitHub Win2D project, I finally have a clear answer on how this works - and it works nothing like I would have expected it to work.
First, the bitmap brush image is by default being positioned at 0,0 on the canvas.
In my case, I wanted to cut a circle from the image and draw it someplace else on the canvas. This requires 2 separate bits of math.
First, you need to position the bitmap's top-left-corner (TLC) to where you want the circle to be drawn. This is done by setting the brush's Transform property. In my example, I'm setting the image TLC to 300/300;
// create the brush
var brush = new CanvasImageBrush(sender, _tiles[1]);
brush.Transform = Matrix3x2.CreateTranslation(300, 300);
Now, to cut/draw the circle using the brush image, I have to describe where the center of the image is to be on the canvas. My image is 250x220.
// 300+250/2, 300+220/2 = 425, 410
_args.DrawingSession.FillCircle(new Vector2(425, 410), (float)(110), brush);
This gives the effect of cutting a circle out of my original bitmap and drawing it on the canvas at the desired location.
Hopefully this is clear enough. I know I certainly struggled to find the answer.
I have a usercontrol that displays a gradient of colors which, once created will be constant.
The usercontrol doesn't contain any controls, not sure if i need to add a picturebox or dynamically add one.
Over that image, I'd like to display a line that will display what the current result is. I have no problem creating the gradient image on the map, however I'd like to somehow cache it so everytime I update the indicator (call CurrentValue from parent form), it will put the indicator line above the gradient image. This is updating about 30 times a second, thus, as of how the code below is working, it's repainting the gradient everytime, which is flickering.
Here's a code sample:
namespace Maps.UserControls
{
public partial class UserControlLegend : UserControl
{
private double m_CurrentValue;
public double CurrentValue
{
get
{
return m_CurrentValue;
}
set
{
m_CurrentValue = value;
RefreshValue();
}
}
public UserControlLegend()
{
InitializeComponent();
}
private void UserControlLegend_Paint(object sender, PaintEventArgs e)
{
if (b == null)
{
g = e.Graphics;
b = new System.Drawing.Bitmap(menuWidth, menuHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// Code here that draws Menu
// Cache bitmap here?
g.Dispose();
}
}
private void RefreshValue()
{
this.Refresh();
g = this.CreateGraphics();
g.DrawImage(b, 0, 0);
//Code to Calcuate current Indicator Location
int x3 = 0;
// Draws current indicator correctly
g.DrawLine(new Pen(new SolidBrush(Color.Black)), this.Width / 2 - 15, x3, this.Width / 2 - 5, x3);
g.Dispose();
}
}
}
Explained above in comments, used a bitmap, and just set the x,y of the control.
First, I'd suggest you set your Control's property DoubleBuffered to True, so that flickering goes away. However, if you don't draw on the Control itself, that will be not useful at all. Drawing on a PictureBox is better, however, becuase it is automatically DoubleBuffered.
Second, you are painting into a new Bitmap every time, which is very bad in terms of memory, since the Bitmap is a few megabytes in size. I'd suggest you have a single Bitmap initialized in the constructor, and a single Graphics, created from that Bitmap in the constructor, too. Every time the paint accurs, just clear the old Graphics g and then draw onto it again and again. g Graphics and b Bitmap should be Disposed one time only, when the entire Control is Disposed.
This may inhance your code.
C#3.0,.net framework 3.5
I am drawing ( using the draw method in the graphics class) a lot of solid rectangles on a windows form vertically. The form starts at 500 x 500 px and the rectangles are only drawn at runtime after data is downloaded from the net -and the number of rectangles depends on the download so I do not know it upfront.
So only a few rectangles are drawn as the size of the form is fixed.
So I googled/Binged ( lest someone suggest I do that) and found a few tips but they don't work in this case -like setting the forms AutoScroll property to true or trying double buffering.I also tried to draw on a listbox control and set it's scroll property etc...but no dice.
I'm guessing there is no way to display , say 200 rectangles vertically on a windows form using draw. I need some other solution... any ideas please.
Maybe a list of pictureboxes and then populate each picturebox with the solid color ?
Thanks
You are drawing GDI+ rectangles on a form during the paint event? The form would have no idea that you are creating objects outside of the clipping space and would therefore have no idea that you need to scroll.
You would need to add a scrollbar to the form and then calculate the value\position of the scrollbar and use that to determine what portion of your rectangles to draw upon the paint event. This would involve a bit of manual effort. You could draw them all to an in-memory bitmap of the appropriate size and then just copy the portions of that to the form upon draw.
Or:
If you wanted the form to do this for you, create a custom rectangle control and place 200 of those on the form. Since they are components and have a concrete height & width, the form would then know it needed to scroll, and would do so accordingly provided that autoscroll was set.
it can be as simple as this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
this.AutoScroll = true;
for (int i = 0; i < 100; i++)
this.Controls.Add(new Rectangle() { Top = i * 120, Left = 10 });
}
}
public class Rectangle : Control
{
public Rectangle()
{
this.Width = 100;
this.Height = 100;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Black, 5), 0, 0, 100, 100);
}
}
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.