How can i define a structure with a string? - c#

I have
public string[] ButtonList()
{
string[] buttons = { "A", "B", "Back", "BigButton", "etc..." }
return buttons;
}
private void EchoButtons()
{
for (int i = 0; i < ButtonList().Length; i++)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
{
// Echo the buttons
}
}
}
Is there anyway i could use the string from the array to define the button?
Example (although this does't work):
for (int i = 0; i < ButtonList().Length; i++)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.ButtonList()[i] == ButtonState.Pressed)
{
// Echo the buttons
}
}
Edit: i hope this makes sense, i wasn't sure i explained it well.

You can use a list of delegates which have GamePadState as a parameter, and return the state for the desired buttons.
var getButtonState = new List<Func<GamePadState, ButtonState>>
{
s => s.Buttons.A,
s => s.Buttons.B,
...
};
// Example to get the state of the first item in the list.
ButtonState state = getButtonState[0]( GamePad.GetState( PlayerIndex.One ) );

Related

C# loop edit (I can't code, just trying to make a simple edit)

I need this pre-existing script to loop every forever, I do not know much code and am unable to edit this myself, I'm hoping one of you smart boys can assist in the editing of this script.
This script is C# built for a game called Space Engineers where it takes information from a solar panel to determine time of day/light level and respond by turning on the lights or turning off the lights.
//How to use:
//Setup a timer that triggers it self and this programmable block every 5-10 minutes. Then set the light strength(
//when the solar panel gets less kW's than that, the lights are turned on). Then set measureSolarPanelName to
//the customname of the solar panel that you want to use to measure the light. If you want to use a prefix, set
//usePrefix to true und set a prefix. When you set overwrite to true, this script will stop
//working until you deactivate overwrite(= false), for eg. battles.
//Thank you for using my script :)
//Settings:
int lightStrength = 10;
string measureSolarPanelName = "Solar Panel";
bool overwrite = false;
bool usePrefix = false;
string prefix = "[NL]";
//Code
public void Main(string argument) {
if (getSolarPower() < lightStrength) {
triggerLights(1);
} else {
triggerLights(0);
}
}
int getSolarPower () {
bool watts;
string info = GridTerminalSystem.GetBlockWithName(measureSolarPanelName).DetailedInfo;
var lines = info.Split('\n');
var output = lines[1].Split(':')[1].Trim();
var final = output.Replace(" kW", "");
if (final.Contains("W")) { final = final.Replace(" W", ""); watts = true; } else watts = false;
var finalb = final.Split ('.')[0];
if (watts) { finalb = "1"; }
return Int32.Parse(finalb);
}
void triggerLights(int status) {
if (status == 1 && !overwrite) {
List<IMyInteriorLight> lights = new List<IMyInteriorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(lights);
List<IMyReflectorLight> spotlights = new List<IMyReflectorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyReflectorLight>(spotlights);
for (int i = 0; i < lights.Count; i++) {
if (getPrefixBool(lights[i].CustomName)) {
lights[i].GetActionWithName ("OnOff_On").Apply(lights[i]);
}
}
for (int i = 0; i < spotlights.Count; i++) {
if (getPrefixBool(spotlights[i].CustomName)) {
spotlights[i].GetActionWithName("OnOff_On").Apply(spotlights[i]);
}
}
}
else
if (status == 0 && !overwrite) {
List<IMyInteriorLight> lights = new List<IMyInteriorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyInteriorLight>(lights);
List<IMyReflectorLight> spotlights = new List<IMyReflectorLight> ();
GridTerminalSystem.GetBlocksOfType<IMyReflectorLight>(spotlights);
for (int i = 0; i < lights.Count; i++) {
if (getPrefixBool(lights[i].CustomName)) {
lights[i].GetActionWithName("OnOff_Off").Apply(lights[i]);
}
}
for (int i = 0; i < spotlights.Count; i++) {
if (getPrefixBool(spotlights[i].CustomName)) {
spotlights[i].GetActionWithName("OnOff_Off").Apply(spotlights[i]);
}
}
}
}
bool getPrefixBool (string name) {
if (usePrefix) {
if (name.StartsWith(prefix)) {
return true;
} else return false;
} else return true;
}
I haven't tried much at this point as I can't exactly code... I have found a couple lines but they don't seem to work.

Switch other toggles when one of it is on

I have an array of toggles defined in script. They are all turned off in the beginning. When the user clicks on one of the toggles, that toggle should be turned on and the other toggles should be switched to off state. Basically there could be only "one" on state toggle. How do I achieve this?
Currently with this script, all the toggles are getting turned off when the user clicks on one of it.
public Toggle[] toggle;
void Start () {
for (int i = 0; i < toggle.Length; i++) {
int idx = i;
toggle[idx].onValueChanged.AddListener (delegate {
ToggleValueChanged (toggle[idx], idx);
});
}
}
public void ToggleValueChanged (Toggle change, int index) {
for (int i = 0; i < toggle.Length; i++) {
if (i == index) {
return;
} else {
if (toggle[i].isOn) {
toggle[i].isOn = false;
}
}
}
}
Unity has a component called ToggleGroup that allow you to do just that.
ToggleContainer Parent Object:
Reference ToggleGroup object in every Toggle component as following:
Scene Hierarchy:
Overview:
https://gfycat.com/bossyscenteddorado
Change your ToggleValueChanged function like this :
public void ToggleValueChanged (Toggle change, int index)
{
for (int i = 0; i < toggle.Length; i++)
{
if (i == index) continue;
if (toggle[i].isOn) {
toggle[i].isOn = false;
}
}
}
When you return in you first if statement, other toggles won't get off. you have to continue iterating your loop.
And instead getting the index and passing it to the delegate, you can use RefrenceEqual
EDIT
Actually each time you manipulate the toggle[i].isOn, you are changing the value of it. So each time, You are calling your function.
EDIT
try this :
public void ToggleValueChanged (Toggle change, int index)
{
for (int i = 0; i < toggle.Length; i++)
{
if (i == index) continue;
if (toggle[i].isOn)
{
toggle[i].SetIsOnWithoutNotify(false);
}
}
}
Why do you even need the index for this?
Simply do
public void ToggleValueChanged (Toggle change)
{
// add a little safety to actually only disable all others if
// this one ws actually enabled
if(!change.isOn) return;
foreach(var toggle in toggles)
{
if (toggle == change) continue;
if (toggle.isOn)
{
// I would actually specifically NOT use "SetIsOnWithoutNotify"
// because you never know who else is actually listening to the state change
// so if you simply set it to false but don't invoke the callback events
// things might behave different than expected
toggle[i].isOn = false;
}
}
}
and accordingly
foreach(var t in toggle)
{
var currentToggle = t;
currentToggle .onValueChanged.AddListener(value => ToggleValueChanged(currentToggle));
}
There is no need to either return or continue, just don't handle the toggle if it's the indexth one:
public void ToggleValueChanged (Toggle change, int index)
{
for (int i = 0; i < toggle.Length; i++)
{
if (i != index)
{
toggle[i].isOn = false;
}
}
}
Also you can assume that all others are off, so no reason to check that either.
If they are mutually exclusive, they aren't "boolean states", it's just one state, supported by an enum and including the null value. If your UI is a set of checkboxes, you should switch it to a set of radio buttons. A dropdown box would do as well.

Simulate CTRL down until I desire in c#?

I'm trying to simulate a user pressing ctrl down, the main goal would be in a datagridview when I select something programarly (initially) I dont want the user to then change that selection if not just add on to it or subtract, just as if you were to hold ctrl + left mouse click. I have no idea where to even begin. I tried to create a selection change event conbined with logicals but that will cause an infinite loop since we would be selecting one by a user then the code change other and other etc infinitely triggering that event. Please help, I'm sort of new to coding. I also don't know how to determine whether a ctrl key has been pressed, is pressed and being held.
private void selecttionh(object sender, EventArgs e)
{
if (stage == "4A" || stage == "3B" && ModifierKeys.HasFlag(Keys.Control))
{
int nothing = 0;
btnclickercl bt = new btnclickercl();
bt.dataGridView1_SelectionChanged(sender, e, dataGridViewReslist, dataGridViewnewres, nothing);
}
if (stage == "4A" || stage == "3B" && (ModifierKeys & Keys.Control) != Keys.Control)
{
MessageBox.Show("Please Press and hold " + "'ctrl'" + " to continue");
dataGridViewReslist.ClearSelection();
for (int i = 0; i < ResRoomSelections.Count; i++)
{
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[ResRoomSelections[i][1]].Selected = true;
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[(ResRoomSelections[i][1]) + 1].Selected = true;
}
}
else
{
dataGridViewReslist.ClearSelection();
for (int i = 0; i < ResRoomSelections.Count; i++)
{
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[ResRoomSelections[i][1]].Selected = true;
dataGridViewReslist.Rows[ResRoomSelections[i][0]].Cells[(ResRoomSelections[i][1]) + 1].Selected = true;
}
}
}
The way to make this happen is to store the selection state separately, update it when the user clicks a cell, then re-apply it. This prevents the selection from being lost every time they click. If you hook the proper event handlers (mouseup, not click) you can do this without the screen flickering and otherwise being a mess to look at.
Everything you need is in this class, including an extension method SetupToggledSelectionMode(), which is your entry point.
static public class Example
{
static private bool[][] GetSelectionState(DataGridView input)
{
int rowCount = input.Rows.Count;
int columnCount = input.Columns.Count;
var result = new bool[rowCount][];
for (var r = 0; r < rowCount; r++)
{
result[r] = new bool[columnCount];
for (var c = 0; c < columnCount; c++)
{
var cell = input.Rows[r].Cells[c];
result[r][c] = cell.Selected;
}
}
return result;
}
static private void SetSelectionState(DataGridView input, bool[][] selectionState)
{
for (int r = 0; r <= selectionState.GetUpperBound(0); r++)
{
for (int c = 0; c <= selectionState[r].GetUpperBound(0); c++)
{
input.Rows[r].Cells[c].Selected = selectionState[r][c];
}
}
}
static public void SetupToggledSelectionMode(this DataGridView input)
{
bool[][] selectionState = GetSelectionState(input); //This will be stored in a closure due to the lambda expressions below
input.CellMouseUp += (object sender, DataGridViewCellMouseEventArgs e) =>
{
selectionState[e.RowIndex][e.ColumnIndex] = !selectionState[e.RowIndex][e.ColumnIndex];
SetSelectionState(input, selectionState);
};
input.SelectionChanged += (object sender, EventArgs e) =>
{
if (selectionState != null)
{
SetSelectionState(input, selectionState);
}
};
}
}
To use, populate your gridview, set up the initial selection programmatically, and call it like this:
myDataGrid.DataSource = myData;
myDataGrid.Refresh();
myDataGrid.SelectAll();
myDataGrid.SetupToggledSelectionMode();
The SetupToggledSelectionMode() method will register the necessary event handlers and store the selection state of the grid in a closed variable accessible to both handlers. So you won't have to declare anything additional; just call the method.
Thank you for this, this really helped me. all I did was to make it more efficient.Since it would call the Selection change every step of the way, so I got rid of that event completely and only kept the CellMouseup Event.
static private bool[][] GetSelectionState(DataGridView input)
{
int rowCount = input.Rows.Count;
int columnCount = input.Columns.Count;
var result = new List<int[]>();
for (var r = 0; r < rowCount; r++)
{
for (var c = 0; c < columnCount; c++)
{
if(input.Rows[r].Cells[c].Selected==true)
{
result.add(new int[]{r,c});//will keep only the integer of selected items
}
}
}
return result;//this for me was a recycled variable it can be used or recycled from somewhere else
}
private void SetSelectionState(DataGridView input,result)
{
for (int i=0;i<result.Count;i++)
{
input.Rows[result[i][0]].Cells[result[i][1]].Selected = true;
}
}
public void SetupToggledSelectionMode(DataGridView input,result)
{
for (int i=0;i<result.Count;i++)
{
if(result[i].SequenceEqual(new int[] { e.RowIndex, e.ColumnIndex }))
{
result.RemoveAt(i);
continueer = 1;
break;
}
}
if (continueer == 0)
{
ResRoomSelections.Add(new int[] { e.RowIndex, e.ColumnIndex });
}
SetSelectionState(input);
//whatever else you need to do
}
I know there is still a better way to search List but I could not get a lamda search to work so I just used brute force
-Thank you John Wu for all

Creating a function to be reusable multiple times in my code

I want to be able to call the following function multiple times through out my code to fill different groups of 8 text boxes in my form.
Right now reference is being passed in "tbPlay" from where it is being called initially in the code.
Each time this function will be called it will be to fill different text box groups.
I am trying to think of a way of using the empty for loop to create the necessary variable names to replace tbPlay0-7 in my case statement, so it isn't only usable for one group of text boxes in my code. I am not sure it can be done.
Can anyone help.
private void convertBasetoDrawn(string numBase, string reference)
{
string baseNumber = numBase;
for (int i = 0; i < 8; i++)
{
//some code here to create variables to replace the text box names in the
//following case statement
}
switch (baseNumber)
{
case "000":
tbPlay0.Text = "000";
tbPlay0.ForeColor = Color.Red;
tbPlay1.Text = "500";
tbPlay2.Text = "050";
tbPlay3.Text = "005";
tbPlay4.Text = "550";
tbPlay5.Text = "505";
tbPlay6.Text = "055";
tbPlay7.Text = "555";
tbPlay7.ForeColor = Color.Red;
break;
}
}
Create a List<TextBox> for each group:
List<TextBox> list01 = new List<TextBox>() { tbPlay0, tbPlay1, ....};
List<TextBox> list02 = new List<TextBox>() { ..., ... , ....};
// ..
}
And pass such a group to the function:
private void convertBasetoDrawn(List<TextBox> list, string numBase, string reference)
{
string[] texts = new string[8]
{ "000", "500", "050", "005", "550", "505", "055", "555" };
for (int t = 0; t < list.Count; t++) list[t].Text = texts[t];
list[0].ForeColor = Color.Red;
list[7].ForeColor = Color.Red;
}
Assuming the texts will always look like that. If they depend on, maybe numbase you can construct them dynamically as well, as long as you know the rules.. Maybe even a simple replacement will do the job?
You didn't use reference, btw..
Now, I'm just guessig here, but maybe this is the pattern for your texts..:
string[] texts = new string[8]
{ "nnn", "dnn", "ndn", "nnd", "ddn", "dnd", "ndd", "ddd" };
for (int s = 0; s < texts.Length; s++)
texts[s] = texts[s].Replace("d", numBase).Replace("n", reference);
Now you can call it like this:
convertBasetoDrawn(list01, "0","5");
Update: For the rules as I understand them now you could do:
string newText = "";
for (int s = 0; s < texts.Length; s++)
{
newText = "";
for (int i = 0; i < 3; i++)
{
if (texts[s][i] == 'n') newText += numBase[i];
else newText += (Convert.ToByte(numBase[i].ToString()) +
Convert.ToByte(reference[0].ToString()) ).ToString("0");
}
texts[s] = newText;
}
and call it like this:
convertBasetoDrawn(list01, "001", "5");
or
convertBasetoDrawn(list02, "000", "1");
Note: no carry over here.. You'd have to define rules for that and code it yourself..
It's not clear how you plan to identify the specific group of eight. But let's assume you have somehow.
Then, if I were writing this code, I would use a UserControl to encapsulate the repeated pattern, exposing the eight TextBox controls — or rather, the properties of them that you want access to — as properties. E.g.
class TextBoxGroup : UserControl
{
public string Text1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
public Color ForeColor1
{
get { return textBox1.ForeColor; }
set { textBox1.ForeColor = value; }
}
public string Text2
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
public Color ForeColor2
{
get { return textBox2.ForeColor; }
set { textBox2.ForeColor = value; }
}
// ...
public string Text8
{
get { return textBox8.Text; }
set { textBox8.Text = value; }
}
public Color ForeColor8
{
get { return textBox8.ForeColor; }
set { textBox8.ForeColor = value; }
}
}
Then in your method, rather than whatever logic you planned on using to figure the starting index for your group, instead you just retrieve the appropriate TextBoxGroup instance and use it in the switch, like this:
case "000":
textBoxGroup.Text1 = "000";
textBoxGroup.ForeColor1 = Color.Red;
textBoxGroup.Text2 = "500";
textBoxGroup.Text3 = "050";
textBoxGroup.Text4 = "005";
textBoxGroup.Text5 = "550";
textBoxGroup.Text6 = "505";
textBoxGroup.Text7 = "055";
textBoxGroup.Text8 = "555";
textBoxGroup.ForeColor8 = Color.Red;
break;
A variation on the above would encapsulate the properties with setter methods taking an index. E.g.
class TextBoxGroup : UserControl
{
// Initialized in constructor to be the eight TextBoxes
private TextBox[] _textboxes;
public void SetText(int i, string text)
{
_textboxes[i].Text = text;
}
}
Of course, if you don't want to use a UserControl, you could just initialize a similar data structure in the main form instead, so that the controls can be accessed by index. But personally, I'd prefer the UserControl as it makes it easier to reuse and ensure consistency across all the groups of TextBox controls.

List order (or iteration) malfunction

Hello I am having trouble with an iteration through a list of 17 labels:
for (int i = 0; i < labels.Count - 1; i++)
{
MessageBox.Show(labels[i].Name);
if (labels[i].Visible == false && labels[i + 1].Visible == true)
{
...
Here are the results I get:
First it goes from label10 to label17, and then in descending order from label9 to label2.
Here is how I add the labels to the list:
private void newGameToolStripMenuItem_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
if (c is Label)
{
labels.Add(c);
c.Enabled = true;
if (c.Visible == false)
{
c.Visible = true;
}
}
}
}
I want it to go from label1 to label16, since the loop is just a loop I guess the problem lies in the order in which the labels were added to the list, but I am not sure how to fix it.
Your main problem is lexicographic order which is inherently used when you sort by Name of the label, what you want is to sort by numbers after the term label. In that case, first sort the labels list and then run the for statement over it, check the code:
var lst = labels.OrderBy(x => int.Parse(x.Name.Substring("label".Length))).ToList();
for (int i = 0; i < lst.Count - 1; i++)
{
MessageBox.Show(lst[i].Name);
...
But have in mind that this code is simple and presumes that label Name property always starts with "label" string. If that can change you must handle that case.
I guess you want to sort the labels according to their names?
labels.Sort((x, y) => { return x.Name.CompareTo(y.Name); });
but what are the difference between:
Show "Label 1" first, then "Label 2", and
Show "Label 2" first, then "Label 1"?
Check the designer.cs file to see in which order the labels are added to the Form
assuming that you have Labels id as Label1,Label2..........,Label16
in order to get the labels serially you have to write the following code
labels = labels.ConvertAll<Control>(GetIdFromLabel);
labels.Sort((x, y) => { return x.Id.CompareTo(y.Id); });
public Control GetIdFromLabel(Control c)
{
c.Id = c.Name.Replace("Label", "") == "" ? 0 : Convert.ToInt32(c.Name.Replace("Label", ""));
return c;
}
add this class in your code also
public class Control
{
public string Name { get; set; }
public int Id { get; set; }
}
Try this out:
private void newGameToolStripMenuItem_Click(object sender, EventArgs e)
{
labels.Clear();
Control[] matches;
for (int i = 1; i <= 16; i++)
{
matches = this.Controls.Find("label" + i.ToString(), true);
if (matches.Length > 0 && matches[0] is Label)
{
Label lbl = (Label)matches[0];
labels.Add(lbl);
lbl.Enabled = true;
if (lbl.Visible == false)
{
lbl.Visible = true;
}
}
}
}

Categories

Resources