Simulate CTRL down until I desire in c#? - 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

Related

Optimize c# foreach complexity

Alright so I`m doing a particular case here and I need to pass through a treeview with many foreaches.
After realising my code is not working properly in memory wise I decided to rewrite the code to LINQ. I have managed to get rid of the first foreaches but at the end, I cannot get rid of the signalUI foreach because it needs the Typeof(SignalUI) properties. These being said I don`t know how to do the LINQ for 2 lists at the same time. I have tried working with JOINS but had no results.
private void signalUITextBox_textchanged(object sender, EventArgs e)
{
for (int j = 0; j < _numberOfSelectedSlaves; j++)
if (_currentNode.Parent.Parent.Text == mas.Slaves[j].Name)
{
int counter = 0;
foreach (FrameUI frame in mas.Slaves[j].FrameList)
{
int signalcounter = 0;
if(_currentNode.Text == mas.Slaves[j].FrameList[counter].FrameName)
foreach (SignalUI signalui in frame.SignalList)
{
foreach (Control i in this.Controls)
{
if (i is TextBox)
{
var txtbox = (TextBox)sender;
if (txtbox.Name == i.Name)
{
foreach (var k in typeof(SignalUI).GetProperties())
if (signalui.SignalName + k.Name == txtbox.Name)
{
mas.Slaves[j].FrameList[counter].SignalList[signalcounter].SetProperty(k.Name, Convert.ToString(txtbox.Text));
}
}
}
}
signalcounter++;
}
counter++;
}
}
}
private void signalUCheckBox_textchanged(object sender, EventArgs e)
{
var SlaveQuery = mas.Slaves.Where(slave => slave.Name == _currentNode.Parent.Parent.Text).First();
var FrameQuery = SlaveQuery.FrameList.Where(frame => frame.FrameName == _currentNode.Text).First();
var chkbox = (CheckBox)sender;
var ControlQuery = this.Controls.OfType<CheckBox>().Where(control => control.Name == chkbox.Name).First();
}
EDIT: Thanks for help. Following below answers i managed to remove all the nested loops
private void signalUITextBox_textchanged(object sender, EventArgs e)
{
int j;
int signalcounter = 0;
int counter = 0;
var txtbox = (TextBox)sender;
var properties = typeof(SignalUI).GetProperties();
var SlaveQuery = mas.Slaves.Where(slave => slave.Name == _currentNode.Parent.Parent.Text).First();
var FrameQuery = SlaveQuery.FrameList.Where(frame => frame.FrameName == _currentNode.Text).First();
for (j = 0; j < _numberOfSelectedSlaves; j++)
if (_currentNode.Parent.Parent.Text == mas.Slaves[j].Name)
break;
foreach (FrameUI frame in mas.Slaves[j].FrameList)
{
if (_currentNode.Text == mas.Slaves[j].FrameList[counter].FrameName)
break;
counter++;
}
foreach (SignalUI signalui in FrameQuery.SignalList)
{
var prop = properties.FirstOrDefault(p => signalui.SignalName + p.Name == txtbox.Name);
if (prop != null)
{
mas.Slaves[j].FrameList[counter].SignalList[signalcounter].SetProperty(prop.Name, Convert.ToString(txtbox.Text));
}
signalcounter++;
}
}
As a general rule: Pull repeated calculations that always yield the same result within each loop iteration out of the body of the loop and to them once. The structure of your code will simplify, declutter, and you effectively reduce its inherent complexity.
The outline of your code will look like this:
private void signalUITextBox_textchanged(object sender, EventArgs e)
{
// build a list of textboxes, but…
// in fact, you don't need this at all if you care only of 'sender' as a TextBox
var textBoxes = this.Controls.OfType<TextBox>().ToList();
// hence:
if (!(sender is TextBox)) return;
var txtbox = (textBoxe) sender;
// build a list of properties (hint: reduce the list further, if you can)
var properties = typeof(SignalUI).GetProperties();
// in addition, a dictionary may come handy: .ToDictionary(p => p.Name);
// the rest of the code seems cluttered and unnecessarily complex, too,
// but hard to refactor without detailed knowledge of your data structures
for (int j = 0; j < _numberOfSelectedSlaves; j++)
{
if (_currentNode.Parent.Parent.Text == mas.Slaves[j].Name)
{
int counter = 0;
foreach (FrameUI frame in mas.Slaves[j].FrameList)
{
int signalcounter = 0;
if (_currentNode.Text == mas.Slaves[j].FrameList[counter].FrameName)
foreach (SignalUI signalui in frame.SignalList)
{
// find the matching property
var prop = properties.FirstOrDefault(p => signalui.SignalName + p.Name == txtbox.Name);
if (prop != null)
{
mas.Slaves[j].FrameList[counter].SignalList[signalcounter].SetProperty(prop.Name, Convert.ToString(txtbox.Text));
}
signalcounter++;
}
counter++;
}
}
}
}
Three nested loops less.

How to check range in checkedlistbox c# winforms?

I have a checkeboxlist with 100 items. Obviously user can check items one by one as many as he need, but I would like to give to user option check range of items (let's say with Shift hold button). So, user check one of the items (let's say item index 5) and then press and hold shift button and check next item (index 10), so I range of the items should be checked from 5...10
I have not found anything about such implementation, looks like it doesn't exist and no one did such kind of things.
How to do it?
Keep track of your last index:
int lastIndex = -1;
In your form's constructor, wire things up:
public Form1() {
InitializeComponent();
checkedListBox1.CheckOnClick = true;
checkedListBox1.SelectedIndexChanged += CheckedListBox1_SelectedIndexChanged;
checkedListBox1.MouseDown += CheckedListBox1_MouseDown;
}
And then use these methods to change the items in the range:
private void CheckedListBox1_SelectedIndexChanged(object sender, EventArgs e) {
lastIndex = checkedListBox1.SelectedIndex;
}
private void CheckedListBox1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Shift) {
var useIndex = Math.Max(lastIndex, 0);
var x = checkedListBox1.IndexFromPoint(e.Location);
if (x > -1 && x != useIndex) {
if (useIndex > x) {
for (int i = useIndex - 1; i > x; i--) {
checkedListBox1.SetItemChecked(i, !checkedListBox1.GetItemChecked(i));
}
} else {
for (int i = useIndex + 1; i < x; i++) {
checkedListBox1.SetItemChecked(i, !checkedListBox1.GetItemChecked(i));
}
}
}
}
}

datagrid get selected rows and cells values in wpf c#

I would like to redo the code of my old windows forms application on wpf and I have a problem with referencing datagridview.
This is the void look of my old application:
private void button2_Click(object sender, EventArgs e)
{
if (DGV1.Rows.Count > 0 && DGV1.SelectedRows != null)
{
bool wart = true;
for (int i = 0; i < listBox2.Items.Count; i++)
{
listBox2.SelectedIndex = i;
int w1 = Int32.Parse(listBox2.SelectedItem.ToString());
int w2 = Int32.Parse(DGV1.SelectedRows[0].Cells[0].Value.ToString());
if (w1 == w2)
{
wart = false;
break;
}
}
if (wart)
{
listBox2.Items.Add(DGV1.SelectedRows[0].Cells[0].Value);
}
}
}
This is the void look of my new application:
private void Button1_Click(object sender, RoutedEventArgs e)
{
IList rows = dataGrid1.SelectedItems;
if(dataGrid1.SelectedItem != null)
{
bool wart = true;
for (int i =0; i < listBox1.Items.Count; i++)
{
listBox1.SelectedIndex = i;
object item = dataGrid1.SelectedItem;
int w1 = Int32.Parse(listBox1.SelectedItem.ToString());
int w2 = Int32.Parse(dataGrid1.SelectedCells[0].Column.GetCellContent(item).ToString()); <--- !!
if(w1 == w2)
{
wart = false;
break;
}
}
if(wart)
{
listBox1.Items.Add(dataGrid1.SelectedCells[0]); <-- !!
}
}
}
The application spills out at the second if, where it displays:
And it should be:
Please Help :-)
It should probably be like this:
listBox1.Items.Add(dataGrid1.CurrentRow.Cells[0].Value);
This code is from WinForms, but I assume the coding for wpf may not be different, since both are in c#.
dataGrid1.SelectedItem isn't just some object, it has concrete type and properties like Id, Tytul, Kategorie, Text
you need to make a cast to that concrete type and access property instead of trying to get the value from low-level UI elements like DataGridCellInfo:
var item = (MyConcreteClass)dataGrid1.SelectedItem;
int w2 = item.Id;

how to get next row data in gridview?

i want to get next data in gridview, the thing is when i use break; it totally out of loop, but when i dont use break; it otomatically call the last row data of the text that i type.
private void button7_Click(object sender, EventArgs e)
{
for (int i = 0; i < gridView1.RowCount; i++)
{
var row = gridView1.GetDataRow(i);
var genre = row["genre"].ToString();
if (genre.IndexOf(textBox8.Text, StringComparison.OrdinalIgnoreCase) >= 0)
{
gridView1.FocusedRowHandle = i;
break;
}
}
}
if i dont use break it will select the last data with 'ba' = basketball.
what i want is when i click next it select ballet and stop, and when i click next button again it select basketball
use an int variable to store the current iteration of loop:
int iteration=0;
private void button7_Click(object sender, EventArgs e)
{
for (int i = iteration; i < gridView1.RowCount; i++)
{
var row = gridView1.GetDataRow(i);
var genre = row["genre"].ToString();
if (genre.IndexOf(textBox8.Text, StringComparison.OrdinalIgnoreCase) >= 0)
{
gridView1.FocusedRowHandle = i;
iteration=i+1;
break;
}
}
}
And on Find button you can reset iteration to 0

C# reassigning array values(down one step ex 5-4) that are within an object during a event

Just learning C# (along with object and event programing) and the teacher didn't really show us how to get things done.
class Postion
{
private int[] x_coordinate = new int[100];
private int[] y_coordinate = new int[100];
private double[] speed = new double[100];
private int[] direction = new int[100];
const int MAX_SPEED = 50;
int counter = 0;
public Postion()
{
x_coordinate[counter] = 0;
y_coordinate[counter] = 0;
speed[counter] = 0;
direction[counter] = 0;
}
//get set methods
public int X
{
get
{
return x_coordinate[counter];
}
set
{
x_coordinate[counter] = value;
}
}
There is one more Class between them
The values are frist assigned by a button click.
Airplane newplane = new Airplane();
private void BtnCreate_Click(object sender, EventArgs e)
{
bool box = txtName.Text != "";
if (box == true)
newplane.Name = txtName.Text;
else { }
box = txtx.Text != "";
if (box == true)
newplane.PlanePostion.X = int.Parse(txtx.Text);
else { }
Etc.
I can call on the array values for display for the list box.
private void lsbplanes_SelectedIndexChanged(object sender, EventArgs e)
{
placeholder = newplane.PlanePostion.Counter;
newplane.PlanePostion.Counter = lsbplanes.SelectedIndex;
if (newplane.PlanePostion.Counter < 0)
newplane.PlanePostion.Counter = 0;
else { }
lblxshow.Text = Convert.ToString(newplane.Getx());
but when using a destroy button to remove an item in the list box I need to have it so the box updates with the new values when the user selects the item in the listbox.
This is what I have to try and do it so far, it sets all the ones above to 0s but does remove the the deleted one fine
private void BtnKill_Click(object sender, EventArgs e)
{
if (lsbplanes.SelectedIndex == -1)
{
MessageBox.Show("Please select an item first.", "No item selected", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
placeholder = lsbplanes.SelectedIndex;
newplane.PlanePostion.Counter = lsbplanes.Items.Count;
while (newplane.PlanePostion.Counter > placeholder)
{
placex = newplane.PlanePostion.X;
placey = newplane.PlanePostion.Y;
placespeed = newplane.Getspeed();
placedic = newplane.Getdirection();
newplane.PlanePostion.Counter--;
newplane.PlanePostion.X = placex;
newplane.PlanePostion.Y = placey;
newplane.PlanePostion.Speed = placespeed;
newplane.PlanePostion.Direction = placedic;
}
lsbplanes.Items.RemoveAt(lsbplanes.SelectedIndex);
newplane.PlanePostion.Counter = lsbplanes.Items.Count;
}
anyone can help me on this?
I was torn in this question, answer exactly what your problem is, or suggest that you redesign it.
#Marc is right you should be using some sort of List<Position> on your Plane object (or a ReadOnlyObservableCollection<Position>).
#Marc is also right, that the problem you are having is that you are trying to push the values down from the end of the list and overwriting them. In these cases it is better to start from the deletion point and pull them down.
So if you have {1,2,3,4,5,6,7,8,9,10} and you delete from item 5, you would have {1,2,3,4,10,10,10,10,10,10}. The code below will let you end up with {1,2,3,4,6,7,8,9,0}
placeholder = lsbplanes.SelectedIndex;
int idx = placeholder;
while (idx < lsbplanes.Items.Count)
{
newplane.PlanePosition.Counter = idx+1;
placex = newplane.PlanePostion.X;
placey = newplane.PlanePostion.Y;
placespeed = newplane.Getspeed();
placedic = newplane.Getdirection();
newplane.PlanePostion.Counter = idx;
newplane.PlanePostion.X = placex;
newplane.PlanePostion.Y = placey;
newplane.PlanePostion.Speed = placespeed;
newplane.PlanePostion.Direction = placedic;
idx++;
}
// Need to zero out elements at the end
newplant.PlanePosition.Counter = lsbplanes.Items.Count;
/* Zeroing code goes here */
newplane.PlanePosition.Counter = placeholder;
lsbplanes.Items.RemoveAt(lsbplanes.SelectedIndex);

Categories

Resources