I'm very new to WP8 dev and c#. I'm trying to make a loop that counts up by n on an interval. I want to press a button to increment n.
Here is my code right now:
namespace Petsounds {
public partial class MainPage : PhoneApplicationPage {
float clicks = 0;
float clickers = 0;
float clickerBuyers = 0;
float clickerCost = 5;
float clickerBuyerCost = 500;
long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
bool buyClickerButtonFlag = false;
bool clickButtonFlag = false;
// Constructor
public MainPage() {
InitializeComponent();
//
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
}
private void clickButtonOnClick(object sender, RoutedEventArgs e) {
clickButtonFlag = true;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
private void buyClickerButtonOnClick(object sender, RoutedEventArgs e) {
buyClickerButtonFlag = true;
}
private void startLoop() {
if (true) {
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag) {
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag) {
buyClickerButtonFlag = false;
if (clicks > clickerCost) {
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond) {
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
//update display
clickCount.Text = clicks.ToString();
buyClickerButtonCost.Text = "Cossst " + clickerCost.ToString();
}
}
}
}
My button's are inconsistent, and if I remove the thread, the buttons are responsive (but of course the counter doesn't work.)
EDIT:
I've changed
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
to
Timer myTimer = new Timer(startLoop);
myTimer.Change(1000, 10);
And now get an error:
A first chance exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
on line
clickCount.Text = clicks.ToString();
First of all... you will quickly find that 10ms is not really 10ms... It might not even be that close... If you did 1000ms... that would be expected to be more accurate.
Also, a DispatcherTimer is going to queue up a function call to the GUI thread each interval... which means you are flooding the GUI thread with startLoop() calls. This doesn't give the thread much time to update anything else... like your buttons.
There is a different approach you might want to consider.
If your task is to increment a numeric value when a user touches a button (and have the numbers increase at a steady pace) consider using the RepeatButton.
RepeatButton: Represents a control that raises its Click event repeatedly from the time it is pressed until it is released.
XAML
<!--
Delay: The time, in milliseconds, the RepeatButton waits
when it is pressed before it starts repeating the click action.
Interval: The time, in milliseconds, between repetitions
of the click action, as soon as repeating starts.
-->
<RepeatButton Content='Buy'
Interval='50' Delay='100'
Click='RepeatButton_Click' />
Code
private float buyCounter = 0;
private void RepeatButton_Click(object sender, RoutedEventArgs e) {
buyCounter += 1;
buyClickerButtonCost.Text = buyCounter.ToString();
}
It's like #Andrew said - DispatcherTimer works on UI thread and with so small intervall you are blocking it.
If you want such a small interval you can use Timer on different Thread:
public MainPage()
{
InitializeComponent();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(1000, 10);
}
private static int value = 0;
private static void MyTimerCallback(object state)
{
value++;
}
But you must remember that you use it on different Thread - this Timer has no access to your UI elements (buttons and so on).
EDIT
You convinced me to check it:
static float clicks = 0;
static float clickers = 0;
static float clickerCost = 5;
static long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
static bool buyClickerButtonFlag = false;
static bool clickButtonFlag = false;
public MainPage()
{
InitializeComponent();
first.Click += ShowCounter;
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(5);
t.Tick += ShowCounter;
t.Start();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(10, 10);
}
private void ShowCounter(object sender, EventArgs e)
{
textBlck.Text = clicks.ToString();
}
private static void MyTimerCallback(object state)
{
clicks++; // added to check running
if (true)
{
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag)
{
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag)
{
buyClickerButtonFlag = false;
if (clicks > clickerCost)
{
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond)
{
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
}
}
I tested it on the device and buttons works.
On the other hand - what's the point of putting a method that waits for a flag buton click, when you can put the job easily to button click event. Let it happen when user clicked button - don't check buton state all the time.
Related
I am creating a game in visual studio using c sharp and want to add a pop up message saying 'game Over' once the timer reaches 0. Currently the countdown timer goes to negative seconds and the game keeps going. Currently attempt is below and any help is apricated.
public MainPage()
{
InitializeComponent();
_random = new Random(); // r is my random number generator
_countDown = 30;
SetUpMyTimers();// method for my timer
endGame();
}
private void endGame()
{
throw new NotImplementedException();
}
private void SetUpMyTimers() // calling my method
{
// start a timer to run a method every 1000ms
// that method is "TimerFunctions" that runs on the UI thread
Device.StartTimer(TimeSpan.FromMilliseconds(1000), () =>
{
Device.BeginInvokeOnMainThread(() =>
{ TimerFunctions(); });
return true;
});
}
private void TimerFunctions()
{
// change the countdown.
_countDown--;
LblCountdown.Text = _countDown.ToString();
}
The countdown is over to call the function.Use winform timer control to implement countdown function
public partial class Form1 : Form
{
TimeSpan Span = new TimeSpan(0, 0, 10);
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
Span = Span.Subtract(new TimeSpan(0, 0, 1));
label1.Text = Span.Hours.ToString() + ":" + Span.Minutes.ToString() + ":" + Span.Seconds.ToString();//时间格式0:0:10
if (Span.TotalSeconds < 0.0)//when the countdown is over
{
timer1.Enabled = false;
MessageBox.Show("game over");
}
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Interval = 1000;//Set every interval to 1 second
timer1.Enabled = true;
MessageBox.Show("End the game after 10s");
}
}
Test timer:
Hope it helps you.
You could try the following code.
<Grid>
<TextBlock Name="tbTime" />
</Grid>
Codebehind:
DispatcherTimer _timer;
TimeSpan _time;
public MainWindow()
{
InitializeComponent();
_time = TimeSpan.FromSeconds(10);
_timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Normal, delegate
{
tbTime.Text = _time.ToString("c");
if (_time == TimeSpan.Zero)
{
_timer.Stop();
MessageBox.Show("GameOver");
}
_time = _time.Add(TimeSpan.FromSeconds(-1));
}, Application.Current.Dispatcher);
_timer.Start();
}
The result:
I have this code
Color[] colours = new Color[5]{Color.Red, Color.Blue, Color.Green, Color.Yellow, Color.Black};
public int randGen(int lower, int upper)
{
Random random = new Random();
return random.Next(lower, upper);
}
public PlayGame()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
changeColour();
}
public void changeColour()
{
int milliseconds = randGen(1000, 5000);
int count = 0;
Device.StartTimer(TimeSpan.FromMilliseconds(milliseconds), () =>
{
var layout = new StackLayout { Padding = new Thickness(5, 10) };
var label = new Label { Text = "Time: ", TextColor = Color.Green, FontSize = 25 };
layout.Children.Add(label);
label.Text += milliseconds.ToString();
this.Content = layout;
if (count < 4)
{
BackgroundColor = colours[count];
count++;
milliseconds = randGen(1000, 5000);
return true;
}
else
{
BackgroundColor = Color.Black;
return false;
}
}
);
}
Which has an array of colours. The idea is that every 1-5 seconds (which should be random each time), the background colour should change, and the text should write how long the screen was on for.
Currently, however, the time shown in the text is not reflective of the time each screen shows for, and I have some speculative concern that milliseconds in:
Device.StartTimer(TimeSpan.FromMilliseconds(milliseconds)
doesn't change at all. Any ideas?
this is what I would do - have your timer fire every second (or whatever granularity you need) but only execute your code every X times
using System.Timers;
// these are class variables
Timer timer;
int timecount = 0;
// adjust this dynamically so your code only executes every 1-n seconds
int interval = 1;
// to this wherever you want to start the timer
timer = new Timer();
timer.Elapsed += Timer_Elapsed;
// fire every 1 sec
timer.Interval = 1000;
timer.Start();
// timer event handler
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
timecount++;
if (timecount == interval)
{
timecount = 0;
// do other stuff here
}
}
I have a Windows Forms application where I need to have a timer working for 90 seconds and every second should be shown after it elapses, kind of like a stopwatch 1..2..3 etc, after 90 seconds is up, it should throw an exception that something is wrong.
I have the following code, but the RunEvent never fires.
private void ScanpXRF()
{
bool demo = false;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
try
{
for (int timerCounter = 0; timerCounter < 90; timerCounter++)
{
timer.Interval = 1000;
timer.Tick += new EventHandler(RunEvent);
timer.Start();
if(timerCounter == 89) {
throw new Exception();
}
}
}
catch (Exception e)
{
timer.Dispose();
MessageBox.Show("There is a problem!");
}
}
private void RunEvent(object sender, System.EventArgs e)
{
//boxStatus.AppendText("RunEvent() called at " + DateTime.Now.ToLongTimeString() + "\n");
MessageBox.Show("timer fired!");
}
Is there anything I am doing wrong here or are there other suggestions for other ways to achieve the same result?
A timer needs to be declared at the form level, or else it may not be disposed of when the form closes:
System.Windows.Forms.Timer timer;
int counter = 0;
Your starting code should just start the timer:
private void ScanpXRF()
{
counter = 0;
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
timer.Tick += RunEvent;
timer.Start();
}
The RunEvent is your Tick event being called every second, so your logic needs to go in there:
private void RunEvent(object sender, EventArgs e)
{
counter++;
if (counter >= 90) {
timer.Stop();
// do something...
}
}
made it work
private void ScanpXRF()
{
_pXRFTimerCounter = 0;
pXRFTimer.Enabled = true;
pXRFTimer.Interval = 1000;
pXRFTimer.Elapsed += new ElapsedEventHandler(pXRFTimer_Tick);
pXRFTimer.Start();
}
private static void pXRFTimer_Tick(Object sender, EventArgs e)
{
_pXRFTimerCounter++;
if (_pXRFTimerCounter >= 90)
{
pXRFTimer.Stop();
// do something...
}
else
{
MessageBox.Show(_pXRFTimerCounter.ToString() + " seconds passed");
}
}
I made the timer
System.Timers
I am trying to insert text to a lable, BUT the text has to be inserted slowly/character by character/letter by letter,
kinda like in old MUD games.
So far I have tried doing this:
private void StoryBox_Click(object sender, EventArgs e)
{
string text = StoryBox.Text;
var index = 0;
var timer = new System.Timers.Timer(2000);
timer.Elapsed += delegate
{
if (index < text.Length)
{
index++;
StoryBox.Text = "This should come out pretty slowly ";
}
else
{
timer.Enabled = false;
timer.Dispose();
}
};
timer.Enabled = true;
}
This is what I have gathered from the site but I don't particularly understand why this isn't working.
As you can see it's under StoryBox_Click.
Is there a way to automate this? So when the program is opened, it counts a couple seconds and THEN starts writing the text out.
Try this:
private async void button1_Click(object sender, EventArgs e)
{
string yourText = "This should come out pretty slowly";
label1.Text = string.Empty;
int i = 0;
for (i = 0; i <= yourText.Length - 1; i++)
{
label1.Text += yourText[i];
await Task.Delay(500);
}
}
You can use the Shown-Event of YourForm when you want to start it after your GUI has been opened.
So reusing your provided code and changing a few things this may work for you:
Add private fields to YourForm class:
private Timer _timer;
private int _index;
private string _storyText;
and initialising it in YourForm constructor
public YourForm()
{
InitializeComponent();
// init private fields;
this._index = 0;
this._storyText = "This should come out pretty slowly";
// Timer Interval is set to 1 second
this._timer = new Timer { Interval = 1000 };
// Adding EventHandler to Shown Event
this.Shown += this.YourForm_Shown;
this._timer.Tick += delegate
{
if (this._index < this._storyText.Length)
{
StoryBox.Text += this._storyText[this._index];
this._index++;
}
else
{
this._timer.Stop();
}
};
}
and the Shown event for YourForm:
private void YourForm_Shown(object sender, EventArgs e)
{
this._timer.Start();
}
I have a script based on the countdown timer. I want that when the time reaches 0, the timer stop and a message appear. The code id this:
public partial class simulare : Form
{
private admin admin;
Timer timer = new Timer();
public simulare(admin admin)
{
InitializeComponent();
this.admin=admin;
label2.Text = TimeSpan.FromMinutes(0.1).ToString();
}
private void simulare_Load(object sender, EventArgs e)
{
var startTime = DateTime.Now;
timer = new Timer() { Interval = 1000 };
timer.Tick += (obj, args) =>
label2.Text = (TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime)).ToString("hh\\:mm\\:ss");
timer.Enabled = true;
timer.Start();
if (condition)
{
timer.Stop();
MessageBox.Show("Done!");
}
}
}
I tried those conditions, but unsuccessful:
if (timer.ToString() == TimeSpan.Zero.ToString())
if (label2.Text.ToString() == TimeSpan.Zero.ToString())
if (label2.Text == TimeSpan.Zero)
You could extract the calculation and assign the result to a TimeSpan variable, then check if the Seconds in that TimeSpan variable are equals to zero
void simulare_Load(object sender, EventArgs e)
{
var startTime = DateTime.Now;
timer = new System.Windows.Forms.Timer() { Interval = 1000 };
timer.Tick += (obj, args) =>
{
TimeSpan ts = TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime);
label1.Text = ts.ToString("hh\\:mm\\:ss");
if (ts.Seconds == 0)
{
timer.Stop();
MessageBox.Show("Done!");
}
};
timer.Start();
}
First off, checking anything in the Load event isn't going to work. That code only runs once (on form load).
So you need a more complex tick event, which I would put into an actual function instead of a lambda:
private int countDown = 50; //Or initialize at load time, or whatever
public void TimerTick(...)
{
label2.Text = (TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime)).ToString("hh\\:mm\\:ss");
countDown--;
if (countDown <= 0)
timer.Stop();
}
I use an int counter here since checking against a view property (the text in this case) isn't a very good design/practice. If you really want a TimeSpan, I would still save it off instead of checking directly against the Text property or a string.