Something about moving shapes around in Canvas in WPF - c#

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

Related

BackColor property not changing properly in code. Any ideas as to what I'm doing wrong?

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

Label.Image gradually increase with for loop

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.

Why is my form blinking

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

How to animate an image in C#

I'm trying to animate an image in C#. Basically, I want to take an image and connect it to another function and based on a scale of 1 to 10, make the image move up or down. By default, the image will start out at 1. I have searched and I've found ways to make shapes move up and down on the screen, but not an actual image. The image is small, say 60x60 pixels. I feel like this should be simple, but I have yet to figure it out. I was thinking of just having an image placed on the Windows form and then basically have it move up or down the y-axis of the form, but I would like it to move smoothly.
Ok, I was able to hook a button up to a timer function and get the button to move smoothly up and down the screen. The button has to keep moving for the duration of the program running. However, I'm having a hard time writing a function that stops the timer and the image(button) from moving once the image reaches a certain location. Without that, the timer continues and the image(button) moves off the screen. I've tried messing with button.Location.Y functions, but I have yet to get it work right. Can anyone please advise? Thanks. Oh yea, once the image(button) reaches Y location of 192 or 447, it should stop moving.
An example of what I have:
private void timer2_Tick(object sender, EventArgs e)
{
button2.Top = button2.Top + 1;
if (button2.Location.Y == button2.Location.Y - 192)
{
timer2.Stop();
timer3.Stop();
}
//if (timer_limit < 100)
//{
// button2.Top = button2.Top + 1;
// timer_limit++;
//}
//else
//{
// timer2.Stop();
//}
}
Several ways to do this. You could just use a PictureBox and change its Location property. Or you could draw the image in the form's OnPaint() override and change the argument to e.Graphics.DrawImage(). You'll then have to call Invalidate() to force the OnPaint method to run. It is the cheapest way.

Suggested (simple) approach for drawing large numbers of visual elements in WPF?

I'm writing an interface that features a large (~50000px width) "canvas"-type area that is used to display a lot of data in a fairly novel way. This involves lots of lines, rectangles, and text. The user can scroll around to explore the entire canvas.
At the moment I'm just using a standard Canvas panel with various Shapes placed on it. This is nice and easy to do: construct a shape, assign some coordinates, and attach it to the Canvas. Unfortunately, it's pretty slow (to construct the children, not to do the actual rendering).
I've looked into some alternatives, it's a bit intimidating. I don't need anything fancy - just the ability to efficiently construct and place objects in a coordinate plane. If all I get are lines, colored rectangles, and text, I'll be happy.
Do I need Geometry instances inside of Geometry Groups inside of GeometryDrawings inside of some Panel container?
Note: I'd like to include text and graphics (i.e. colored rectangles) in the same space, if possible.
Shapes are fairly heavy-weight. You should probably look into using graphics paths. Those are much more efficient when the user doesn't need to interact with individual parts of the drawing - and sometimes even then.
Try not to creating shapes that you do not need, and recycle ones that you already have. Basically no user will see the whole screen, so do NOT have the shapes that are out of sight. Don't create new ones f you can avoid - basically keep shapes falling out in a "ready" list, so you can reuse them.
If you have a large number of Shape instances, you could perhaps construct them asynchronously (on a worker thread) and queue up the actual Add operations via the Dispatcher. The idea here is that the UI won't be complete right away, but the user can start interacting right away, while elements continue loading.
EDIT: The above is incorrect. WPF does require that Visual elements be created on the UI thread. You can still accomplish this sort of 'lazy' visual loading using a pattern like this:
private Random _random = new Random();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Thread testThread = new Thread(TestThread);
testThread.Start();
}
private void TestThread()
{
for (int i = 0; i < 1000; i++)
{
Dispatcher.BeginInvoke((Action)CreateShape);
}
}
private void CreateShape()
{
var shape = new Rectangle();
shape.Width = _random.Next(10, 50);
shape.Height = _random.Next(10, 50);
shape.Fill = new SolidColorBrush(Colors.Red);
Canvas.SetLeft(shape, _random.Next(0, 400));
Canvas.SetTop(shape, _random.Next(0, 200));
LayoutRoot.Children.Add(shape);
}
This basically queues up tasks to be run 'asynchronously' on the UI thread (i.e. whenever the message pump is being serviced), so you can maintain responsiveness while performing the 'long' UI update.

Categories

Resources