how can I draw when the form is open? - c#

Hello it might be a silly question but i can't figure out the problem here.. here is my code to fill a form with a single block:
private void drawBackground()
{
Graphics g = genPan.CreateGraphics();
Image Block = Image.FromFile(#"C:\Users\Administrator\Desktop\movment V1\movment V1\images\BrownBlock.png");
float recWidth = Block.Width;
// rectangle width didnt change the name from previous code, it's picture width.
float recHeight = Block.Height;
// rectangle Heightdidnt change the name from previous code, it's picture Height.
float WinWidth = genPan.Width; // genPan is a panel that docked to the form
float WinHeight = genPan.Height;
float curWidth = 0; //indicates where the next block will be placed int the X axis
float curHeight = 0;//indicates where the next block will be placed int the Y axis
while ((curHeight + recHeight) <= WinHeight)
{
if (curWidth >= WinWidth / 3 || curWidth <= WinWidth / 1.5 ||
curHeight >= WinHeight / 3 || curHeight <= WinHeight / 1.5)
{
g.DrawImage(Block, curWidth, curHeight, recWidth , recHeight );
}
curWidth += recWidth;
if ((WinWidth - curWidth) < recWidth)
{
curWidth = 0;
curHeight += 50;
}
}
}
If I launch this func through a button it will work perfectly fine. But if I launch the func after the InitializeComponent(); method in the constructor OR in a FORM shown event, while the button is still on the form it will execute the func however the block backgroud wont be visible but the grey color will be. but if i remove the button the background will be visible. =\
I cant understand why is it happening, how to fix it and what am I doing wrong.. can anyone explain please..?

You can't really do that using your current logic. The problem is that the control (genPan panel in your case) has its own Paint event that when called, overwriting any graphics you used on it.
Even when you draw in button click it works only until the form is repainted e.g. try focus other window and focus your form again: you will lose what you have drawn.
Proper way to do such things is to write your own class that inherit from some basic control (Panel in your case) then overriding its OnPaint event and draw whatever you want there.
So first, have such class:
public class BlockBackgroundPanel : Panel
{
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Image Block = Image.FromFile(#"C:\Users\Administrator\Desktop\movment V1\movment V1\images\BrownBlock.png");
float recWidth = Block.Width;
//rest of your code, replace "genPan" with "this" as you are inside the Panel
}
}
Then in your .Designer.cs file (you can open it in the Studio) change the code so that genPan will be of your new class instance:
private BlockBackgroundPanel genPan;
//...
this.genPan = new BlockBackgroundPanel ();

If you need to draw background only based on some condition/action/user interaction...
Put the call to this funciton into the forms OnPaint method and enable it only if some bollean variale equals true. And that boolean becomes true, only on button click.
Some hypothetical example:
protected override OnPaint(...) //FORMS ONPAINT OVERRIDE
{
if(needBackGround) //INITIAL VALUE AT STARTUP IS FALSE
drawBackground();
}
public void ButtonClickHandler(...)
{
needBackGround= !needBackGround; //INVERSE THE VALUE OF BOOLEAN
}
This is clearly just a sniplet to give you a hint and not a real code. There could be other problems you will need to face, like: flickering, handling resize, performance... but this is just a point to start.

Related

Transparent image over a control

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);

Zedgraph drawing a line with the mouse

I wrote a code to draw a line using the mouse.
On mouse down i save where the user clicked.
bool mZgc_MouseDownEvent(ZedGraphControl sender, MouseEventArgs e)
{
GraphPane graphPane = mZgc.GraphPane;
graphPane.ReverseTransform(e.Location, out mMouseDownX, out mMouseDownY);
return false;
}
On mouse up i draw the line:
bool zgc_MouseUpEvent(ZedGraphControl sender, MouseEventArgs e)
{
GraphPane graphPane = mZgc.GraphPane;
double x, y;
graphPane.ReverseTransform(e.Location, out x, out y);
LineObj threshHoldLine = new LineObj(Color.Red, mMouseDownX, mMouseDownY, x, y);
graphPane.GraphObjList.Add(threshHoldLine);
mZgc.Refresh();
return false;
}
The problem is that while the mouse is down, the user don't see the line (because i draw it only on "up" event).
How can i solve it ?
Technically I can use "on hover" and draw/remove the line from the graph each second and refresh the graph, but it's a bit crazy.
Is there a "normal" way to do this ?
Thanks
Ok, I've solved the problem I'll post the answer for future generations.
First of all we need to make a minor change in the zedgraph code to allow ourselves to change lines after they created, It probably breaks some "immutable" paradigm that the creators are trying to achieve, but if you want to avoid creating and deleting lines every time the mouse is moving that's the way.
In the file Location.cs edit the X2,Y2 properties (set was missing):
public double X2
{
get { return _x+_width; }
set { _width = value-_x; }
}
public double Y2
{
get { return _y+_height; }
set { _height = value-_y; }
}
from here it is easy, i won't post all the code but will explain the steps:
Add a member to your class : private LineObj mCurrentLine;
On mouse down create a line mCurrentLine = new LineObj(Color.Red, mMouseDownX, mMouseDownY, mMouseDownX, mMouseDownY);
On mouse move change the line X2,Y2 coordinates mCurrentLine.Location.X2 = x;
On mouse up just stop that drawing process (to avoid changing the line in "on mouse move"
If someone actually going to use it, and needs better explanation, just comment.

How to automatically snap a WPF window to an edge of the screen while retaining its size?

As the application starts, I'd like my WPF window to automatically snap to the right edge of the screen. Is there a way to do that? I also want to be able to retain its dimensions. So, unlike the snapping behavior that happens when you drag a window to the edge of the screen, causing the window to resize to either a portion of the screen or full screen, I want my window to simply snap to the edge at a certain location by default or if dragged by the user to a specific location afterwards, without resizing. I still want to retain the ability of the user to drag the window away from the edge.
Is there anything like that already implemented or would I have to create my own behavior schema? I tried numerous search keyword combinations, but couldn't find anything similar to what I'm doing. Some of the searches included disabling snapping behavior or providing snapping behavior, but nothing in the way I described above.
EDIT:
I haven't been able to find a ready solution, so I wrote my own. This solution is based on BenVlodgi's suggestions, so I thank him for helping me out. This is a very rough implementation and still requires a lot of polishing and better code techniques, but it works and it's a good base for anyone wanting to try this. It's incredibly simple and works very well with WPF. The only limitation of this implementation is that I haven't tried getting it to work with two screens yet, but it's incredibly simple (I'm just not going to have time for it and I don't need that functionality at this point). So, here's the code and I hope that it helps someone out there:
public partial class MainWindow : Window
{
// Get the working area of the screen. It excludes any dockings or toolbars, which
// is exactly what we want.
private System.Drawing.Rectangle screen =
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
// This will be the flag for the automatic positioning.
private bool dragging = false;
// The usual initialization routine
public MainWindow()
{
InitializeComponent();
}
// Wait until window is lodaded, but prior to being rendered to set position. This
// is done because prior to being loaded you'll get NaN for this.Height and 0 for
// this.ActualHeight.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Sets the initial position.
SetInitialWindowPosition();
// Sets the monitoring timer loop.
InitializeWindowPositionMonitoring();
}
// Allows the window to be dragged where the are no other controls present.
// PreviewMouseButton could be used, but then you have to do more work to ensure that if
// you're pressing down on a button, it executes its routine if dragging was not performed.
private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// Set the dragging flag to true, so that position would not be reset automatically.
if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
{
dragging = true;
this.DragMove();
}
}
// Similar to MouseDown. We're setting dragging flag to false to allow automatic
// positioning.
private void Window_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
{
dragging = false;
}
}
// Sets the initial position of the window. I made mine static for now, but later it can
// be modified to be whatever the user chooses in the settings.
private void SetInitialWindowPosition()
{
this.Left = screen.Width - this.Width;
this.Top = screen.Height / 2 - this.Height / 2;
}
// Setup the monitoring routine that automatically positions the window based on its location
// relative to the working area.
private void InitializeWindowPositionMonitoring()
{
var timer = new System.Windows.Threading.DispatcherTimer();
timer.Tick += delegate
{
// Check if window is being dragged (assuming that mouse down on any portion of the
// window is connected to dragging). This is a fairly safe assumption and held
// true thus far. Even if you're not performing any dragging, then the position
// doesn't change and nothing gets reset. You can add an extra check to see if
// position has changed, but there is no significant performance gain.
// Correct me if I'm wrong, but this is just O(n) execution, where n is the number of
// ticks the mouse has been held down on that window.
if (!dragging)
{
// Checking the left side of the window.
if (this.Left > screen.Width - this.Width)
{
this.Left = screen.Width - this.Width;
}
else if (this.Left < 0)
{
this.Left = 0;
}
// Checking the top of the window.
if (this.Top > screen.Height - this.Height)
{
this.Top = screen.Height - this.Height;
}
else if (this.Top < 0)
{
this.Top = 0;
}
}
};
// Adjust this based on performance and preference. I set mine to 10 milliseconds.
timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
timer.Start();
}
}
Make sure that your window has the following:
MouseDown="Window_MouseDown"
MouseUp="Window_MouseUp"
WindowStartupLocation="Manual"
Loaded="Window_Loaded"
Also, this doesn't work well with Windows native components of the window, such as the top bar, so I disable the style and create my own (which is actually good for me, since I don't want the windows style for this):
WindowStyle="None"
I don't like the polling approach, but it's been excessively difficult to find a better solution that is still simple in WPF, so I'm gonna post my own.
The solution that I found is actually quite simple, in that it reimplements the behaviour of the DragMove() method of the window, which gives you the option to change the window position while it's being dragged. The following code reimplements DragMove() by storing the distance between the top left corner of the window and the mouse cursor.
public partial class MainWindow : Window
{
// this is the offset of the mouse cursor from the top left corner of the window
private Point offset = new Point();
public MainWindow()
{
InitializeComponent();
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point cursorPos = PointToScreen(Mouse.GetPosition(this));
Point windowPos = new Point(this.Left, this.Top);
offset = (Point)(cursorPos - windowPos);
// capturing the mouse here will redirect all events to this window, even if
// the mouse cursor should leave the window area
Mouse.Capture(this, CaptureMode.Element);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (Mouse.Captured == this && Mouse.LeftButton == MouseButtonState.Pressed)
{
Point cursorPos = PointToScreen(Mouse.GetPosition(this));
double newLeft = cursorPos.X - offset.X;
double newTop = cursorPos.Y - offset.Y;
// here you can change the window position and implement
// the snapping behaviour that you need
this.Left = newLeft;
this.Top = newTop;
}
}
}
Now you could implement the snapping / sticky window behaviour like this: The window will stick to the edge of the screen if it's within a range of 25 pixels (plus or minus).
int snappingMargin = 25;
if (Math.Abs(SystemParameters.WorkArea.Left - newLeft) < snappingMargin)
newLeft = SystemParameters.WorkArea.Left;
else if (Math.Abs(newLeft + this.ActualWidth - SystemParameters.WorkArea.Left - SystemParameters.WorkArea.Width) < snappingMargin)
newLeft = SystemParameters.WorkArea.Left + SystemParameters.WorkArea.Width - this.ActualWidth;
if (Math.Abs(SystemParameters.WorkArea.Top - newTop) < snappingMargin)
newTop = SystemParameters.WorkArea.Top;
else if (Math.Abs(newTop + this.ActualHeight - SystemParameters.WorkArea.Top - SystemParameters.WorkArea.Height) < snappingMargin)
newTop = SystemParameters.WorkArea.Top + SystemParameters.WorkArea.Height - this.ActualHeight;
The downside of this approach is that the snapping will not work, if the window is being dragged on the title bar, because that doesn't fire the OnMouseLeftButtonDown event (which I don't need, because my window is borderless). Maybe it will still help someone.
There is no API calls you can make (as far as I've seen) to use the Windows snapping features, however you could just get the System.Windows.Forms.Screen.PrimaryScreen.WorkingArea of the screen and set your Top, Left, Height and Width Properties of your Window accordingly.
Edit: The above suggestion does require Forms, which you probably don't want. I believe the WPF equivalent is System.Windows.SystemParameters.WorkArea

Graphics.Clear on a Windows Form Control also clears the control itself - how to prevent this?

I try to draw a custom selection rectangle on a Picturebox, using the method below:
void _drag_UpdateView(bool clearOnly, MouseEventArgs e)
{
System.Diagnostics.Debug.WriteLine("UpdateView");
using (Graphics g = this.i_rendered.CreateGraphics())
{
g.Clear(Color.Transparent);
if (clearOnly)
return;
int px = (_drag_start.X > e.Location.X)?e.Location.X:_drag_start.X;
int py = (_drag_start.Y > e.Location.Y)?e.Location.Y:_drag_start.Y;
if (px < 0)
px = 0;
if (py < 0)
py = 0;
int wx = Math.Abs(e.Location.X-_drag_start.X);
int wy = Math.Abs(e.Location.Y-_drag_start.Y);
g.DrawRectangle(Pens.LightBlue, px, py, wx, wy);
}
}
Whenever I call:
g.Clear(Color.Transparent);
the whole picturebox turns black. However, the rectangle gets drawn over it.
If I do not call that method, the rectangles stack on itself, of course. I want to remove the old rectangle and create a new one like this.
Can anyone describe what's wrong?
Do not paint your control inside any other method than OnPaintBackground or OnPaint.
Actually you may need it for performance reasons (your won't refresh the full control, just change something on-the-fly) but it'll make your code much more tricky and you'll always need to do the same job inside the Paint event too (because it may be called for many other reasons so the output should be the same).
Inside the Paint you do not even need to use CreateGraphics, the graphics context is inside the PaintEventArgs object so change your code to:
void DoPainting(object sender, PaintEventArgs e)
{
// Do your calculations
e.Graphics.DrawRectangle(Pens.LightBlue, px, py, wx, wy);
}
As pointed by #AndersForsgren to use the CreateGraphics method is not common and usually you do not need to call it (with the exception pointed before and when, for example, you need to perform some calculations based on the graphics like for AutoSize).

Redrawing to a silverlight canvas every cycle of a loop. Is it possible?

any help or pointers would be massively appreciated.
Basically i'm trying to EITHER move OR redraw some ellipses to a canvas once a "turn".
At the moment I can click a button to RenderTransform the ellipse to a new location within the canvas. Whenever I try to do this more than once, for example, incrementing the TranslateTransform X and Y values by one each loop, the whole application hangs. Is this an issues which RenderTransform? The Canvas? The MainPage thread? The code looks solid, so its a mystery as to why it simply wouldnt move the ellipse more than once.
private void update()
{
int x = 0;
int y = 0;
while (turns <= 5)
{
TranslateTransform t = new TranslateTransform();
t.X = x + 1;
t.Y = y + 1;
// agent is a child element of a canvas.
agent.RenderTransform = t;
turns--;
}
}
You can use CompositionTarget it will help you
look at this example
another good example
use it like this:
private void Update(object sender, EventArgs e)
{
//Your code here
}
and hook the Rendering event CompositionTarget.Rendering += Update;
Edit: If you are using Canvas it's better to use Canvas.Left and Canvas.Top attached properties

Categories

Resources