Updating graphics on panels - c#

OK so I am new to C# and I am confused about how events work. currently I have a panel in which I am drawing rectangles. the event is called when the panel is initialized but I want to call it after I do something in my data Grid. I simply want to know how to tell the object to update.
Do I not use an event in this situation? If so do I just create a new graphics instance and start?
Here is my code:
private void panel6_Paint(object sender, PaintEventArgs e)
{
if(dataGridView1.RowCount != 0 )
{
Pen lightRed = new Pen(Brushes.LightSalmon);
lightRed.Width = 1.0F;
lightRed.LineJoin = System.Drawing.Drawing2D.LineJoin.Miter;
int counter = 0;
foreach (var pair in currentPosition)
{
if(dataGridView1[0, counter].Style.BackColor == Color.Red)
{
e.Graphics.DrawRectangle(lightRed, new Rectangle(0, currentPosition.Count / panel6.Height * counter, 66, currentPosition.Count / panel6.Height * counter));
}
}
lightRed.Dispose();
}
}

Whenever you want to force a redraw, call:
Invalidate();
...or
Refresh();
Invalidate() is preferred...Refresh() attempts to be more immediate.
So, for your panel:
panel6.Invalidate();
You would call this at any point where you want to signal to the control that it should repaint itself. That will result in your Paint event firing.

Related

Binding Writeable bitmap to canvas

I'm trying to write a simple WPF program to implement Conway's Game of Life.
My Window consists of a toolbar and a canvas with and embedded Image, on which I'm trying to display a writeable bitmap.
I have a button on the toolbar which, when clicked, updates a single generation and then displays the resulting bitmap on the canvas image. I update the image directly with
img.Source = wbmp;
This works without a problem.
However, I'd like to show the ongoing updates, without having to click the "Update" button each time. So I tried implementing a loop of 10 iterations. However, the updated image is only shown after the 10th iteration is completed (i.e. I don;t see the first 9 generations)
My understanding is that I need to bind the Image control to the Writeable bitmap in order to "force" an update each generation. I've tried this with the code below - but now nothing displays at all. Initially I found that the PropertyChanged event didn't seem to be firing but I had no method assigned so, I added PropertyChanged = delegate {}; (I did this because the Internet told me to!)
I am really not sure where I'm going wrong. I'm rather clueless about WPF and binding in particular. (Much of my code is adapted copy-pasta.) Any help would be greatly appreciated!
<Canvas Name ="canvas" Grid.Row="1" Background="LightGray">
<Image Name="img" Source="{Binding GoLImage}"/>
</Canvas>
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource golImage; //writeable bitmap;
public ImageSource GoLImage
{
get { return golImage; }
set
{
golImage = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GoLImage)));
}
}
}
private void Button_Run10_Click(object sender, RoutedEventArgs e)
{
BitmapPixelMaker bmp = new BitmapPixelMaker(1200, 800);
for (int i = 0; i < 10; i++)
{
goL.UpdateLifeGrid(); //goL is an instance of my class implementing the Game of Life
goL.LifeGridTobmp(bmp);
WriteableBitmap wbmp = bmp.MakeBitmap(96, 96);
//Trying to display bitmap
MyViewModel golDisplay = new MyViewModel();
golDisplay.GoLImage = wbmp; //This doesn't automatically display on each iteration
}
}
The for loop in your Button Click handler blocks the UI thread. And you have not assigned the view model instance to the DataContext property of the view.
Use a DispatcherTimer with a Tick event handler like shown below.
Do not create a new view model instance and a new WriteableBitmap in each cycle, but just modify the existing one - you should therefore change the view model property declaration to public WriteableBitmap GoLImage so that the ModifyBitmap method can access it.
private readonly MyViewModel golDisplay = new MyViewModel();
private readonly DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(0.1)
};
public MainWindow()
{
InitializeComponent();
golDisplay.GoLImage = WriteableBitmap(1200, 800, 96, 96, PixelFormats.Default, null);
DataContext = golDisplay;
timer.Tick += OnTimerTick;
timer.Start(); // optionally, call Start/Stop in a Button Click handler
}
private void OnTimerTick(object sender, EventArgs e)
{
var bmp = new BitmapPixelMaker(1200, 800);
goL.UpdateLifeGrid();
goL.LifeGridTobmp(bmp);
ModifyBitmap(bmp); // write directly to golDisplay.GoLImage
}
An alternative to a DispatcherTimer might be an simple loop over an asynchronous and awaited update call.
Something like shown below, where the Update method would perform the CPU-intensive calculations and then return the new pixel buffer
private async void OnWindowLoaded(object sender, RoutedEventArgs e)
{
while (true)
{
var buffer = await Task.Run(() => game.Update());
bitmap.WritePixels(new Int32Rect(0, 0, game.Width, game.Height),
buffer, game.Stride, 0);
}
}

How to print a Windows Form without showing/displaying it

I am trying to print automatically a series of Windows-Forms. I don't need to show them. The code examples I found on the internet work only when I display the Form with show()! I need to initialize the form with data and send it to the printer Here is the code I am using:
public partial class Form2_withoutShow : Form{
PrintDocument PrintDoc;
Bitmap memImage;
public Form2_withoutShow (Data data)
{
InitializeComponent();
/*
Initialize Data (texboxes, charts ect.) here
*/
this.PrintDoc = new PrintDocument();
this.PrintDoc.PrintPage += PrintDoc_PrintPage;
this.PrintDoc.DefaultPageSettings.Landscape = true;
}
public void Print()
{
this.PrintDoc.Print();
}
void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
{
int x = SystemInformation.WorkingArea.X;
int y = SystemInformation.WorkingArea.Y;
int width = this.Width;
int height = this.Height;
Rectangle bounds = new Rectangle(x, y, width, height);
Bitmap img = new Bitmap(width, height);
this.DrawToBitmap(img, bounds);
Point p = new Point(10, 10);
e.Graphics.DrawImage(img, p);
}
private void Form2_withoutShow_Load(object sender, EventArgs e)
{
// remove TITLEBar
this.ControlBox = false;
this.Text = String.Empty;
}
}
I call the Print() method from another class in for-loop and pass the Data to be initialized through the constructor.
The MSDN example captures the part on the screen where the forms should be displayed. This doesn't work for me. The approach I am using now yields only the print of the empty Window if I don't call show(). How do I get the data into the form without having to call the show() method? Approaches like mimize the windows when displaying it, also don't work because that is also the print result: a minimized window.
Before showing the form, the form and its controls are not in Created state. To force the form and its controls to be created it's enough to call internal CreateControl(bool fIgnoreVisible) method of your form:
var f = new Form1();
var createControl = f.GetType().GetMethod("CreateControl",
BindingFlags.Instance | BindingFlags.NonPublic);
createControl.Invoke(f, new object[] { true });
var bm = new Bitmap(f.Width, f.Height);
f.DrawToBitmap(bm, new Rectangle(0, 0, bm.Width, bm.Height));
bm.Save(#"d:\bm.bmp");
Also remove codes which you have in your form Load event handler, and put them in constructor of your form.
Note
There are also other workarounds for the problem:
For example you can show the form in a location outside of boundary of the screen and then hide it again. Set the Location to (-32000, -32000) and set StartPosition to be Manual then Show and Hide the form before drawing to bitmap.
Or as another example, you can show the form with Opacity set to 0 and then Show and Hide the form before drawing to bitmap.

Fading effect on Splash sreen

I am trying to make a fading effect on my splash screen, on a WPF application.
The Opacity of the image object is initially 0. This code would modify the Opacity from 0 (min) to 1 (max), but the line img_waves.Opacity just doesn't work. The image opacity remains 0.
private void Splash_ContentRendered(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(3000);
for (double x = 0; x<=1; x+=0.01d)
{
System.Threading.Thread.Sleep(15);
//MessageBox.Show(x.ToString());
img_waves.Opacity = x;
}
this.Close();
}
But, if I activate the line ´MessageBox.Show(x.ToString());´
as you can see on this image:
The code works, but I have to keep clicking on the message boxes.
My ask is: Why? Why doesn't work without the MessageBox.Show?
Because you're blocking the GUI thread. It never gets a chance to redraw the form. When you add the message box, the message queue is pumped, which allows the drawing.
The simplest way to deal with this would be like this:
private async void Splash_ContentRendered(object sender, EventArgs e)
{
await Task.Delay(3000);
for (double x = 0; x<=1; x+=0.01d)
{
await Task.Delay(15);
img_waves.Opacity = x;
}
this.Close();
}
Do note that this means the form can still be interacted with during the animation. This shouldn't be a problem for a splashscreen, but it could cause you trouble in a "real" form. Still, make sure the form can't be closed during the animation - that could cause exceptions :)
There's also other ways to force the message queue to be pumped, but it's usually frowned upon.
All that said, you're using WPF - why are you doing the animation manually like this? Can't you just handle it as an animation effect in WPF, natively? There's a sample on MSDN.
Whilst I agree with #Luaan explanation as to why as an alternative solution to your loop you can use Storyboard with DoubleAnimation on Opacity property
private void Splash_ContentRendered(object sender, EventArgs e)
{
var sb = new Storyboard();
var da = new DoubleAnimation(0, 1, new Duration(TimeSpan.FromSeconds(1.5)));
da.BeginTime = TimeSpan.FromSeconds(3);
Storyboard.SetTargetProperty(da, new PropertyPath("Opacity"));
Storyboard.SetTarget(da, img_waves);
sb.Children.Add(da);
sb.Completed += (s1, e1) => this.Close();
sb.Begin();
}

Background image shows, but pen lines don't

I set a background of a picturebox as an image, which is fine.
I try to draw pen lines over the background image, but the lines do not appear on top of the image
This should happen: (I changed the code and not a clue what I changed to get this to happen, but this is without resetting the picturebox)
The picturebox is updated every 2000ms to show the flag signals, but can't for the life of me get the lines to appear.
Here are the bits of code:
lblSelectedChar.Text = inputchar.ToString();
picSemaphore.BackgroundImage = semaphore.Properties.Resources.human;
leftHandDown();
rightHandLow();
The leftHandDown() and rightHandLow() methods draw the lines, but I think they are drawn behind the image. I would like them drawn in front. Any idea how I would do this?
private void leftHandDown()
{
lblLeftHand.Hide();
display.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
}
Also, when the label is moved, it leaves a white space where it last was. Any idea how to stop this happening?
As LarsTech suggested, use the Paint() event of your PictureBox to draw the lines with the supplied Graphics via the e.Graphics value. You'd call picSemaphore.Refresh(); to make the lines update.
Here's a quick example to give you the basic idea:
public partial class Form1 : Form
{
private bool displayLeftArmLine = false;
private bool displayRightArmLine = false;
public Form1()
{
InitializeComponent();
picSemaphore.Paint += new PaintEventHandler(picSemaphore_Paint);
}
void picSemaphore_Paint(object sender, PaintEventArgs e)
{
if (displayLeftArmLine)
{
e.Graphics.DrawLine(penLeftArm, centXCoord, centYCoord, LHDownXCoord, LHDownYCoord);
}
if (displayRightArmLine)
{
e.Graphics.DrawLine(penRightArm, centXCoord, centYCoord, RHDownXCoord, RHDownYCoord);
}
}
private void leftHandDown()
{
lblLeftHand.Hide();
lblLeftHand.Top = LHDownYCoord + 74;
lblLeftHand.Left = LHDownXCoord + 13;
lblLeftHand.Show();
lblLeftHand.Update();
displayLeftArmLine = true;
picSemaphore.Refresh(); // force Paint() event for "picSemaphore" to fire
}
}

Clear image on picturebox

How can I clear draw image on picturebox?
The following doesn't help me:
pictbox.Image = null;
pictbox.Invalidate();
Please help.
EDIT
private void pictbox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
vl.Draw(g, ref tran.realListForInsert);
}
public void Draw(Graphics g, ref List<double> arr)
{
g.DrawRectangle(new Pen(Brushes.Red, 3), nodeArr[Convert.ToInt32(templstName)].pict.Location.X, nodeArr[Convert.ToInt32(templstName)].pict.Location.Y, 25, 25);
g.DrawRectangle(new Pen(Brushes.Green, 3), nodeArr[Convert.ToInt32(templstArgName)].pict.Location.X, nodeArr[Convert.ToInt32(templstArgName)].pict.Location.Y, 25, 25);
nodeArr[Convert.ToInt32(templstName)].text.Text = arr[Convert.ToInt32(templstArgName)].ToString();
arr[Convert.ToInt32(templstName)] = arr[Convert.ToInt32(templstArgName)];
}
Setting the Image property to null will work just fine. It will clear whatever image is currently displayed in the picture box. Make sure that you've written the code exactly like this:
picBox.Image = null;
As others have said, setting the Image property to null should work.
If it doesn't, it might mean that you used the InitialImage property to display your image. If that's indeed the case, try setting that property to null instead:
pictBox.InitialImage = null;
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
pictureBox1.Image = null;
}
You need the following:
pictbox.Image = null;
pictbox.update();
private void ClearBtn_Click(object sender, EventArgs e)
{
Studentpicture.Image = null;
}
I assume you want to clear the Images drawn via PictureBox.
This you would be achieved via a Bitmap object and using Graphics object. you might be doing something like
Graphics graphic = Graphics.FromImage(pictbox.Image);
graphic.Clear(Color.Red) //Color to fill the background and reset the box
Is this what you were looking out?
EDIT
Since you are using the paint method this would cause it to be redrawn every time, I would suggest you to set a flag at the formlevel indicating whether it should or not paint the Picturebox
private bool _shouldDraw = true;
public bool ShouldDraw
{
get { return _shouldDraw; }
set { _shouldDraw = value; }
}
In your paint just use
if(ShouldDraw)
//do your stuff
When you click the button set this property to false and you should be fine.
For the Sake of Understanding:
Depending on how you're approaching your objective(s), keep in mind that the developer is responsible to Dispose everything that is no longer being used or necessary.
This means: Everything you've created along with your pictureBox (i.e: Graphics, List; etc) shall be disposed whenever it is no longer necessary.
For Instance:
Let's say you have a Image File Loaded into your PictureBox, and you wish to somehow Delete that file.
If you don't unload the Image File from PictureBox correctly; you won't be able to delete the file, as this will likely throw an Exception saying that the file is being used.
Therefore you'd be required to do something like:
pic_PhotoDisplay.Image.Dispose();
pic_PhotoDisplay.Image = null;
pic_PhotoDisplay.ImageLocation = null;
// Required if you've drawn something in the PictureBox. Just Don't forget to Dispose Graphic.
pic_PhotoDisplay.Update();
// Depending on your approach; Dispose the Graphics with Something Like:
gfx = null;
gfx.Clear();
gfx.Dispose();
Hope this helps you out.
I've had a stubborn image too, that wouldn't go away
by setting the Image and InitialImage to null.
To remove the image from the pictureBox for good, I had to use the code below,
by calling Application.DoEvents() repeatedly:
Application.DoEvents();
if (_pictureBox.Image != null)
_pictureBox.Image.Dispose();
_pictureBox.Image = null;
Application.DoEvents();
if (_pictureBox.InitialImage != null)
_pictureBox.InitialImage.Dispose();
_pictureBox.InitialImage = null;
_pictureBox.Update();
Application.DoEvents();
_pictureBox.Refresh();
I had to add a Refresh() statement after the Image = null to make things work.
I used this method to clear the image from picturebox. It may help some one
private void btnClear1_Click(object sender, EventArgs e)
{
img1.ImageLocation = null;
}
Its so simple!
You can go with your button click event,
I used it with a button property Name: "btnClearImage"
// Note 1a:
// after clearing the picture box
// you can also disable clear button
// by inserting follwoing one line of code:
btnClearImage.Enabled = false
// Note 1b:
// you should set your button Enabled property
// to "False"
// after that you will need to Insert
// the following line to concerned event or button
// that load your image into picturebox1
// code line is as follows:
btnClearImage.Enabled = true;
You should try. When you clear your Graphics you must choose color. SystemColors.Control is native color of form
Graphics g = pB.CreateGraphics();
g.Clear(SystemColors.Control);
clear pictureBox in c# winform Application
Simple way to clear pictureBox in c# winform Application

Categories