I have a Winforms app in C# that calls calls a method asynchronously and uses a callback.
I would like to display an animated gif to let the end user know that work is being done.
I would like to have the animated gif hover over the center of the form.
How can I do this?
Update:
Thanks. I guess the step I was missing was to use a Picture Box to hold the gif.
The following seems to be doing the trick of showing the gif and like jmatthews3865 said below I can just set the visible property of the PictureBox to false to hide it.
private ShowAnimatedGif()
{
PictureBox pb = new PictureBox();
this.Controls.Add(pb);
pb.Left = (this.Width / 2) - (pb.Width / 2);
pb.Top = (this.Height / 2) - (pb.Height / 2);
pb.Image = Resources.AnimatedGifHere;
pb.Visible = true;
}
in your form, simply include the image with it's visible property set to false.
from the event which calls the long running async process (button1_click etc.), set the images visibility property to true. event fires, image appears, async process runs and your ui thread should still be responsive.
in your callback event set the images visible property to false to indicate that the process is complete.
Need some code to give an exact answer, but this is fairly trivial, insert the gif before you make the asynchronous call, then remove it in the callback.
This is the answer. I'm using LoadingCircle which is an animated gif component.
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
}
private void button2_Click(object sender, EventArgs e)
{
Thread tr = new Thread(FlushToServer);
tr.Start();
}
private void ShowAnimation(bool show)
{
if (show)
{
loadingCircle1.Visible = true;
loadingCircle2.Active = true;
}
else
{
loadingCircle1.Visible = false;
loadingCircle1.Active = false;
}
}
private void FlushToServer()
{
this.Invoke(this.pa,true);
//your long running process
System.Threading.Thread.Sleep(5000);
this.Invoke(this.pa,false);
}
}
i modify the above code a bit and it will not throw error "c# invoke or begininvoke cannot be called on a control until the window handle has been created."
namespace AnimateUI
{
public partial class Form1 : Form
{
public delegate void ProcessAnimation(bool show);
ProcessAnimation pa;
public Form1()
{
InitializeComponent();
pa = this.ShowAnimation;
pictureBox1.Visible = false;
}
private void ShowAnimation(bool show)
{
if (show)
{
pictureBox1.Visible = true;
}
else
{
pictureBox1.Visible = false;
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread tr = new Thread(StartTask);
tr.Start();
}
private void StartTask()
{
if (!this.IsHandleCreated)
this.CreateControl();
this.Invoke(this.pa, true);
System.Threading.Thread.Sleep(15000);
this.Invoke(this.pa, false);
}
}
}
Related
My form transition is slow when I click button, I am using thread to have a form effect that form opacity starts from 0.1 and increase the number. Then I have a method and start the method from Form_Load in thread.
private void RunTimer_Tick_Things()
{
if (flag)
{
while (this.Opacity <= cs.CheckMaxOpacityValue())
{
Thread.Sleep(cs.GetTimerSleepNumberToIncreaseOcacity());
if (this.Opacity == cs.CheckMaxOpacityValue())
{
thrdTimer.Abort();
break;
}
this.Opacity += cs.GetIncreasedOpacityValue();
}
}
else
{
while (this.Opacity >= cs.CheckMinOpacityValue())
{
Thread.Sleep(cs.GetTimerSleepNumberToDecreaseOpacity());
this.Opacity -= cs.GetDecreasedOpacityValue();
}
thrdTimer.Abort();
}
}
And I have button in this form to open another form. Like this
private void button2_Click(object sender, EventArgs e)
{
Form2DatabaseSetup frm2 = new Form2DatabaseSetup();
StopThread();
this.Hide();
frm2.Show();
flag = false;
}
My problem is when I click this button the second form is opening slowly.
Consider like, you click the button then the first form hides and waiting for 1,5 second then the second form opens.
Note: The second form has thread and same functions.
Does Anyone has experienced it or know , has a knowledge about this case?
at first i thought you are increasing opacity by 0.01 so 15 * 100 =1.5 exactly what you described, but after you send the values i can see the increase is 0.06, so i think you have some problems with all the function you have ( GetTimerSleepNumberToIncreaseOcacity, GetIncreasedOpacityValue ) .
try instead of the functions to work with hard coded values and work your way up,
i use this code and it works fine:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var f = new Form2();
f.Show();
new Thread(() =>
{
while (f.Opacity < 1)
{
Thread.Sleep(15);
//if (this.Opacity == cs.CheckMaxOpacityValue())
//{
// thrdTimer.Abort();
// break;
//}
f.Invoke((Action)delegate { f.Opacity += 0.06; });
}
}).Start();
}
}
I have a bit strange problem, which I find hard to debug
Sometimes I can't close my program and it freezes when I try to close it.
I made a large program for video image recognition.
I made a special button to close the camera. This button works with this by calling a function below, and it indeed, it does work.
private void exitcamera()
{
FinalVideo.SignalToStop();
FinalVideo.WaitForStop();
FinalVideo = null;
}
Notice that the original video was started like this
private void buttonStartCamera_Click(object sender, EventArgs e)
{
FinalVideo = new VideoCaptureDevice(VideoCaptureDevices[comboBox1.SelectedIndex].MonikerString);
FinalVideo.DesiredFrameSize = new System.Drawing.Size(640, 480);
FinalVideo.DesiredFrameRate = 90;
FinalVideo.NewFrame += new NewFrameEventHandler(FinalVideo_NewFrame);
FinalVideo.ProvideSnapshots = true; //snapshots
FinalVideo.Start();
}
Now my problem seems (and this is a guess because I can't debug this moment)
That some thread is still active wanting to update the main form with data.
However it might not be able to do so since that one is closing.
I think something like that is going on so I wrote on the main application form
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// Thread.Sleep(1000); // not sure about these delays might help syncing threads
ExitCamera();
Thread.Sleep(1000);
}
However with that last code in place the program has even more trouble to exit.
I would like to send the subthreads an exit, but I dont know their names (if they have a name), I dont know how to list them or to instruct them to stop they are in another dll not my part of the code. From some dll's I dont have the code.
So are there ways of listing sub threads and then close them one by one, if one presses the uppercorner right cross to exit the application?
Well I managed to debug the program, and finally found what caused the problem.
It is a little bit strange since as a button I could stop the camera using the exitcamera function.
However, inside a _formclosing event the same routine didn't work although it worked after I had marked out the waitforstop function.
private void exitcamera()
{
FinalVideo.SignalToStop();
// FinalVideo.WaitForStop(); << marking out that one solved it
FinalVideo.NewFrame -= new NewFrameEventHandler(FinalVideo_NewFrame); // as sugested
FinalVideo = null;
}
I am still a bit confused about it, why this wont work in case a closing event. But it seems to be solved by this.
Maybe you have memory leaks problems caused by the event. You could try to unhook the event while exiting the programm :
FinalVideo.NewFrame -= new NewFrameEventHandler(FinalVideo_NewFrame);
Maybe this will help.
This helped with another problem when I wanted to show a preview, click a "grab" button, change the resolution of the camera from low res to high res, grab an image, and then change back to low res for the preview. Here is what worked, even though I had to abandon it because stopping and starting the camera reset the auto exposure so the picture was awful on the grabbed image:
using AForge.Video;
using AForge.Video.DirectShow;
public partial class Form1 : Form
{
private int PreviewRefreshDelayMS = 40;
private FilterInfoCollection VideoCaptureDevices;
private VideoCaptureDevice CustomerWebcam;
private int CustomerWebcam_CapabilitiesIndexMin;
private int CustomerWebcam_CapabilitiesIndexMax;
private bool bCustomerWebcam_capture;
private Bitmap CustomerWebcam_bitmap;
private System.DateTime CustomerWebcam_nextframetime = DateTime.Now;
public Form1()
{
InitializeComponent();
}
// Some good info to make this more robust
// http://haryoktav.wordpress.com/2009/03/21/webcam-in-c-aforgenet/
//
private void button1_Click(object sender, EventArgs e)
{
CustomerWebcam = new VideoCaptureDevice(VideoCaptureDevices[comboBox1.SelectedIndex].MonikerString);
CustomerWebcam.NewFrame += new NewFrameEventHandler(CustomerWebcam_NewFrame);
int indexMin = -1;
int MinPixels = 0;
int indexMax = -1;
int MaxPixels = 0;
for (int i = 0; i < CustomerWebcam.VideoCapabilities.Length; i++)
{
int pixels = CustomerWebcam.VideoCapabilities[i].FrameSize.Height * CustomerWebcam.VideoCapabilities[i].FrameSize.Width;
if (indexMin == -1) { indexMin = i; MinPixels = pixels; }
if (indexMax == -1) { indexMax = i; MaxPixels = pixels; }
if (pixels < MinPixels) { indexMin = i; MinPixels = pixels; }
if (pixels > MaxPixels) { indexMax = i; MaxPixels = pixels; }
}
CustomerWebcam_CapabilitiesIndexMin = indexMin;
CustomerWebcam_CapabilitiesIndexMax = indexMax;
CustomerWebcam.VideoResolution = CustomerWebcam.VideoCapabilities[indexMin];
CustomerWebcam.DisplayPropertyPage(IntPtr.Zero);
CustomerWebcam.Start();
}
void CustomerWebcam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
if (CustomerWebcam_bitmap != null)
{
CustomerWebcam_bitmap.Dispose();
CustomerWebcam_bitmap = null;
}
if (bCustomerWebcam_capture)
{
CustomerWebcam_bitmap = (Bitmap)eventArgs.Frame.Clone();
System.Random rnd = new Random();
CustomerWebcam_bitmap.Save("img" + Convert.ToString((int)(rnd.NextDouble() * 10000000)) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bCustomerWebcam_capture = false;
((Bitmap)eventArgs.Frame).Dispose();
}
else
if (DateTime.Now > CustomerWebcam_nextframetime)
{
CustomerWebcam_bitmap = (Bitmap)eventArgs.Frame.Clone();
pictureBox1.Image = CustomerWebcam_bitmap;
CustomerWebcam_nextframetime = DateTime.Now.AddMilliseconds(PreviewRefreshDelayMS);
((Bitmap)eventArgs.Frame).Dispose();
}
}
private void Form1_Load(object sender, EventArgs e)
{
VideoCaptureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
foreach (FilterInfo VideoCaptureDevice in VideoCaptureDevices)
{
comboBox1.Items.Add(VideoCaptureDevice.Name);
}
comboBox1.SelectedIndex = 0;
}
private void button2_Click(object sender, EventArgs e)
{
CustomerWebcam.SignalToStop();
CustomerWebcam = null;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!(CustomerWebcam == null))
if (CustomerWebcam.IsRunning)
{
CustomerWebcam.SignalToStop();
CustomerWebcam = null;
}
}
private void button4_Click(object sender, EventArgs e)
{
bCustomerWebcam_capture = true;
}
}
One other thing to mention ... the AForge library was the most consistent way I was able to find for using a webcam to grab a still image and save as a JPEG without delving into the world of Windows 8 metro apps. I was hoping to use OpenCV.NET, or just the regular .NET API with DirectShow or WIA, but this was the most simple and it worked for me.
And here are some good samples that were hard to find but very useful: https://github.com/mdavid/aforge.net
I was dealing with this problem. Here is a simple way to stop the camera and close Win Form.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (FinalVideo != null)
{
if (FinalVideo.IsRunning)
{
FinalVideo.SignalToStop();
FinalVideo = null;
}
}
}
In my situation WaitForStop() was needed, but the code execution was deading inside the method.
I've replaced it right after the call to SignalToStop(), with:
while (m_Device.IsRunning) { }
This is the code involved on the AForge library:
public bool IsRunning
{
get
{
if (this.thread != null)
{
if (!this.thread.Join(0))
{
return true;
}
this.Free();
}
return false;
}
}
public void WaitForStop()
{
if (this.thread != null)
{
this.thread.Join();
this.Free();
}
}
Edit: this didn't fix the hang the 100% of times. Sometimes a call to a com object (mediaControl.Stop();) on the WorkerThread() method just took forever.
Avoid direct interaction with the form - hope to have a better solution than timer but solves problem. I
Static helper class
public static Bitmap StaticBitmap = new Bitmap(100,100);
Form
public void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
lock (StaticHelper.StaticBitmap)
{
using (Bitmap b = (Bitmap)eventArgs.Frame)
{
StaticHelper.StaticBitmap = (Bitmap)b.Clone();
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
lock (StaticHelper.StaticBitmap)
{
pictureBox1.Image = (Bitmap)StaticHelper.StaticBitmap.Clone();
}
}
This will destroy your problem ( I have had this problem, i tried)
using System.Threading;
bool photo_was_taken = false;
private void buttonStartCamera_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(exitcamera));
thread.Start();
FinalVideo = new VideoCaptureDevice(VideoCaptureDevices[comboBox1.SelectedIndex].MonikerString);
FinalVideo.DesiredFrameSize = new System.Drawing.Size(640, 480);
FinalVideo.DesiredFrameRate = 90;
FinalVideo.NewFrame += new NewFrameEventHandler(FinalVideo_NewFrame);
FinalVideo.ProvideSnapshots = true; //snapshots
FinalVideo.Start();
}
private void FinalVideo_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
// what you want to do ( your code is here )
photo_was_taken = true;
}
private void exitcamera()
{
while (!photo_was_taken)
{
Thread.Sleep(5); // you can change wait milliseconds
}
FinalVideo.SignalToStop();
FinalVideo.NewFrame -= new NewFrameEventHandler(FinalVideo_NewFrame);
//FinalVideo.WaitForStop();
while (FinalVideo.IsRunning)
{
FinalVideo.Stop();
// FinalVideo = null; >> // that is not condition
}
}
This is what you need, 100% working solutions:
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
Invoke((MethodInvoker) delegate
{
_videoSource.SignalToStop();
_videoSource.WaitForStop();
});
}
Please, let me add my working solution on closing a webcam with the wonderful library AForge.NET. It´s a pitty it development has been abandoned.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (videoCaptureSource != null)
{
while (videoCaptureSource.IsRunning) // Perhaps you need to limit the number of iterations
{
videoCaptureSource.SignalToStop();
videoCaptureSource.WaitForStop();
if (videoSourcePlayer != null)
{
videoSourcePlayer.SignalToStop();
videoSourcePlayer.WaitForStop();
//videoSourcePlayer.NewFrame -= new NewFrameEventHandler(videoSourcePlayer_NewFrame); // Uncomment this line if you have added an event handler
}
Thread.Sleep(3000);
}
videoCaptureSource = null;
videoDevices = null;
videoSourcePlayer = null;
}
}
When I only needed the frame attaching an event handler to the VideoCaptureDevice (not the VideoSourcePlayer) I couldn´t stop the video from the VideoCaptureDevice, instead I used an invisible VideoSourcePlayer and stopped it from there.
i tried some solution, but nothing work.
i partial solved adding a thread sleep after WaitForStop
if (FinalVideo != null)
{
if (FinalVideo.IsRunning)
{
FinalVideo.SignalToStop();
Thread.Sleep(1000);
}
}
if i try to call Stop application will be freeze
I am creating some files from xml data in the background using
Task.Factory.StartNew(() => xmlconvert(xx, yy));
Now, the question is how to show the progress of this method using a StatusStrip control with some message and the progress or at least just a scrolling animation for the progress. I don't just have any idea how would it work.
Update:
First of all, this method 'xmlconvert(xx, yy)' has four different forms depends on the condition user selects at runtime.
In the main form of my application user can select from different conditions to process on the data. Then finally when user click on the Button 'Create' all these conditions are being checked and a suitable method will be called within that button click event. I need to show the progress of this method which is being invoked at runtime.
private void btnCreateRelease_Click(object sender, EventArgs e)
{
// Checks set of conditions
if(cond 1)
{
xmlconvert_1();
}
else if (cond2)
{
xmlconvert_2();
}
else if (cond3)
{
xmlconvert_3();
}
else if (cond4)
{
xmlconvert_4();
}
}
I want to show progress of one of these methods which will be invoked at runtime depends on the condition.
Thanks a lot.
You can use the BackgroundWorker for this, and it's pretty simple, too. Here's a sample to get you going:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Do your work in here.
xmlconvert(xx, yy);
for (int i = 0; i <= 100; i++)
{
backgroundWorker1.ReportProgress(i);
System.Threading.Thread.Sleep(100);
}
}
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
}
And here's the link to the documentation.
To get it to work in your scenario, I would suggest you add a Progress bar to your StatusStrip control and update it from within the backgroundWorker1_ProgressChanged event.
If you wish just to show, that your app is not hang may help following approach:
public static class ActionExtensions
{
public static void RunWithMargueProgress(this Action action)
{
var progressForm = new ProgressForm();
progressForm.Show();
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressForm.Close();
progressForm.Dispose();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
Where ProgressForm would be a simple form with ProgressBar, that is set to Marquee style. If you have idea, how it is progressing, it is better to show progress for user and use BackgroundWorker.
As long as it's parameter is Action, it is easily reusable.
Usage would be:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress();
}
If you have control in status strip, that you wish to animate, you can do it like this:
public static void RunWithMargueProgress(this Action action, ToolStripProgressBar progressBar)
{
progressBar.Style = ProgressBarStyle.Marquee;
progressBar.MarqueeAnimationSpeed = 30;
Task.Factory.StartNew(action)
.ContinueWith(t =>
{
progressBar.MarqueeAnimationSpeed = 0;
progressBar.Style = ProgressBarStyle.Continuous;
}, TaskScheduler.FromCurrentSynchronizationContext());
}
Usage would be pretty much the same:
private void button_Click(object sender, EventArgs e)
{
Action action = () => Thread.Sleep(5000);
action.RunWithMargueProgress(ToolStripProgressBar);
}
I was wondering if there was a way of adding a sort of animation to text displayed on a form.
What I had in mind when I thought of this was kind of similar to what you can do with text in PowerPoint (i.e. a typewriter-like animation where the text is typed one at a time, have the whole textbox appear with a certain effect etc), I'm just looking to find out what you can do using Windows Forms.
Currently I'm using a textbox to display information on my form application, though in hindsight I realise labels would have worked just as well.
EDIT: Turns out I was using labels after all, I just gave it a name with 'textbox' inside for lack of a better description.
public partial class Form1 : Form
{
int _charIndex = 0;
string _text = "Hello World!!";
public Form1()
{
InitializeComponent();
}
private void button_TypewriteText_Click(object sender, EventArgs e)
{
_charIndex = 0;
label1.Text = string.Empty;
Thread t = new Thread(new ThreadStart(this.TypewriteText));
t.Start();
}
private void TypewriteText()
{
while (_charIndex < _text.Length)
{
Thread.Sleep(500);
label1.Invoke(new Action(() =>
{
label1.Text += _text[_charIndex];
}));
_charIndex++;
}
}
}
Now, I personally wouldn't do this because gratuitous animations tend to annoy users. I'd only use animation sparingly - when it really makes sense.
That said, you can certainly do something like:
string stuff = "This is some text that looks like it is being typed.";
int pos = 0;
Timer t;
public Form1()
{
InitializeComponent();
t = new Timer();
t.Interval = 500;
t.Tick += new EventHandler(t_Tick);
}
void t_Tick(object sender, EventArgs e)
{
if (pos < stuff.Length)
{
textBox1.AppendText(stuff.Substring(pos, 1));
++pos;
}
else
{
t.Stop();
}
}
private void button1_Click(object sender, EventArgs e)
{
pos = 0;
textBox1.Clear();
t.Start();
}
or something like that. It'll tick off ever half second and add another character to the multi-line text box. Just an example of what someone could do.
In my application I have a mainform. When the open button is clicked I want to show a second (borderless) form whith the text loading. I've got this working so far.
But what I want is that the loading form is centered relative to the mainform. How do I do this?
SOLUTION:
private void tsbOpen_Click(object sender, EventArgs e)
{
if (_fileDialog.ShowOpenDialog() == DialogResult.OK)
{
_progress = new frmProgress(); // _progress is a member var
backgroundWorker1.RunWorkerAsync("open");
_progress.ShowDialog(this);
}
}
You can set StartPosition to CenterParent and pass the mainform as an Owner.
I created a subform named ProcessingRequest and I put some text and an animated gif on it.
I have a Property in my main form that calculates the location my sub form should be in.
private Point ProcessingLocation { get { return new Point(this.Location.X + this.Width / 2 - new ProcessingRequest().Width / 2, this.Location.Y + this.Height / 2 - new ProcessingRequest().Height / 2); } }
I have a class that makes a new thread to show the sub form.
public class ShowProgress
{
static private System.Drawing.Point point;
static private ProcessingRequest p;
static public void ShowProgressForm(System.Drawing.Point myPoint)
{
point = myPoint;
Thread t = new Thread(new ThreadStart(ShowProgress.ShowForm));
t.IsBackground = true;
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
static private void ShowForm()
{
p = new ProcessingRequest();
p.StartPosition = FormStartPosition.Manual;
p.Location = point;
p.TopMost = true;
Application.Run(p);
}
static public void CloseForm()
{
p.Invoke(new CloseDelegate(ShowProgress.CloseFormInternal));
}
static private void CloseFormInternal()
{
p.Close();
}
}
public delegate void CloseDelegate();
Then in my main form I simply put
ShowProgress.ShowProgressForm(ProcessingLocation);
//heavy processing code goes here or whatever
ShowProgress.CloseForm();
:)
Martijn try this
at the start of the method put some code like this
public sub Bah()
{
if (me.InvokeRequired)
{
me.Invoke(new action(Bah));
return
}
myform.showdialog...
}
dont know if this code compiles to 100% but you get the idea
Get the position of the main form coordinates and its size and take the size of child form and put some simple mathematics on it.