Can't figure out why image in label1 connected with imageList1 doesn't want to change more than once after pressing of a mouse button. Imagelist consists of 7 images which I want to have gradually appear in label element...that was the whole idea.
private void button1_Click(object sender, EventArgs e)
{
int number = 0;
for (int i = 0; i < imageList1.Images.Count; i++)
{
label1.Image = imageList1.Images[number++];
}
}
The default ImageIndex in label1 properties is set to 0 (first image) and after the for loop it gets to index1.
I am guessing that the last image stays? If you want the images to appear one by one with a timeout you should do something like
private async void button1_Click(object sender, EventArgs e)
{
foreach (Image image in imageList1)
{
await Task.Delay(1000); //wait for one second before changing
label1.Image = image;
}
}
Of course depending on your requirements you may want to disable the button and as pointed out by #nvoigt you may want to use some animation capabilities of the UI framework.
Your form will only repaint once the whole button event ran. That means you will only ever see the last image. Look into background workers or maybe timers to have animation. Maybe WPF is the way to go if animation is the main purpose of your program.
Your loop does assign the images OK but there is no time to show them because updating the UI is not happening before the loop is through.
You could force the UI update by inserting an Application.DoEvents()
label1.Image = imageList1.Images[number++];
Application.DoEvents();
You can try it but you should not actually use this as your solution! It has two serious issues, none of which you want:
It gives you no control over the animation speed.
Application.DoEvents can introduce serious problems in your code and you should not get into the habit of using it at all. Look it up or just believe it!
The best way to do any animation in Winforms is to use a Timer. In the Button click you set it up and start it. In its Tick you do the animation..
Have alook at this post for a button animation example! Instead of Mouse_Enter use your button click. Stop the Timer when the images have all been shown!
If all you want to do is playing around a little getting used to Timers is highly recommended and there is no need at all for WPF. If you will need a lot of high class animation WPF is indeed the way to go.
Here is the code to a minimal solution:
Timer timer1 = new Timer();
int imageIndex = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (imageIndex >= imageList1.Images.Count ) timer1.Stop();
label1.Image = imageList1.Images[imageIndex++];
}
private void button1_Click(object sender, EventArgs e)
{
imageIndex = 0;
timer1.Interval = 100; // change ms to suit your needs!
timer1.Start();
}
There are various issues with your code.
By incrementing number inside the loop, you force it to have the same value as i. If that's what you want to do, why not simply use i?
You never let the UI thread update the display. The UI thread will update the display only after the event handler finishes.
The result is that only the last image will be displayed.
To allow the UI thread to update the display, you need to use Application.DoEvents, eg:
foreach(Image image in imageList1.Images)
{
label1.Image = image;
Application.DoEvents();
}
Of course this will just go through all the images at once, so you'll just see a blur.
If you want a simple, smooth animation, use an [animated GIF2 or use WPF. Trying to do this in Windows Forms with individual images is not straightforward.
If you want to show an animation, you can put a delay as #Stilgar suggests, although this won't guarantee a smooth animation. Thread switching or high CPU load means that the delay between images will always be greater than the delay amount. The result will be a jerky animation.
You can use a timer event to update the image. This is better, but high CPU load can still delay processing of the event. Only WPF can guarantee the animation will be smooth without complex coding.
Related
I've recently been developing an RTF Editor which is only a simple UserControl that has a RichTextBox with a couple Events like PreviewTextInput and PreviewMouseUp.
I noticed something slightly annoying though.
The Performance of the RichTextBox is absolutely terrible whenever the UI is being resized and the RichTextBox has a lot of Text to cause its Wrapping Algorithm to fire.
This gives the Application a really sloppy feel, as if it is poorly optimized (even though it isn't).
At first I noticed this performance hit while selecting Text, so instead of using the SelectionChanged event, I decided to use the PreviewMouseUp event and then fetch the Selection.
Then after further testing I found out that the resize also caused huge loads.
And I'm talking about loads ranging between 5% -> 30% with a Quad-Core CPU running at 3.8GHz!
To further test it out, I decided to comment out my RichTextBox and only include a new RichTextBox with no defined Property
<RichTextBox/>
Inserting this into a Window, filling with Text, and then resizing the Window to cause the Wrapping Algorithm did the same again, up to 30% usage!
I tried to research about this matter, and most people ended up recommending setting the PageWidth to high values in order to prevent Wrapping:
richTextBox1.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
richTextBox1.Document.PageWidth = 1000;
Which I do not want, since the previous Version of the Editor I wrote was made with WinForms and could do Wrapping effortlessly, and I also want it in the new WPF Version.
Did anyone else ever face this issue?
If yes, could you please point me into the right direction to remove this huge strain on the hardware?
I'm a bit sad because I love WPF, but I did find one or the other Object that is really unoptimized and/or not practical in comparison to the WinForms counterpart, the RichTextBox seems to be another one of those cases :(
Sorry for the huge amount of Text, but I really wanted to Document this neatly in case some other poor soul faces this issue and for you guys to see what I've tried so far.
One way to overcome this issue might be to switch to "no wrap" mode when window is being resized, but when user finished with resizing - switch back to normal mode. Then wrapping algorithm will be executed just once at the end and users should still have smooth feeling about your application. Sample code:
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
this.SizeChanged += OnSizeChanged;
}
private Timer _timer;
private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
// user started resizing - set large page width
textBox.Document.PageWidth = 1000;
// if we already setup timer - stop it and start all over
if (_timer != null) {
_timer.Dispose();
_timer = null;
}
_timer = new Timer(_ => {
// this code will run 100ms after user _stopped_ resizing
Dispatcher.Invoke(() =>
{
// reset page width back to allow wrapping algorithm to execute
textBox.Document.PageWidth = double.NaN;
});
}, null, 100, Timeout.Infinite);
}
}
Intermediate C# dev here. Trying to transition into game programming by writing a simple Simon clone (private learning only, I do not own any copyrights or intend to distribute/sell) and I'm stuck.
Here's a link to the full code thus far: Simon
The problem lies in the PlayTile() method below:
private void PlayTile(Button btnColorOfTile, Color lightedTileColor, SoundPlayer tileSoundPlayer, Color originalTileColor)
{
// ***BUG03***
TilePress(btnColorOfTile, lightedTileColor, tileSoundPlayer);
// Small pause so tile can be lit before instantly changing back
Thread.Sleep(SLEEP_TIME);
TileRelease(btnColorOfTile, originalTileColor, tileSoundPlayer);
// Small pause between each tile play so that you can distingish multiple plays of the same tile.
Thread.Sleep(SLEEP_TIME);
}
This is supposed to "light" up the tile by changing the BackColor property, pause for a half second (SLEEP_TIMEis set to 500ms) while playing the tile's sound, and change the BackColor back to the normal color.
The sound plays properly with the pause and everything, but the tiles are not changing color. I change the BackColor property in TilePress() and change it back in TileRelease() which is called in the MouseUp and MouseDown for the tile's event handlers and it works just fine.
Any ideas why PlayTile() is working for sound but not changing the BackColor property?
Also, if you see any glaring mistakes in the code, please let me know. This is all about learning for me so constructive criticism is desired.
Thanks in advance for any help!
Thread.Sleep blocks your UI thread(this means your form can not process any window messages like keyboard, mouse, move, paint etc. It is a "Not Responding" state and not a desired behavior).
You can use async/await instead.
Example:
async Task PlayTile()
{
this.BackColor = Color.Red;
await Task.Delay(500);
this.BackColor = Color.White;
await Task.Delay(500);
}
async private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
await PlayTile();
}
}
I've implemented a simple multithreaded Server\Client game as an assignment for my college.
on the client side in addition to the main thread there are:
1-thread which is responsible of drawing the play ground and the players on the form.
2-thread to communicate with the server to send the directions and receive the location and other information.
first I used the invoke technique but it didn't work because I was using the Graphics after it disposed. see Draw on a form by a separate thread
so In order to avoid that and regularize the drawing thread, I just raising the flag 'Invalidate' every specific time on the drawing thread and leave the actual handling of it to the main thread:
public Form1()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
InitializeComponent();
}
private void Draw()
{
while (true)
{
Thread.Sleep(200);
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (map.hasGraph)
{
map.Draw(e.Graphics);
if (this.Index != null)
e.Graphics.FillRectangle(Brush, Rectangle);
if (OpponentIndex != null)
e.Graphics.FillRectangle(OpponentBrush, OpponentRectangle);
}
}
the problem here that the form is blinking in arbitrary fashion even though I'm using double buffering, and the blinking is reduced when I increase the sleeping time for the drawing thread, but I think 200ms is already too much.
any advice?
[Edit]
I realized that I'm setting the double buffering flag from the code and from the property editor which made the problem (this may be a fool idea) but I spent half an hour testing my code with one of the flags and with both of them, the problem raised when I set the double buffering flag from two places, my problem is solved but please now I need to know if this could be what solved it.
It must get worse and worse the longer it runs right?
Everytime your program paints it launches draw, which has an infinite loop, which calls paint, which calls draw in another infinite loop. IT seems you have a circular reference here. If I can assume Map.Draw is private void Draw()
There is a far easier solution to this, draw everything to a bitmap then draw the bitpmap in the onPaint event.
Bitmap buffer=new Bitmap(this.Width, this.Height); //make sure to resize the bitmap during the Form.Onresize event
Form1()
{
InitializeComponent();
Timer timer=new Timer();
timer.Interval= 100;
timer.Tick+=......
timer.Start()
}
//the Timer tick event
private void timer_tick(....
{
if (map.hasGraph)
{
using (Graphics g = Graphics.FromImage(buffer))
{
//You might need to clear the Bitmap first, and apply a backfround color or image
//change color to whatever you want, or don't use this line at all, I don't know
g.Clear(Color.AliceBlue);
if (this.Index != null)
g.FillRectangle(Brush, Rectangle);
if (OpponentIndex != null)
g.FillRectangle(OpponentBrush, OpponentRectangle);
}
panel1.BackgroundImage=buffer;
}
}
Note I did not test this for syntax accuracy.
The memory of the system might be quite low for the operation executed.
read more about it.
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx
Create an image which will be used to store last rendered scene.
Create new thread whith will draw to the image
Create a timer whitch will refresh image
Copy image to form on timer tick
I have a rectangle which want to move using Canvas.SetLeft(rect, x);
But I wanna make it look like a smooth transition (animation). Here's the code snippet that's supposed to do this:
void animate()
{
for (int a = 0; a < 10; a++)
{
MainWindow.current.Dispatcher.BeginInvoke(new Action(move));
x += 10;
Thread.Sleep(100);
}
}
void move()
{
Canvas.SetLeft(rect, x);
}
It seems very basic but I'm having trouble doing it. I want the thread to sleep for a little time, & then set the rectangle's x to a new value. But instead, the thread sleeps for 30*10 milli seconds, then the rectangle moves right instantly by 10*10 units. I'm not able to get the animation effect I want. And I'm aware that I've called Sleep on the GUI thread but I don't think that should affect the animation.
Instead of forcing your UI thread to sleep. I would suggest you to use the Storyboard Animation provided by WPF specially for this purpose. This link will get you started if its's something new too you -
http://vbcity.com/blogs/xtab/archive/2009/12/28/wpf-simple-animations-to-move-and-resize-elements-simultaneously.aspx
This is not going to be a success. For one thing you have a threading issue, Sleep() on the UI thread blocks screen updates. Using another thread will give poor results at best.
And you don't need this, just learn how to define animations (<Storyboard />) in XAML. You can trigger them from C#.
Edit
Try and watch this little snippet:
private void button1_Click(object sender, RoutedEventArgs e)
{
label1.Content = "Click";
Thread.Sleep(2500);
}
I have a Windows form application written in C#. I update the title of the form frequently, but there's a substantial lag between the title changing and the title dislayed in the taskbar being updated.
What's a clean way to force an update / redraw of the task bar's entry for my program? Failing that, how can I force a redraw of the entire task bar?
Elaboration: It turns out that the delay in updating the taskbar is fixed at about 100ms, however this seems to be a delay based on when the Form.Text was last modified. If you modify the text faster then that - say, every 10ms, the taskbar is not updated until the Form.Text has been left unchanged for at least ~100ms.
OS: Vista 32.
Did you try to call Form.Refresh() after updating the title?
Edit:
If you are doing the title updates in a loop you might have to do something along the line of:
this.Invalidate();
this.Update();
Application.DoEvents();
A task bar update more than every 100ms is going to be too fast for the user to resolve anyway. Presumably you're showing some sort of progress or status indicator to the user?
If so, you're crippling the app needlessly doing so many UI updates. That processing time is better used getting the customer's job done.
I think you need to revisit the UI design aspects of what you're trying to do.
I just did a simple test. The changes are quite instantaneous. From the look of it, it's definitely less than 500ms. If you need to update the title at a higher rate, I won't really recommend it. Generally I've seen the fastest update rate of twice per second.
EDIT:
I tested using keypress event. When I hold down the key for a fast repeat, it won't update until I've release my key. Thus, same scenario as your setup.
Btw, why do you need to update every 10ms? Just keep in mind that Thread.Sleep(timeout) with timeout of less than 50ms is not accurate. Also, 10ms timeout will equal to 100Hz, unless you're using high end display, you'll have miss a few frame. Most general LCD have a refresh rate of 60Hz. And our eye can't differentiate anything faster than 25Hz. Thus 40ms delay is more than enough, if you want to animate. Generally I would recommend 15Hz (67ms) for simple animation. If just want to scroll some text, 2Hz is more than enough. Anything faster will make the user dizzy.
Are you using code similar to this in your form?:
private void Form1_Load(object sender, EventArgs e)
{
Timer t = new Timer();
t.Interval = 10;
t.Tick += new EventHandler(t_Tick);
t.Start();
}
int aa = 0;
void t_Tick(object sender, EventArgs e)
{
this.Text = aa++.ToString();
}
Works fine for me - no lag between form and taskbar at all.
Are you sure you aren't locking up the GUI thread and not calling Application.DoEvents on your loop?
I'm using the new Windows 7 beta, so it's a small chance that it's different behavior.