I am having troubles with datagridview when I want to have data partialy loaded and load next/previous data by scrolling (putting scrollbar with mouse to limit positions also).
This topic is much discussed, but mostly suggested answer "adding dataGridView1.PerformLayout()" does not works for me (Datagridview error : Value' should be between 'minimum' and 'maximum').
Due to this, I have created simple test form only with datagridview, code:
using System;
using System.Linq;
using System.Windows.Forms;
namespace DataGridViewTester
{
public delegate void DeleteTableDelegate(DataGridView table);
public delegate void FillRowsDelegate(DataGridView table, int rowsToAdd);
public partial class Form1 : Form
{
VScrollBar vScrollbar;
int index = 0;
public Form1()
{
InitializeComponent();
vScrollbar = dataGridView1.Controls.OfType<VScrollBar>().First();
vScrollbar.ValueChanged += new EventHandler(this.scrollbarValue_Changed);
AppendRange(dataGridView1, 100);
}
public void ClearTableContents(DataGridView table)
{
//For calling from different threads
if (table.InvokeRequired)
{
var d = new DeleteTableDelegate(ClearTableContents);
table.Invoke(d, new object[] { table });
return;
}
try
{
table?.Rows.Clear();
}
catch (InvalidOperationException ex) {
Console.WriteLine(ex);
}
table?.PerformLayout();
}
public void AppendRange(DataGridView table, int rowsToAdd)
{
if (table.InvokeRequired)
{
var d = new FillRowsDelegate(AppendRange);
table.Invoke(d, new object[] { table, rowsToAdd });
return;
}
Console.WriteLine(rowsToAdd);
DataGridViewRow[] dataGridViewRows = new DataGridViewRow[(rowsToAdd < 0 ? -rowsToAdd : rowsToAdd)];
index = (rowsToAdd >= 0) ? (index ) : (index - 2*rowsToAdd);
for (int i = 0; i < dataGridViewRows.Length; i++)
{
dataGridViewRows[i] = new DataGridViewRow();
dataGridViewRows[i].CreateCells(dataGridView1, new object[] { index + i, dataGridViewRows[i].GetHashCode() });
}
index = (rowsToAdd >= 0) ? (index+ rowsToAdd) : (index);
table?.Rows.AddRange(dataGridViewRows);
table?.PerformLayout();
}
private void scrollbarValue_Changed(Object sender, EventArgs e)
{
if (vScrollbar.Visible)
{
if (vScrollbar.Value + (vScrollbar.LargeChange - 1) >= vScrollbar.Maximum)
{
System.Console.WriteLine("Get NEXT results " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
try
{
ClearTableContents(dataGridView1);
Console.WriteLine("CLEARED " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
AppendRange(dataGridView1, 1); //Append it to Grid
Console.WriteLine("APENDED " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
}
catch (System.ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex);
}
System.Console.WriteLine(vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
}
else if (vScrollbar.Value <= vScrollbar.Minimum)
{
System.Console.WriteLine("Get PREVIOUS results " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
try
{
ClearTableContents(dataGridView1);
Console.WriteLine("CLEARED " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
AppendRange(dataGridView1, -1); //Append it to Grid
Console.WriteLine("APENDED " + vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
dataGridView1?.PerformLayout();
}
catch (System.ArgumentOutOfRangeException ex)
{
Console.WriteLine(ex);
}
System.Console.WriteLine(vScrollbar.Maximum + ">=" + vScrollbar.Value + ">=" + vScrollbar.Minimum + " Large change -1 = " + (vScrollbar.LargeChange - 1));
}
}
}
}
}
Thank you for your answers.
EDIT: This behaviour is caused holding scrollbar by mouse. If this paging is mapped to PageDown and PageUp buttons, there are no errors.
I was trying to show texts 50 times on the textbox, but it just show only once.
(which is the last time(50th) text)
enter image description here
'''
int x = 0;
int y = 0;
int counting = 0;
for(x = 0; x < 10; x++)
{
for(y = 0; y < 5; y++)
{
counting++;
richTextBox1.Text = "TEST" + " " + x.ToString() + " " + y.ToString() + "\r\n";
}
}
textBox1.Text = counting.ToString();
'''
The 'counting' works great, but "TEST" is just shows one time.
What should I have to do if I can make 50 texts?
Thank you.
You need to Change the line
richTextBox1.Text = "TEST" + " " + x.ToString() + " " + y.ToString() + "\r\n";
to 1 of 2 things:
richTextBox1.Text += "TEST" + " " + x.ToString() + " " + y.ToString() + "\r\n";
Or
richTextBox1.Text =richTextBox1.Text + "TEST" + " " + x.ToString() + " " + y.ToString() + "\r\n";
That Will append the new text to the existing.
You are setting a value to the text box 50 times, not building up the value as you set it. If you want to show all 50 values, you'll need to concatenate them:
int x = 0;
int y = 0;
int counting = 0;
# Start with an empty text box
richTextBox1.Text = "";
for(x = 0; x < 10; x++)
{
for(y = 0; y < 5; y++)
{
counting++;
richTextBox1.Text += "TEST" + " " + x.ToString() + " " + y.ToString() + "\r\n";
}
}
textBox1.Text = counting.ToString();```
I have a list of items that are determined by what a player enters into several input fields, but for the sake of this example, let's say that it contains "a", "b", "c", "d", and "e". These are then sorted into a dictionary with a list of numbers as the values (unimportant). I then randomize the dictionary with two different randomizer variables (i and j), for the sake of taking two objects from the dictionary and displaying them to the screen, so the player can press various associated buttons. This goes on until x amount of turns have passed. The main problem I'm having is the prevention of semi-duplicates, such as "a b" and "b a" from appearing.
I have tried entering inserting both the randomized pair and its semi-duplicate into another dictionary and then using while loop statements preventing any pairs from that dictionary from appearing. Unfortunately, this hasn't worked.
Below is my code.
public void Start() {
finalList = new Dictionary<string, int>();
for (index = 0; index < allNumbers; index++)
{
finalList.Add(itemList[index], valueList[index]);
Debug.Log(finalList[index.ToString()]);
}
}
public void Update() {
choose();
}
public void choose() {
duplicates = new Dictionary<string, string>();
duplicates.Clear();
while (rounds < insertNum) {
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
while (j == i || (duplicates.ContainsKey(key) || duplicates.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
//break;
} while (j == i || (duplicates.ContainsKey(key) && duplicates.ContainsKey(reverseKey)))
{
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}while (j == i && (duplicates.ContainsKey(key) || dupes.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}
while (j == i && (duplicates.ContainsKey(key) && duplicates.ContainsKey(reverseKey))) {
i = UnityEngine.Random.Range(0, allNumbers - 1);
j = UnityEngine.Random.Range(0, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
reverseKey = "(" + itemList[j].ToString() + " " + itemList[i].ToString() + ")";
Debug.Log("(new keys " + key + ", " + reverseKey + ")");
}
duplicates.Add(key, "1"); // the one is just a filler variable
duplicates.Add(reverseKey, "1");
if (buttonOneBool) { //this is in another script, ignore
finalList[itemList[i].ToString()] = valueList[i] += 2;
finalList[itemList[j].ToString()] = valueList[j] -= 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonTwoBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 2;
finalList[itemList[j].ToString()] = valueList[j] += 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonThreeBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 1;
finalList[itemList[j].ToString()] = valueList[j] -= 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonFourBool) {
finalList[itemList[i].ToString()] = valueList[i] += 1;
finalList[itemList[j].ToString()] = valueList[j] += 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
}
break;
}
The simplest way to fix this is to guarantee i < j. When selecting a new i and j like this:
i = UnityEngine.Random.Range(min, max);
j = UnityEngine.Random.Range(min, max);
Instead do this:
i = UnityEngine.Random.Range(min, max - 1);
j = UnityEngine.Random.Range(i + 1, max);
Doing this rules out the possibility of selecting a "mirror image" of a previous case and also avoids the "i == j" case.
After these modifications your choose() function should look something like this:
public void choose()
{
duplicates = new HashSet<string>();
while (rounds < insertNum)
{
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
while (duplicates.Contains(key))
{
i = UnityEngine.Random.Range(0, allNumbers - 2);
j = UnityEngine.Random.Range(i + 1, allNumbers - 1);
key = "(" + itemList[i].ToString() + " " + itemList[j].ToString() + ")";
}
duplicates.Add(key); // the one is just a filler variable
if (buttonOneBool) { //bool definitions are in another script, ignore
finalList[itemList[i].ToString()] = valueList[i] += 2;
finalList[itemList[j].ToString()] = valueList[j] -= 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonTwoBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 2;
finalList[itemList[j].ToString()] = valueList[j] += 2;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonThreeBool) {
finalList[itemList[i].ToString()] = valueList[i] -= 1;
finalList[itemList[j].ToString()] = valueList[j] -= 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
} else if (buttonFourBool) {
finalList[itemList[i].ToString()] = valueList[i] += 1;
finalList[itemList[j].ToString()] = valueList[j] += 1;
i = UnityEngine.Random.Range(0, n - 1);
j = UnityEngine.Random.Range(0, n - 1);
}
break;
}
}
You can get two random indexes to a list using code below. The code uses integer as the list type and will work with any type.
List<int> randomList = itemList.Select((x, i) => new { index = i, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.index).ToList();
Then the two random indexes into the list are randomList[0] and randomList[1]. The code assigns a random number to each index i of the list. To get the two items from the list use itemList[randomList[0]] and itemList[randomList[1]]. The code assumes there are at least two items in the original list ItemList[].
If your original list has duplicates than you need to use Distinct() as shown in code below
List<int> distinctList = itemList.Distinct().ToList();
List<int> randomList = distinctList.Select((x, i) => new { index = i, rand = rand.Next() }).OrderBy(x => x.rand).Select(x => x.index).ToList();
I need to color each line depends on the last char in each string form list. This is my code and it's always make the last line green. What's wrong with it?
List<string> plik = File.ReadAllLines(path).ToList();
string pom;
int size = plik.Count;
richTextBox1.Clear();
for (int i = 0; i < size; i++)
{
richTextBox1.Text += "[" + i.ToString() + "]" + " " + plik[i] + Environment.NewLine;
pom =plik[i];
richTextBox1.Select(richTextBox1.GetFirstCharIndexFromLine(i), richTextBox1.Lines[i].Length);
// richTextBox1.Select(0, pom.Length);
if (pom.Substring(pom.Length - 1) == "n")
{
richTextBox1.SelectionBackColor = pom.Substring(pom.Length - 1) == "n" ? Color.Red :Color.Red;
}
if(pom.Substring(pom.Length - 1) != "n")
{
richTextBox1.SelectionBackColor = pom.Substring(pom.Length - 1) != "n"?Color.Green:Color.Green;
}
}
just replace
richTextBox1.Text += "[" + i.ToString() + "]" + " " + plik[i] + Environment.NewLine;
by
richTextBox1.AppendText("[" + i.ToString() + "]" + " " + plik[i] + Environment.NewLine);
Append the text instead of modifying it in whole. Using += will replace the entire string and hence you'll lose the set color each time. Use AppendText instead.
Also you can remove the unnecessary ifs in your code. This should work:
for (int i = 0; i < size; i++)
{
richTextBox1.AppendText("[" + i.ToString() + "]" + " " + plik[i] + Environment.NewLine);
richTextBox1.Select(richTextBox1.GetFirstCharIndexFromLine(i), richTextBox1.Lines[i].Length);
richTextBox1.SelectionBackColor = plik[i][plik[i].Length - 1] == 'n' ? Color.Red : Color.Green;
}
I have some code:
var result = new List<string>;
...
for (var i = 0; i < level; ++i)
if (result.ElementAtOrDefault(i) == null)
result.Insert(i, " " + positions[i]);
else
result[i] += " " + positions[i];
if (result.ElementAtOrDefault(level) == null)
result.Insert(level, " " + currentPosition);
else
result[level] += " " + currentPosition;
Can I do this without checking element for null from i-position? I need to add a part of string in i-position. But I have "ArgumentOutOfRangeException" if element wasn't created. Also method "insert" don't replace the element but push back it.
I tried to get data from "ArgumentOutOfRangeException" (which index called this exception) but I've failed.
You can reduce using ElementAtOrDefault with adding some condition like this
int i;
for (i = 0; i < level && i < result.Count; ++i){
//change existing items
result[i] += " " + positions[i];
}
for (int j = 0, countAdd = level - result.Count; j < countAdd; ++j)
//add new items
result.Add(" " + positions[i+j]);
//add current
if (level >= result.Count)
result.Add(" " + currentPosition);
else
result[level] += " " + currentPosition;