Editing properties of a PictureBox from another class - c#

Context: I am trying to make a little game of Backgammon in Windows.Forms. I have 3 classes GameBoard.cs, Points.cs and Checkers.cs (as well as Form1.cs). What I need to be able to do is change values of some properties of a PictureBox using some of my other classes.
Specifically I have this code:
// gameBoard.cs
namespace backgammon
{
public class gameBoard
{
Checker checker1;
Points point1;
Points[] pointsArray;
public gameBoard()
{
// make new checker (ID, PictureBox, startingPoint)
checker1 = new Checker(1, checkerPicBox1, 1);
// make new Point (ID, arrayOfCheckers)
point13 = new Points(1, new Checker[]{checker1 /*,checker2... etc*/});
pointsArray = new Points[MAX_POINTS];
pointsArray[0] = point1;
}
}
}
so this is how I have "setup" the checkers and points. My checker and point classes can get and set on all variables that were passed into its construction.
Problem: what I am trying to achieve is to "highlight" a checker once it is clicked.
In Form1:
// Form1.cs
private void checkerPicBox1_Click(object sender, EventArgs e)
{
int pointNumber = gameBoard.checker1.getPointMember();
// find the top most checker in the checker array so we can highlight it
Checker topMost = gameBoard.pointsArray[pointNumber - 1].getCheckerFromIndex(gameBoard.pointsArray[pointNumber - 1].getCheckerArray().Length - 1);
// get the picturebox and change the image
topMost.getPictureBox().BackgroundImage = global::Backgammon.Properties.Resources.blackCheckerSelected;
}
The code compiles and runs but when it gets to the last line of checkerPicBox_Click nothing seems to happen (the image doesn't change).
What is happening here? Do I not have the correct instance of my PictureBox? Or am I doing this in a weird way/not the way i'm supposed to be?

Since the checkers are connected to the pictureboxes, why not create a new reference property for them in the Checker class?
You could pass the form containing the pictureboxes in the constructor, and use the form to create your references to the right picturebox instances.
// gameBoard.cs
namespace backgammon
{
public class gameBoard
{
Checker checker1;
Points point1;
Points[] pointsArray;
public gameBoard(Form gameForm)
{
// make new checker (ID, PictureBox, startingPoint)
checker1 = new Checker(1, gameForm.checkerPicBox1, 1);
// make new Point (ID, arrayOfCheckers)
point13 = new Points(1, new Checker[]{checker1 /*,checker2... etc*/});
pointsArray = new Points[MAX_POINTS];
pointsArray[0] = point1;
}
}
}
Add the picturebox property to your Checker class:
// checker.cs
public class Checker
{
PictureBox _picturebox;
//... other code here
public Checker(int ID, PictureBox picturebox, Points startingPoint)
{
_picturebox = picturebox;
//...other code here
}
}
Then we can use this in the game form:
gameBoard gameBoard1 = new gameBoard(this);
gameBoard1.checker1._picturebox.BackgroundImage = global::Backgammon.Properties.Resources.blackCheckerSelected;
gameBoard1.checker1._picturebox.Invalidate();

Related

Optimizing a Unity CharacterSelection

in my game, I got some teachers to make the player increase his skills. At the moment, there are 12 guys, so I wrote a base class for them.
This class selects the correct data from its own "teacherDataClass". The data is set by an index from the Editor.
My code:
[SerializeField]
int teacherIndex; // set an index in the editor -> selection of teacher
NpcTeacherData teacherData; // the data class
private void Start()
{
NpcTeacherData[] teachers = // Collection of all teachers
{
new TeacherAlchemist(),
new TeacherBlacksmith(),
new TeacherBowyer(),
new TeacherButcher(),
new TeacherHunter(),
new TeacherInnkeeper(),
new TeacherJuggler(),
new TeacherMessenger(),
new TeacherPriest(),
new TeacherTamer(),
new TeacherThief(),
new TeacherTownGuard()
};
teacherData = teachers[teacherIndex]; // Get the right teacher by the index
}
So this looks fine and this works fine. If you do not want to use the Editor, you could compare the tag of the teacherObject like this way
NpcTeacherData teacherData; // the data class
private void Start()
{
switch (gameObject.tag) // compare the tag of the teacher and set its class
{
case "TeacherAlchemist":
teacherData = new TeacherAlchemist();
break;
case "TeacherBlacksmith":
teacherData = new TeacherBlacksmith();
break;
//...
}
}
But I do not like these ways that much :/ Are there any better possibilities? Like a DropDownSelection in the Editor maybe?
I tried
[SerializeField]
object script; // Place the right data class here
[SerializeField]
object script as NpcTeacherData; // Place the right data class here
But this did not work.
I just want to optimize it :)
Maybe you can use an enum to achieve this?
It would be something like this:
public enum TeacherType
{
TeacherAlchemist,
TeacherBlacksmith,
TeacherBowyer,
TeacherButcher,
TeacherHunter,
TeacherInnkeeper,
TeacherJuggler,
TeacherMessenger,
TeacherPriest,
TeacherTamer,
TeacherThief,
TeacherTownGuard
}
public TeacherType type;
private NpcTeacherData teacherData;
private void Start()
{
switch (type)
{
case TeacherType.TeacherAlchemist:
teacherData = new TeacherAlchemist();
break;
//...
}
}
However it doesn't differ a lot from your first solution (only choice is a bit easier since the type is shown instead of an index).
Hope this helps,
You can use Editor GUI Popup. Here is how you can make a dropdown/popup -
public string[] options = new string[] {"TeacherAlchemist","TeacherBlacksmith","TeacherBowyer","TeacherButcher","TeacherHunter","TeacherInnkeeper","TeacherJuggler","TeacherMessenger","TeacherPriest","TeacherTamer","TeacherThief","TeacherTownGuar"};
public int selectedIndex = 0;
Then Use OnGUI function to store selected value
void OnGUI()
{
selectedIndex = EditorGUILayout.Popup("TeacherDataClass", selectedIndex, options);
}
You can find reference here.

C# XNA if-else and defaulting to first value

So I am developing a custom ButtonGUI class for my game. Here's the initialization of the button object:
// Button code:
ButtonGUI btn1 = new ButtonGUI("Button 1", new Rectangle(150, 300, (int)myFont.MeasureString(menuButtons[0]).X, (int)myFont.MeasureString(menuButtons[0]).Y), myFont, Color.CornflowerBlue);
Now consider this code:
// Draw() method:
btn1.Draw(spriteBatch);
if (btnHover)
{
btn1.btnRect = new Rectangle(140, 300, (int)hoverFont.MeasureString(menuButtons[0]).X, (int)hoverFont.MeasureString(menuButtons[0]).Y);
btn1.btnFont = hoverFont;
btn1.btnColour = Color.Red;
}
else
{
btn1.btnRect = new Rectangle(150, 300, (int)myFont.MeasureString(menuButtons[0]).X, (int)myFont.MeasureString(menuButtons[0]).Y);
btn1.btnFont = myFont;
btn1.btnColour = Color.CornflowerBlue;
}
This would be OK if I had only 1 button... But if I have like 10 buttons or more? This really isn't what DRY suggests. I feel like I'm missing something, there must be a way to return button properties to their default values once the condition is no longer met without doing the whole thing manually, or is there? Thanks in advance!
It may make sense to create a structure to hold all of the values that may change.
class ButtonData
{
// put members corresponding to each member of ButtonGUI you wish
// to change
}
class ButtonSwapper
{
ButtonGUI myButton;
ButtonData hoverData;
ButtonData notHoverData;
void change(bool hover)
{
ButtonData dataToUse = hover ? hoverData : notHoverData;
// set each relevant member of myButton to its pair in
// dataToUse
}
}
then call change as necessary.

WPF - How to add event handler to element from outside the class?

I am searching for answer many hours and with no success. There is the way to do that, but every included element has to be static. What is not suitable for me.
I have MainPage class and Tile class. This tile class instantiates few kinds of tiles in MainPage layout. I need to add to some kinds of these tiles Tapped event. This event should modify MainPage elements.
What is the best way to do that? I always end with "non-static warning", as you can see in the code:
public class Tile
{
public static Grid buttonTile_MapPanel(int rowNum, int colNum)
{
Grid mapPanelButton = new Grid();
mapPanelButton.Background = new SolidColorBrush(Color.FromArgb(255, 50, 50, 50));
TextBlock title = new TextBlock();
title.Text = "Mapa";
title.HorizontalAlignment = HorizontalAlignment.Center;
title.VerticalAlignment = VerticalAlignment.Center;
title.Foreground = new SolidColorBrush(Colors.White);
mapPanelButton.Children.Add(title);
//this will be signed as error: An object reference is required for non static field...
mapPanelButton.Tapped += new RoutedEventHandler(MainPage.mapPanelService);
return mapPanelButton;
}
}
public sealed partial class MainPage : Page
{
public void mapPanelService(object sender, RoutedEventArgs e)
{
MapPanel.Margin = new Thickness(0,0,0,0);
}
}
Main problem was bad type of handler, which demanded something else than it should.
Correct code:
public void dataPanelService(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
.
.
.

Silverlight new TouchPoint() causes a program crash

I want to simulate TouchPoints in Silverlight. The following code already raises an unhandled Exception:
currentTouchPoints[i] = new TouchPoint();
currentTouchPoints[i].SetValue(TouchPoint.PositionProperty,
new Point(x[i]+100+i*100,y[i]+500));
The first call to new TouchPoint() makes the program crash.
i guess you are allowed to call the new TouchPoint(), but its TouchDevice would be not valid and all its properties are readonly, so it would be useless.
I haven't found a solution myself.
I'd create a custom class and use that one instead.
For example if you are using already the normal TouchPoint in your code and don't want to change everything, import this custom namespace instead of System.Windows.Input
namespace MyTouchPoint
{
class TouchPoint
{
public Point Position = new Point (0,0);
public TouchDevice TouchDevice = new TouchDevice();
TouchPoint (int id_, Point position_)
{
TouchDevice.Id = id_;
Position = position_;
}
};
class TouchDevice
{
public int Id = 0;
};
} // end namespace
cheers,
Lorenzo

C#, variable value as a part of an object name?

For example i have a player object with a list of suits of cards it has.
player.card[0] = "diamonds";
player.card[1] = "clubs";
player.card[2] = "spades";
etc...
also i have 4 hidden pictureboxes with an image of suites ( pb_spades , pb_hearts m etc. )
and another 4 pictureboxs ( pb_playerCard1 , pb_playerCard2 , etc. ) to which I have to assign an image from a hidden pb corresponding to the suit of card the player object has.
Therefor
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
of course, doing it all with IFs would take quite a long time... Can I somehow use variable value as a part of an objects name?
kinda
for (int i = 1; i != 5; i++)
{
pb_playerCard+'i'.Image = pb_+'player.card[i+1]'.Image;
}
I don't think that you can use a value as a part of the control name. But, one can use an aray of controls. You will have to change many declarations and initializations of your picture boxes and put them into an array, but you will be able to write more descriptive and readable code.
Create a class Suite that has all properties, like this:
class Suite {
public string Name { get; set; }
public Image Image { get; set; }
and then create a static object for each color:
public static Diamonds = new Suite { Name = "Diamonds", Image = Resources.DiamondImage };
// ...
}
Now you can use Suite.Diamonds.
Even better is to use a Flyweight pattern to avoid the static fields. You use the Flyweight to implement the Card class.
First, there's no reason to have a hidden PictureBox control just so you can use it's Image property to store an image. Just create Image objects.
You could store the images in a dictionary, indexable by name:
var cards = new Dictionary<string, Image>() {
{ "diamonds", Image.FromFile("diamonds.jpg") }
{ "clubs", Image.FromFile("clubs.jpg") }
//...
};
Then instead of this:
if ( player.card[0] == "diamonds" ) { pb_playerCard1.Image = pb_diamonds.Image; }
You would write:
pb_playerCard1.Image = images[player.card[0]];
This code is still not great (any time you see variables like foo1, foo2, foo3, you should be putting those in an array so they can be indexed by number). The next step might be to refactor the code so you have something like:
pb_playerCard[0].Image = images[player.card[0]];
Or:
pb_playerCard[0].Image = player.card[0].Image;
thanks again guys, I got it working using a List to store card suites for the player objects and a dictionary that stores image references.
Dictionary<CardType, System.Drawing.Image> newDeckImages = new Dictionary<CardType, System.Drawing.Image>();
...
newDeckImages.Add(CardType.Diamonds, pb_diamonds.Image);
newDeckImages.Add(CardType.Hearts, pb_hearts.Image);
newDeckImages.Add(CardType.Clubs, pb_clubs.Image);
newDeckImages.Add(CardType.Spades, pb_spades.Image);
...
private void showMyCards()
{
pb_termCard1.Image = newDeckImages[Terminator.cards[0]];
pb_termCard2.Image = newDeckImages[Terminator.cards[1]];
pb_termCard3.Image = newDeckImages[Terminator.cards[2]];
pb_termCard4.Image = newDeckImages[Terminator.cards[3]];
}
The main class Player and the associated enumeration should be like this:
class Player
{
public Player(int n)
{
Cards = new List(n);
}
public IList Cards { get; private set; }
}
enum CardType
{
Diamond,
Club,
Spade,
Heart
}
Another method GetPictureBox() should in the winform partial class
public PictureBox GetPictureBox(string pictureBoxName)
{
if(this.Controls.ContainsKey(pictureBoxName))
{
Control control = this.Controls[pictureBoxName];
PictureBox pictureBox;
if ((pictureBox = control as PictureBox) != null)
{
return pictureBox;
}
}
return null;
}
Use this method in the class using the player instance.
Player player = new Player(3);
player.Cards.Add(CardType.Diamond);
player.Cards.Add(CardType.Club);
player.Cards.Add(CardType.Spade);
Dictionary dictionary = new Dictionary();
dictionary.Add(CardType.Diamond, pb_diamonds);
dictionary.Add(CardType.Spade, pb_spades);
dictionary.Add(CardType.Club, pb_clubs);
dictionary.Add(CardType.Heart, pb_hearts);
Finally the assignment to the image property,
for (int i = 1; i < 5; i++)
{
string playercardPictureBoxName = string.Concat("pb_playercard", i.ToString());
PictureBox pictureBox = this.GetPictureBox(playercardPictureBoxName);
if (pictureBox != null)
{
pictureBox.Image = dictionary[player.Cards[i - 1]].Image;
}
}
No, not how you're doing it, and although similar things can be achieved with reflection, what you want here is probably closer to just using FindControl for the "pb_playerCard" part and a simple dictionary of type Dictionary<CardSuitEnum, WhateverTheTypeOfImageIs> which you can populate with the pb_diamonds.Image etc.
Apols, I don't know what type .Image holds and can't be bothered to look it up :)

Categories