Pausing between operations - silverlight

I am trying to build a simple memory game. Clicking on a card that has not been "flipped" simply "flips" the card over (revealing the underside of the image).
When a card is already showing and a second card is flipped over, I would like to pause for one second. Then, if the card that was flipped over matches the first card, I remove it from the board, if it does not match, I would like to flip both cards back to their hidden stage.
I have the "flipping" coded, I just want to know how I can pause it for one second after the second card is flipped.
I've tried:
System.Threading.Thread.Sleep(1000)
and
Dispatcher.BeginInvoke(() => System.Threading.Thread.Sleep(1000));
But it doesn't work like I want. This is my first WP7 and Silverlight project, so not sure what I'm doing wrong.
Any advise would be greatly appreaciated!

use the DispatcherTimer class:
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0,0,0,1);
timer.Tick += SomeTickMethod;
timer.Start();
private void SomeTickMethod(obejct sender, EventArgs e) {
FlipBackCards();
//remember to stop it :)
((DispatcherTimer)sender).Stop();
}
then in your Tick method you flip over the card. You could make the timer a class member in which case (based on some of your own logic) you could Stop() the timer from firing at any time.
Hope that helps
(note I quickly just typed up this code, may not be 100%, should be close)

Related

Changing MediaElement source without Flicker

I have a simple video player that plays a series of videos using the WPF MediaElement. The videos together form one continuous film that move around a still image. At the end of each video the movement freezes on the final frame of the currently playing video. When I press a button the next video plays, which continues the movement around the still image. It's an application I'm going to use to give a speech. Effectively I've got a series of videos for which the last frame of each video is the same as the first frame of the next video.
I'm using a WPF MediaElement and changing the Source property when the user clicks on the mouse.
The problem that I have is that, when I change the Source property, the MediaElement becomes transparent while the next video is loaded. This means there is a flicker between videos. Is there any way of preventing this flicker? What other strategies could I use?
Here's some code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.x_MediaElement.MouseLeftButtonDown += x_MediaElement_MouseLeftButtonDown;
this.MouseLeftButtonDown += MainWindow_MouseLeftButtonDown;
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
}
void MainWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
}
private void MoveNext()
{
_sourceIndex++;
if (_sourceIndex >= _sources.Length)
_sourceIndex = 0;
Debug.WriteLine(string.Format("Playing {0}", _sources[_sourceIndex]));
this.x_MediaElement.Source = new Uri(_sources[_sourceIndex]);
this.x_MediaElement.Play();
}
private int _sourceIndex = -1;
private string[] _sources = new string[] {
//SOURCE GO HERE
};
void x_MediaElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
MoveNext();
e.Handled = true;
}
}
I am gonna be honest with you. MediaElement has more bugs than you can count with fingers. Starting from that the mediaElement blows up after playing 20 videos(no more MediaEnded event, it will crash or something like that). And ofcourse the performance. Its not synchronized with vertical sync. So the video might actually seem laggy.
I advise you to look into DirectShow technology(essentially what WPF is based on, but you can switch renderer which will avoid lag). COnsidering that you will not be developing any professional application, I guess MediaElement will be fine.
However, MediaElement is the simplest option, and if it works for you, then keep working with it. As for your problem, I think there are few possible solutions:
Have two MediaElements and switch between them. If one video ends, start another vid in another MediaElement, as long as you play first frame on second mediaElement, hide the first mediaElement, and vice versa. You can poll for position, and maybe MediaStarted event. This way the flicker will be almost impossible to notice.
If you want fluent video playing without ANY flicker at all, there is GMFPlay. You can check it out. Though it's not MediaElement. But it can play videos simultaneously without any flicker.
Take screenshot of the last frame(you can take screenshots with WPF) and show it as Image while MediaElement is secretly loading.

disable map movement from flicks

I have a map on which I dynamically display pushpins, and would like to interact with the pushpins without interacting with the map (so I can't just disable the map by setting IsEnabled="False", nor can I use an image of the map).
I've tried the techniques depicted here: Handling tap on Pushpin in a fixed Map, which work great for zooming, and panning.
Extending this idea I've set up event handlers for all the events that would seem to match:
Hold="map_Hold"
MapPan="map_MapPan"
MapZoom="Map_MapZoom"
ManipulationStarted="map_ManipulationStarted"
ManipulationDelta="map_ManipulationDelta"
ManipulationCompleted="map_ManipulationCompleted"
MouseLeftButtonDown="map_MouseLeftButtonDown"
MouseLeftButtonUp="map_MouseLeftButtonUp"
TargetViewChanged="map_TargetViewChanged"
ViewChangeStart="map_ViewChangeStart"
ViewChangeEnd="map_ViewChangeEnd"
ViewChangeOnFrame="map_ViewChangeOnFrame"
(in the event handlers I simply set e.Handled = true;)
Yet you can still pan the map by making flicking motions on it (like how you would flick to the next image in the photo app).
Is there a way to disable the map panning altogether?
Or is there a way to interact with the pins if IsEnabled="False"?
This post was a while ago so you probably have solved the problem by now. But I had this problem today and this is the solution I had. (it's not very elegant, but seems to work fine).
For me this problem occurred when letting go of a pushpin after dragging and dropping it - Even though I had disabled the map_pan event when dragging a pushpin, the map would still move from the 'flick' gesture which is created when letting go of a push pin after dragging and dropping it.
First of all, create a page scoped DespatchTimer and an int which will be our counter:
DispatcherTimer dti = new DispatcherTimer();
int counter =0;
Next, in the pushpin's MouseLeftButton up event, disabled your map, start the despatch timer with an interval of 1. Also create the Tick event handler for it.
private void pushpin_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Map.IsEnabled = false;
dti.Interval = new TimeSpan(0, 0, 0, 0, 1); // 1 Seconds
dti.Start();
dti.Tick += new EventHandler(dti_Tick);
//other code logic
}
Next, create dti_Tick event. Within the tick event, stop the despatch timer when it is the second iteration into the tick event and re-enable the map. Reset the counter for future use.
void dti_Tick(object sender, EventArgs e)
{
counter++;
if (counter>= 1)
{
dti.Stop();
Map.IsEnabled = true;
counter= 0;
}
}
This process will have stopped any 'flick' from occuring after your drag and drop pushpin logic. Clearly this code can be further optimised, but hope it is of some help to you.

Dispatcher events are rendered too fast on screen. How can I "slow down" the process?

I have a UniformGrid, with each cell having the property of being colored white or red.
I want to have a loop which runs on all the grid's coordinates (top to bottom, left to right) and paint the grid cells red, but I wish to do so that it looks like an animation - a red line filling the grid. In reality, the grid just fill up entirely in a second, thus no illusion of animation.
I have a loop running which calls the UI Dispatcher and sends it the "paintCell" function, which colors the cell (red or white). Supposedly, it seems as if the Dispatcher object runs the paintCell code but doesn't actually re-paint the screen until all the queued rendering events are worked on and removed from the queue.
I tried adding: "Thread.sleep(500)", thinking I might slow down the whole coloring-and-rendering section, but the program acts the same concerning the interval between cell colorings - the program does sleep, but the whole grid is till repainted and rendered at once.
Any clarifications needed?
You shouldn't Sleep in the UI thread (which I suspect you do), as this blocks the whole UI for the duration of Sleep. You should do the sleep in your loop, which sends the updates through the Dispatcher (you run the loop in a separate thread, of course, right?)
You should use a DispatcherTimer for your painting operations. The DispatcherTimer runs on the UI thread, so you can call your paintCell method in there without any extra work:
DispatcherTimer _timer = new DispatcherTimer();
void StartTimer()
{
_timer.Interval = new TimeSpan(0, 0, timerResolutionInSeconds);
_timer.Tick += (s, e) => TimerElapsed();
_timer.Start();
}
int cell;
private void TimerElapsed()
{
paintCell(cell++);
// .... stop timer when done
if (done)
_timer.Stop()
}

Double buffer for Silverlight game

I start learning Silverlight and would like to create some simple game.
I am using CompositionTarget.Rendering event for my animation
But animation is not smooth, I developed games before and I used double buffer to avoid such problems, but I can't find if it possible with Silverlight.
Does anybody know how to create smooth animation with CompositionTarget.Rendering event.
Thanks,
.NET Developer.
Are you assuming that the Rendering event fires at a constant rate? It's not guaranteed to. On my machine it usually fires 60 times per second, but sometimes it's a bit faster and sometimes noticeably slower. It seems to skip a frame on occasion, which could cause your animation not to be smooth.
However, the event gives you the information to determine exactly how long it's been since the last frame (though you have to know how to get it), and you can code your animation to take this into account. Move farther if it's been a longer amount of time since the last frame, etc.
You need to take the EventArgs that's passed to your event handler, cast it to RenderingEventArgs, and then read its RenderingTime property. Calculate the delta in RenderingTime since your last event; that tells you how long it's been since your last frame was shown, and you can use that to pace your animations.
CompositionTarget.Rendering += CompositionTarget_Rendering;
...
private static TimeSpan? _lastRenderTime;
private void CompositionTarget_Rendering(object sender, EventArgs e) {
var args = (RenderingEventArgs) e;
var elapsed = _lastRenderTime.HasValue ?
args.RenderingTime - _lastRenderTime.Value :
TimeSpan.Empty;
_lastRenderTime = args.RenderingTime;
// "elapsed" tells you how long since the last frame.
// Now you can update your animation accordingly. For example,
var left = Canvas.GetLeft(_myControl);
left += elapsed.TotalSeconds * 100;
Canvas.SetLeft(_myControl, left);
}
I've heard that, at least in WPF, RenderTime doesn't tell you the current time, but rather what time it will be when the frame is shown on the screen. I haven't seen that substantiated from official sources, and even if it's true, I don't know if it's true for Silverlight as well. But whatever the case, it will give you the best possible information for writing your animation.
How much processing do you perform in Rendering event? As one option you can render part of your scene into WriteableBitmap and only use rendering event to swap bitmaps.

How can I change the way InkCanvas draws?

I've searched for examples for this, but the ones I've ran across seem to focus on simpler stuff like setting the InkCanvas DefaultDrawingAttributes such as Width, Height, Color etc. Doesn't seem like there's a lot of material for this.
For example, if I hold down the mouse button I can see it drawing lines. What if I want to draw ellipses instead of lines, or draw ellipses around sampled points between the start and end of the line?
I know I can get new points with the StrokeCollected event, but beyond that I have no idea where to go. This guy seemed like he got msdn's code working, but I couldn't do it. I only know how to build the interface using XAML, and there doesn't seem to be a sample either.
edit
Created a StrokeCollection class variable called thisIsNotNice, initialized in the constructor and did this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = thisIsNotNice;
foreach (StylusPoint p in e.Stroke.StylusPoints)
{
StylusPointCollection spc = new StylusPointCollection();
spc.Add(p);
Stroke s = new Stroke(spc);
s.DrawingAttributes.Height = 3;
s.DrawingAttributes.Width = 3;
thisIsNotNice.Add(s);
}
e.Handled = true;
}
But it doesn't work as it should. The ellipses are drawn, but the lines drawn by the mouse are still there. Also, for some reason, the first time it works as it should, drawing just the ellipses, but afterward it draws both the ellipses and the lines. But, if I do this instead:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = new System.Windows.Ink.StrokeCollection();
e.Handled = true;
}
The lines aren't kept on the screen. So, I don't understand why they aren't being erased in the above code.
If I do this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
foreach (Stroke s in myInkCanvas.Strokes)
System.Diagnostics.Trace.WriteLine(s);
e.Handled = true;
}
I can also see that the canvas contains the line strokes.
While erasing the strokes after they have been added to the collection is far from ideal, it at least does what I want. I could set up the line color to be the same of the background, but then I wouldn't be able to retrieve just the ellipses. I could copy them to a separate collection too, but that's just awful.
It sounds like you want to customize the way strokes appear on your inkCanvas. There are two separate things to consider here:
1) The way they look as the ink flows off the pen, before it is lifted (the DynamicRenderer, who runs on another thread to ensure that ink is always fast, is responsible for this. It sounds like you're happy with your solution to this already.
2) The way the eventual stroke sitting on the canvas looks. To customize this you might try subclassing Stroke, overriding:
protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes);
Each time you get a strokeCollected (and here's the same horrible thing you were worried about but there you go), you remove the incoming stroke from the canvas and replace it with your custom implementation, stealing the stroke data from the incoming one.
Your implementation of DrawCore would look something like (pseudocode):
foreach(sp in this.StylusPoints)
drawingContext.DrawEllipse(RADIUS, sp.X, sp.Y)
And so as not to get the lines that normally happen you would not call base.DrawCore(context,attributes) at any point.

Resources