I want to run a certain method every x milliseconds when a form is visible (so as to change the contents of a picturebox within that form, so that an animation is displayed, to be precise) without obviously blocking events. I cannot load a .gif since I will be using spritesheets and atlas files. I believe this approach steps outside of the event-driven programming language paradigm that comes with .net/c#. I am not sure of the correct way to face this problem - maybe using a thread, but that sounds a bit overkill - any suggestions?
You can use a timer which will fire an event at regular intervals. As to only doing that while the form is visible, you can disable the timer when the form is minimized or hidden, and re-enable it when it's shown again.
Related
I am new to Winforms development and I do not see a solution yet on Stackoverflow, but may have missed it.
I have a dialog box that comes up, but due to application startup processing, it is only half displayed for the first 2 seconds or so (i.e. shows border and the background except where controls will be shown). The control locations are white until controls are displayed after that initial 2 seconds.
I understand I could put a delay in the application while it is starting up, but would prefer something like a Suspend() / Resume() pair in strategic locations. I have tried putting in the load event, but that had no effect. Also, it looks like Refresh() breaks the Suspend/Resume. Ideas appreciated since I would like to use this strategy elsewhere in the application as well. I am wondering what is an approach that will work for this and other areas that flicker badly (or outright show a long delay before fully displaying like this startup dialog box as described).
Try putting your long-running code in the Load event handler instead. By putting it in the Shown event handler, it causes the form to freeze until it's done loading because the shown event handler is not letting other events in the message loop, e.g. the Paint event -- get processed. At least if you put it in the Load event, all the long running code will occur before anything gets displayed at all.
If you don't like having any delay, consider putting the long running code in a timer that kicks off in the Shown event.
Then there's always the BackgroundWorker if you want to get more advanced with long-running code.
I am having a very difficult time trying to debug/fix an application.
Briefly:
- I created a "wizard" type app that starts with the user taking a photograph (using the common dialog for photos)
If the user tries to use the text input window (SIP) (the little keyboard input window) after a photo is taken the event loop seems to hang - the event is not processed or is delayed for a while.
If the user does not take a picture the SIP keyboard works great.
This only happens on some of my devices. Specifically it is not a problem on an MC65 but is a problem on an ES400.
It appears that the app's event loop gets screwed up with the way I am displaying forms and taking photos.
If created a simple test app with single form containing a button (Event handler takes a photo) and a text box that accepts input. That works fine. But it is only a single form app that does nothing else.
When I combine the photo taking with my form displaying (making a "wizard" ) things go badly.
I wonder what kind of event loop should I be running?
Essentially the user takes a photo then goes through some forms (I hide one form and show another when they click the "next" button.)
The Form.Show is called from the main form after a picture is taken and then I have something like:
while(UserNotFinished)
{
Application.DoEvents()
}
Where UserNotFinished is a flag set from my wizard/forms after the "submit" button is pressed.
I will be happy to provide more code but not sure what would be useful.
I am new to C# and CF development (lots of years of C++/Win32)
The real confusing part is that this works on one device but not on another. In fact, the device hangs completely. It ends the activesync connection and sometimes I have to hard reset by removing the battery.
I think your problem stems from the while(true) { DoEvents(); } and perhaps how you are trying to go between forms. The only time I've used the DoEvents() method is when I'm already in the scope of a windows event and I need to be sure something in the message queue is processed so screen updates are correct. I'd suggest making a controller class to manage the screen flow for your wizard. You can control the screen flow by either using ShowDialog() and execute the flow control directly in the scope of a single call, or you'll have to use Show() and an asynchronous mechanism such as subscribing to and handling specific form and control events in the controller class. Also saw the comment about introducing another thread, beware that Forms belong to the thread they were created in and you must Invoke(...) all Form members in the context of the creating thread.
Hmm. Very strange
I started a new thread and basically call Application.DoEvents() in in as well and it seems to fix the problem...
I don't know why the
while(true)
{
DoEvents()
}
in the main thread doesn't work.
I am new to C# but I have been programming with VB6 for a long time. I have a very simple project that runs without any errors. There is one problem however and it has to do with the Windows itself. If I open up any Window (from any other program) and that window overlaps my application...the moment I close that "other window", my application's Window does not repaint the portion that was overlapped by the "other window".
The same thing happens whenever the Bubbles screensaver comes on for Windows Vista. When I jiggle the mouse to cancel the screen saver, guess what??? I have bubbles scattered all over my Window. In VB, we had a simple "Window.Refresh" and voila! In C# however, this does not work. I have scoured the forum(s) and there seems to be a mention of "refreshing a thread". I'm kind of confused at this point. Why can't things be simple??
You can call Form.Invalidate to invalidate the client area of the form. That will cause it to receive a paint message, and redraw itself.
This is similar to the VB6 Refresh method.
However, this should happen automatically. If your application is doing some processing in the UI thread, however, it will prevent it from processing its messages until the work is completed. If this is the case, you should consider using BackgroundWorker (or some other method) to push the work onto a background thread.
There is nothing special you should need to do to redraw the Form in C# (Form should redraw fine when you bring the window back to the top).
Do you have any third party controls in play? Control.Refresh() does exist in C#, should you should be able to call it from the Form itself (usually this.Refresh() in your Form's codebehind), or any Control which has children (like Panel).
I have a strange issue that I'm not too sure on how to fix or address. I'm writing a mini text editor style application - RichTextBox editor.
I need to do some complex parsing after the selection changes - updating position, selection text and various other bits about the context of the text around the area.
As it takes a bit of processing I don't want it to fire each time the selection changes if the user is scrolling with their arrow keys. I thought of using the Application.Idle, but it fires too regularly. I tried a timer, but it may fire while the selection arrows are still moving.
What I was thinking of was a countdown timer sort of utility that will reset the timer each time the RichTextBox SelectionChanged event fires, then when the timer hits 500 ms or 1000 ms it will execute the complex processing runs.
Does this sound like a good idea?
You should probably start your processing in its own thread when it takes too long. As soon as you get new inputs you can stop the previous calculation and start with the new information again (so consider a cancel mechanism for your thread).
When your thread is done you have to check if its results are valid (the selecion did not change in the meantime). Finally, you can "synchronize" the results of the calculation to the GUI, which is hopefully quick enough :)
This does only work, when there is a certain amount of calculation that can be done without writing to the GUI ... I am not sure if you can implement it this way. It depends on the type of your calculations.
I have an animated gif placed on the button. It's animating ok (most of the times :P) but when the windows is redrawn (repainted) the animation stops. I have tried to refresh the button (button.Refresh() ) while handling Paint event but it didn't solve the issue. \
Anyone knows how to fix this?
Perhaps I am mistaken, but I think the issue is that it stops animating not when the form is redrawn, but when the animated object is obscured by another window. This is intended behavior; the bug is that in Windows Vista and Windows 7, the display is composited, so even though the window was 'obscured' it was never truly obscured, and it will never receive paint messages when un-obscured which will kick the animation back in.
This bug appears to affect any ButtonBase-derived control with an animated object.
The issue is the Control.IsWindowObscured function. It will return true. You can see in the ButtonBase.cs file, at System.Windows.Forms.ButtonBase.OnFrameChanged, there is a line of code at the very end that says:
if (IsWindowObscured) {
StopAnimate();
return;
}
and therein lies the problem.
FYI, OnFrameChanged is called from the ImageAnimator thread. This is the callback that is specified in ImageAnimator.Animate(image, eventhandler). ButtonBase sets this up in the private void Animate(bool animate) function. The ImageAnimator thread polls every 50ms and checks to see if a new frame is necessary for any of the images it is monitoring; if so, it sets a flag to have the control invalidated and the new frame drawn.
Since this is inaccessible to us, I don't think there is much we can do about it. As a workaround, I implemented a timer in my form that invalidates the control every 500ms, so it will force it to restart if it had previously stopped. It is quite annoying that we can't override or even access it. I'm afraid the only solution is the hack above, or to create or use a control created by yourself or a third-party.
To clarify -- this is only a problem on Windows Vista or Windows 7 using desktop composition. The issue is that windows are never truly obscured, like they are when not using desktop composition. They are always buffered by the window manager. (There are special layered windows in windows 2000+, but ignore that for now). Previously, the parts of a window were not available if they were not on the screen or obscured by another window. When they came back into view, by changing focus or position etc, then the system will notify that area to repaint itself. However when using desktop composition, the repainting is never required, since the actual contents of the window are buffered elsewhere. This is why the window previews work in the taskbar, and Flip-3d, for example. The side effect is that code that expects to get a paint message when it is visible once again after being obscured will fail. The ButtonBase code expects to recieve a paint message once it comes back into view, which will start the animation again. And therefore, this optimization became a bug.
The issue should be reported in Microsoft Connect, though it is unlikely to be resolved.