Manipulate form control properties from other classes? - c#

I'm trying to manipulate the properties of a control in my main form via one of my classes.
Basically I'm trying to update a label, I've set it to public like so:
public Label DicerollLabel;
And I'm refering to my main form from my class like this:
private Form1 _mainForm;
When I try to access the label and set a value to it like this:
_mainForm.DicerollLabel.Text = "Hello World!";
I get the following error:
An unhandled exception of type 'System.NullReferenceException' occurred.
My entire code to the two files involed is below:
Main Form:
namespace BettingGame
{
public partial class Form1 : Form
{
private Player _playerOne;
private Player _playerTwo;
private DiceRoller _diceRoller;
private decimal _prizePool;
private Random _random;
public int ProgressBar
{
get {return progressBar1.Value;}
set { progressBar1.Value = value; }
}
public Label DicerollLabel;
public Form1()
{
InitializeComponent();
_playerOne = new Player() {PlayerName = "x", PlayerFunds = 100};
_playerTwo = new Player() {PlayerName = "x", PlayerFunds = 100};
_diceRoller = new DiceRoller();
_random = new Random();
playerOneFundsLabel.Text = "Funds: " + _playerOne.PlayerFunds.ToString(CultureInfo.CurrentCulture) + "$";
playerTwoFundsLabel.Text = "Funds: " + _playerTwo.PlayerFunds.ToString(CultureInfo.CurrentCulture) + "$";
PrizeAmountLabel.Text = "";
diceRollLabel.Text = "";
}
private void button1_Click(object sender, EventArgs e)
{
_playerOne.PlayerBetAmount = (decimal) playerOneBet.Value;
_playerTwo.PlayerBetAmount = (decimal) playerTwoBet.Value;
if (!(_playerOne.PlayerBetAmount <= 0) || !(_playerTwo.PlayerBetAmount <= 0)
&& (_playerOne.PlayerBetAmount > 0) && (_playerTwo.PlayerBetAmount > 0))
{
_prizePool = _playerOne.PlayerBet() + _playerTwo.PlayerBet();
PrizeAmountLabel.Text = _prizePool.ToString(CultureInfo.CurrentCulture);
FormUpdate();
}
else
{
MessageBox.Show("Invalid bets! Bet amount must be greater than 0!");
}
}
private void FormUpdate()
{
playerOneFundsLabel.Text = "Funds: " + _playerOne.PlayerFunds.ToString(CultureInfo.CurrentCulture) + "$";
playerTwoFundsLabel.Text = "Funds: " + _playerTwo.PlayerFunds.ToString(CultureInfo.CurrentCulture) + "$";
}
private void button2_Click(object sender, EventArgs e)
{
//for (int i = 0; i < 45; i++)
//{
// int value = _random.Next(1, 50);
// diceRollLabel.Text = value.ToString();
// diceRollLabel.Update();
// Thread.Sleep(50);
//}
_diceRoller.RollDice();
}
}
}
And the DiceRoller class:
namespace BettingGame
{
class DiceRoller
{
private Random _random = new Random();
private Form1 _mainForm;
public void RollDice()
{
_mainForm.DicerollLabel.Text = "Hello World!";
}
}
What am I doing wrong? Note that I'm only in my second week of programming so I'm still learning!

change your DiceRoller class to define the following constructor:
public DiceRoller(Form1 host) {
_mainForm = host;
}
Then in your Form1 where you create an instance of DiceRoller:
private DiceRoller _diceRoller = new DiceRoller(this);
This is a really terrible design long term. It's definitely code I would expect from a new programmer - so don't feel bad. You're doing fine.
In the future try to think about reusability. By making your DiceRoller dependent on Form1 (and the specific controls in Form1) you are faced with two challenges later on:
1. You can not use DiceRoller in another project (or possibly even in your same project) without modification.
2. If you change any of the controls that DiceRoller depends upon you must also change DiceRoller.
I'll let you think about how you might avoid these issues. I'm sure if you need help with them you'l post another question. :)

You need to have a reference to the Form1 created. This is not named Form1, but within the Form1 you can refer to it using the keyword "this" and you can pass that into a constructor for your DiceRoller class.
class DiceRoller
{
private Random _random = new Random();
private Form1 _mainForm;
public DiceRoller(Form1 f)
{
_mainForm = f;
}
public void RollDice()
{
_mainForm.DicerollLabel.Text = "Hello World!";
}
}
Then you call this within your constructor for Form1:
public Form1()
{
InitializeComponent();
_playerOne = new Player() {PlayerName = "x", PlayerFunds = 100};
_playerTwo = new Player() {PlayerName = "x", PlayerFunds = 100};
_diceRoller = new DiceRoller(this);
....
These are the minimal changes needed to make your code work. However, you might consider other changes such as passing in the label instead of the form.

Related

Notification System with real time minutes compare

I'm trying to do a notification system that will popup every exact defined minute.
I'm using Tulpep.NotificationWindow from Nuggets but i have a problem with it, it won`t pop up the notification, but works as a messagebox instead of Tulpep.NotificationWindow.
My code:
namespace SpawnBosi
{
public partial class Form1 : Form
{
Thread t1;
int channel1 = 46;
int result;
public Form1()
{
InitializeComponent();
t1 = new Thread(new ThreadStart(checkforminutes));
t1.Start();
}
public void checkforminutes()
{
while (true)
{
result = DateTime.Now.Minute;
compareminutes();
}
}
public void Ch1Notificaton()
{
var popupNotifier = new PopupNotifier();
popupNotifier.TitleText = "Title of popup";
popupNotifier.ContentText = "Content text";
popupNotifier.IsRightToLeft = false;
popupNotifier.Popup();
}
public void compareminutes()
{
if (result == channel1)
{
Ch1Notificaton();
}
}
}
}
Thread t1 will check every second if the minute changed and will compare the channel1 with actual DateTime.Now.Minute.
if the time is == with channel1 set time should notify . But it donesn't work with this system. How can i resolve my problem?
UPDATE
int channel1 = 25;
int channel2 = 26;
public Form1()
{
InitializeComponent();
}
public void checkandcompareminutes()
{
int actualminute = DateTime.Now.Minute;
if (actualminute == channel1 || actualminute == channel2)
{
Ch1Notificaton();
}
}
public void Ch1Notificaton()
{
var popupNotifier = new PopupNotifier();
popupNotifier.TitleText = "Title of popup";
popupNotifier.ContentText = "Content text";
popupNotifier.IsRightToLeft = false;
popupNotifier.Popup();
}
private void timer1_Tick(object sender, EventArgs e)
{
checkandcompareminutes();
}
}
There are a lot of things I would change here:
You do not need to use the object variable result. Use a local variable and combine the code of checkforminutes and compareminutes to one method.
You should use a Thread.Sleep to wait at least e.g. 500 milliseconds in your while-loop. This loop is quite exhaustive in regards to CPU time.
Overall you should better use the Timer class instead of checking the time in a while-loop.

Multiple class instances raising same event

I'm having trouble figuring out what is wrong with my C# code.
I'm trying to learn how to use ConcurrentQueue class in System.Collections.Concurrent namespace.
In order to do this, I'm creating 2 instances of the same class in different threads, passing to the constructors a different Listbox control.
I am expecting each class instance of EventGenerator to raise events at random intervals, updating the Listbox the were passed with randomly generated number, and adding that number to a ConcurrentQueue which is also passed to the constructor.
In my main thread, is the method to DeQueue the ConcurrentQueue of objects EnQueued to it by both spawned threads.
But what I'm getting is the 2 EnQueue Listboxes displaying the same data and the DeQueue Listbox seeming reporting to have deQueued them both.
I apologize if my description is not good enough, and my code follows, along with a link to an image of my form in case it might better help visualize what I'm trying to do...
Form
public partial class Form1 : Form
{
ConcurrentQueue<int> CQ;
EventGenerator eventGenerator1;
EventGenerator eventGenerator2;
public Form1()
{
InitializeComponent();
CQ = new ConcurrentQueue<int>();
eventGenerator1 = new EventGenerator(CQ, listBox1);
eventGenerator1.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
eventGenerator2 = new EventGenerator(CQ, listBox2);
eventGenerator2.OnRandomEvent += new EventGenerator.RandomEventHandler(RandomEvent);
}
private void RandomEvent(object sender, IncomingConnectionEventArgs e)
{
string s = e.Property_Int.ToString()
+ " "
+ e.Property_String;
UpdateListbox(s, e.LB);
}
private void UpdateListbox(string argstring, ListBox argListBox)
{
if (InvokeRequired)
{
Invoke(new Action<string, ListBox>(UpdateListbox), new object[] { argstring, argListBox });
return;
}
int n;
bool b = false;
//do
//{
b = CQ.TryDequeue(out n);
//} while (!b);
argListBox.Items.Add(argstring);
argListBox.SelectedIndex = argListBox.Items.Count -1;
listBoxDeQueue.Items.Add(n.ToString());
listBoxDeQueue.SelectedIndex = listBoxDeQueue.Items.Count - 1;
}
private void button_Start_Click(object sender, EventArgs e)
{
Thread methodThread1 = new Thread(new ThreadStart(TheThread1));
methodThread1.Start();
Thread methodThread2 = new Thread(new ThreadStart(TheThread2));
methodThread2.Start();
}
private void TheThread2()
{
eventGenerator2.Start();
}
private void TheThread1()
{
eventGenerator1.Start();
}
private void button_Stop_Click(object sender, EventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
eventGenerator1.Stop();
eventGenerator2.Stop();
}
}
IncomingConnectionEventArgs
class IncomingConnectionEventArgs : EventArgs
{
public System.Windows.Forms.ListBox LB;
public int Property_Int { get; set; }
public string Property_String { get; set; }
public IncomingConnectionEventArgs(int argInt, string argString,
System.Windows.Forms.ListBox lb)
{
LB = lb;
Property_Int = argInt;
Property_String = argString;
}
}
EventGenerator
class EventGenerator
{
public delegate void RandomEventHandler(
object sender,
IncomingConnectionEventArgs e);
public event RandomEventHandler OnRandomEvent;
public Random r = new Random();
public ListBox listBox;
public bool Generate = true;
public ConcurrentQueue<int> CQ;
public EventGenerator(ConcurrentQueue<int> argCQ, ListBox argListBox)
{
CQ = argCQ;
listBox = argListBox;
}
public void Start()
{
Generate = true;
while (Generate)
{
Thread.Sleep(r.Next(100, 2000));
RandomEvent();
}
}
public void Stop()
{
Generate = false; ;
}
public void RandomEvent()
{
if (OnRandomEvent == null)
{
return;
}
int n = r.Next(1000, 10000);
CQ.Enqueue(n);
IncomingConnectionEventArgs Args =
new IncomingConnectionEventArgs(n, "", listBox);
OnRandomEvent(this, Args);
}
}
The problem is with your use of Random. Unless you use a single instance of Random or explicitly seed each instance differently, the two threads calling Random.Next(...) will typically generate the same values.
A better way to seed your instance is this:
Random r = new Random(Guid.NewGuid().GetHashCode());

How to make an instance variables inside a class

I'm learning the Windows Application Form in Visual Studio, I'm making a number guessing game where the program generates a random number.
I put the random number generator inside the Button_Click method, I want the number to say the same when the program start but it change every time I click the button.
public partial class myWindow : Form
{
public myWindow()
{
InitializeComponent();
}
private void guessButton_Click(object sender, EventArgs e)
{
Random random = new Random();
int roll = random.Next(0, 99);
Where should I declare or put the random number generator and variable so it doesn't change ?
Make it a class member:
public partial class myWindow : Form
{
private int _roll;
private int _numGuesses;
public Window()
{
InitializeComponent();
Random random = new Random();
_roll = random.Next(0, 99);
}
private void guessButton_Click(object sender, EventArgs e)
{
bool isGuessCorrect = // Set this however you need to
if (isGuessCorrect)
{
// They got it right!
}
else
{
_numGuesses++;
if (_numGuesses > 9)
{
// Tell them they failed
}
else
{
// Tell them they're wrong, but have 10 - _numGuesses guesses left
}
}
}
}

Pass Values from Form -> Class -> Form C#

I have 2 Forms: V.Batch and V.BatchEdit and a Class: M.Batch
In V.Batch there is a DataGrid. I want to pass the value I get from the DataGrid to V.BatchEdit and the get set method is in M.Batch.
The problem here is that the value isn't passed properly in V.BatchEdit. It returns 0.
Here is the code
V.Batch:
M.Batch bt;
public Batch()
{
bt = new M.Batch();
InitializeComponent();
}
private void metroButton3_Click_1(object sender, EventArgs e)
{
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit();
this.Hide();
bEdit.Show();
}
M.Batch:
public int batchNum;
public int BatchNum
{
set { batchNum = value; }
get { return batchNum; }
}
V.BatchEdit
static M.Batch bt = new M.Batch();
DataSet a = bt.getBatch(bt.batchNum);
public BatchEdit()
{
db = new Database();
InitializeComponent();
System.Windows.Forms.MessageBox.Show(bt.batchNum.ToString() + "Batchedit");
try
{
metroTextBox1.Text = a.Tables[0].Rows[0][2].ToString();
}
catch (Exception exceptionObj)
{
MessageBox.Show(exceptionObj.Message.ToString());
}
}
I'm new to coding and c#. I'm not sure if I placed static even though it should not be static or what.
Yes, you are using static improperly here.
The easiest way to see what's going wrong is to notice that you are calling new M.Batch() twice. That means you have two different instances of M.Batch in your application. And nowhere in your code do you attempt to share those instances.
What you should be doing is passing your instance of M.Batch from one form to another, e.g. in the constructor:
// V.Batch
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit(bt);
this.Hide();
bEdit.Show();
// V.BatchEdit
private M.Batch bt;
private DataSet a;
public BatchEdit(M.Batch batch)
{
this.bt = batch;
this.a = this.bt.getBatch(bt.batchNum)
// Rest of your code here.
}
If You don't need the 'M.Batch' class for something else and you only use it for passing the value to V.BaychEdit, just declare a public property in V.BatchEdit like you did in M.Batch and use it like this:
V.BatchEdit bEdit = new V.BatchEdit();
bEdit.BatchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
Your problem is that although your using statics you're still assigning a new instance to the static field.

c# newb question from "Head Start C# Greyhound Lab"

i'm new at C # and i'm creating a racetrack simulator, but i'm currently encountering some issues when i run my code. I have an array of four Greyhound objects, and if I call Greyhound.Run on my form1.cs and i don't have a "MessageBox.Show("Distance" + distance)" on my Run method, which shows me how many pixels each greyhound is supposed to move, all of the greyhounds end up moving the same distance. I don't understand why this is happening
namespace Race
{
class Greyhound
{
public int StartingPosition;
public int RacetrackLength;
public PictureBox MyPictureBox = null;
public int Location = 0;
public Random Randomizer;
public bool Run()
{
Point p = MyPictureBox.Location;
if (p.X + MyPictureBox.Width >= RacetrackLength)
{
//TakeStartingPostion();
return true;
}
else
{
Randomizer = new Random();
int distance = Randomizer.Next(100);
MessageBox.Show("Distance is " + distance);
p.X += distance;
MyPictureBox.Location = p;
return false;
}
}
public void TakeStartingPostion()
{
Point P = MyPictureBox.Location;
P.X = StartingPosition;
MyPictureBox.Location = P;
}
}
}
namespace Race
{
public partial class Form1 : Form
{
Guy[] guys = new Guy[3];
Greyhound[] hounds = new Greyhound[4];
public Form1()
{
InitializeComponent();
hounds[0] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound1,
RacetrackLength = 636
};
hounds[1] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound2,
RacetrackLength = 636
};
hounds[2] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound3,
RacetrackLength = 636
};
hounds[3] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound4,
RacetrackLength = 636
};
}
private void button2_Click(object sender, EventArgs e)
{
for (int i = 0; i < hounds.Length; i++)
{
hounds[i].Run();
}
}
}
}
It's because you're calling new Random() each time the Run() method hits the else block. The default Random constructor initializes the pseudorandom number generator based on the current time. When there's no interruption, all 4 method run at "the same time", so they spit out the same random numbers. To fix this, either create only a single Random, or else use a different seed for each one (by using the Random constructor that takes a seed as a parameter.)
Something like this would work:
public class Greyhound
{
public static Random randomizer = new Random();
// ... In the run method ...
int distance = Greyhound.randomizer.Next(100);
}
Update: As Groo points out, if you were actually calling Next() from multiple threads, the code I've shown isn't thread-safe. While that's not the case with your code, it's a good idea to be aware of issues like this earlier rather than later. The general solution to this (potential) problem is to surround the call to Next() with a lock, like so:
// ... After the declaration of randomizer ...
private static object randomLock = new object();
// ... New Next() call...
lock (randomLock)
Greyhound.randomizer.Next(100);
Since you probably call the Run methods of all of your objects quickly in succession, each instance of the Random class gets instantiated with the same seed, returning the same pseudo-random number.
You can solve this problem by creating a static random class which will have a single (Singleton) Random instance, making sure that each of your callers gets the next number in succession.
With some locking for thread safety, it would look something like:
public class StaticRandom
{
private static readonly Random _r = new Random();
private static object _lock = new object();
public static int Next(int max)
{
lock (_lock)
return _r.Next(max);
}
}
And then use it without instantiating it:
// no need for the "Randomizer" field anymore
int distance = StaticRandom.Next(100);
Jon Skeet has the full implementation in his miscutil library, along with some usage info.

Categories