Related
I need to open the cells around a user picked cell in my rendition of minesweeper using C#. A user enters a coordinate I.e. 1, 3 and the program " opens" the cell and it either displays a number which indicates the bombs around it or its an empty field (Like minesweeper!). How should I appproach solving this problem so the cell and every cell near it open if empty?
//Opens cells if there is no bomb
public bool Open(int row, int column)
{
bool result = false;
if (row >= 0 && row < Dimension && column >= 0 && column < Dimension)
{
if (!cells[row, column].Open)
{
cells[row, column].Open = true;
result = true;
}
}
return result;
}
'''
Simplest solution is to recursively call your Open function for each neighboring cell. Since Open function already checks for invalid/open cells, no need to pre-check before recursive call.
if (cells[row, column].Empty)
{
Open(row - 1, column - 1);
Open(row - 1, column);
Open(row - 1, column + 1);
Open(row, column - 1);
Open(row, column + 1);
Open(row + 1, column - 1);
Open(row + 1, column);
Open(row + 1, column + 1);
}
If non-recursive method is desired, you could use something like breadth-first search
public bool Open(int row, int column)
{
bool result = false;
if (row >= 0 && row < Dimension && column >= 0 && column < Dimension)
{
if (!cells[row, column].Open)
{
cells[row, column].Open = true;
if (cells[row, column].Empty)
{
var bfs = new Queue<(int Row, int Column)>()
bfs.Enqueue((row, column));
while (bfs.Count > 0)
{
(int r, int c) = bfs.Dequeue();
int rb = r - 1;
if (rb < 0) rb = r;
int re = r + 1;
if (re >= Dimension) re = r;
int cb = c - 1;
if (cb < 0) cb = c;
int ce = c + 1;
if (ce >= Dimension) ce = c;
for (int i = rb; i <= re; i++)
{
for (int j = cb; j <= ce; j++)
{
if (!cells[i, j].Open)
{
cells[i, j].Open = true;
if (cells[i, j].Empty) bfs.Enqueue((i, j));
}
}
}
}
}
result = true;
}
}
return result;
}
This is the task: You are given a labyrinth, which consists of N x N squares, each of it can be passable or not. Passable cells consist of lower Latin letter between "a" and "z", and the non-passable – '#'. In one of the squares is Jack. It is marked with "*".
Two squares are neighbors, if they have common wall. At one step Jack can pass from one passable square to its neighboring passable square. When Jack passes through passable squares, he writes down the letters from each square. At each exit he gets a word. Write a program, which from a given labyrinth prints the words, which Jack gets from all the possible exits.
The input data is read from a text file named Labyrinth.in. At the first line in the file there is the number N (2 < N < 10). At each of the next N lines there are N characters, each of them is either Latin letter between "a" and "z" or "#" (impassable wall) or "*" (Jack). The output must be printed in the file Labyrinth.out.
Input:
6
a##km#
z#ada#
a*m###
#d####
rifid#
#d#d#t
So far I've done that :
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
public class Maze
{
private const string InputFileName = "Labyrinth.in";
private const string OutputFileName = "Labyrinth.out";
StringBuilder path = new StringBuilder();
public class Cell
{
public int Row { get; set; }
public int Column { get; set; }
public Cell(int row, int column)
{
this.Row = row;
this.Column = column;
}
}
private char[,] maze;
private int size;
private Cell startCell = null;
public void ReadFromFile(string fileName)
{
using (StreamReader reader = new StreamReader(fileName))
{
// Read maze size and create maze
this.size = int.Parse(reader.ReadLine());
this.maze = new char[this.size, this.size];
// Read the maze cells from the file
for (int row = 0; row < this.size; row++)
{
string line = reader.ReadLine();
for (int col = 0; col < this.size; col++)
{
this.maze[row, col] = line[col];
if (line[col] == '*')
{
this.startCell = new Cell(row, col);
}
}
}
}
}
public void FindAllPathsAndPrintThem()
{
if (this.startCell == null)
{
// Start cell is missing -> no path
SaveResult(OutputFileName, "");
return;
}
VisitCell(this.startCell.Row,
this.startCell.Column, path);
if (path.Length == 0)
{
// We didn't reach any cell at the maze border -> no path
SaveResult(OutputFileName, "");
return;
}
}
private void VisitCell(int row, int column, StringBuilder path)
{
if (row < 0 || row > maze.GetLength(0) - 1 ||
column < 0 || column > maze.GetLength(1) - 1)
{
SaveResult(OutputFileName, path.ToString());
return;
}
if (this.maze[row, column] != 'x' && this.maze[row, column] != '#')
{
// The cell is free --> visit it
if (this.maze[row, column] != '*')
{
path.Append(this.maze[row, column]);
this.maze[row, column] = 'x';
}
VisitCell(row, column + 1, path);
VisitCell(row, column - 1, path);
VisitCell(row + 1, column, path);
VisitCell(row - 1, column, path);
}
}
public void SaveResult(string fileName, string result)
{
using (StreamWriter writer = new StreamWriter(fileName))
{
writer.WriteLine(result);
}
}
static void Main()
{
Maze maze = new Maze();
maze.ReadFromFile(InputFileName);
maze.FindAllPathsAndPrintThem();
}
}
Sorry for long question. There need to be a small bug but I don't get it where.
The output is madifiddrdzaadamk. Thanks in advance.
Here's a solution I came up with. It keeps track of what cells have been visited during one go through the maze, but not for all attempts. This can be done by making the function return an IEnumerable<string> that will represent the paths to an exit from the current point in the maze. First check if you have gone off the edge of the maze and return nothing. Then check if you're at a wall and if so no path is returned. Otherwise you check if you are at the edge and if so you return the path that is represented by the current cell only. Then you have to mark the current cell as visited, then attempt to find all paths in each of the 4 directions and concatenate the current cell to any that you find and yield them. Then at the end you mark the cell as not visited so it can be used for other attempts.
private static IEnumerable<string> VisitCell(int row, int column, bool[,] visited)
{
if (row < 0 || column < 0 || row >= maze.GetLength(0) || column >= maze.GetLength(1))
yield break;
if (maze[row, column] == '#' || visited[row, column])
yield break;
if (row == 0 || row == maze.GetLength(0) - 1 ||
column == 0 || column == maze.GetLength(1) - 1)
{
yield return maze[row, column].ToString();
}
visited[row, column] = true;
foreach (var path in VisitCell(row, column + 1, visited))
{
yield return maze[row, column] + path;
}
foreach(var path in VisitCell(row, column - 1, visited))
{
yield return maze[row, column] + path;
}
foreach (var path in VisitCell(row + 1, column, visited))
{
yield return maze[row, column] + path;
}
foreach (var path in VisitCell(row - 1, column, visited))
{
yield return maze[row, column] + path;
}
visited[row, column] = false;
}
Then you can run this code
private static char[,] maze =
{
{ 'a', '#', '#', 'k', 'm', '#' },
{ 'z', '#', 'a', 'd', 'a', '#' },
{ 'a', '*', 'm', '#', '#', '#' },
{ '#', 'd', '#', '#', '#', '#' },
{ 'r', 'i', 'f', 'i', 'd', '#' },
{ '#', 'd', '#', 'd', '#', 't' }
};
private static void Main(string[] args)
{
foreach(var path in VisitCell(2, 1, new bool[6, 6]))
Console.WriteLine(path);
}
to get this result
*madam
*madamk
*madk
*madkm
*a
*az
*aza
*difid
*dir
*did
You can tweak it to remove the star from the beginning of each path.
Here is the correct code:
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
public class Maze
{
private const string InputFileName = "Labyrinth.in";
private const string OutputFileName = "Labyrinth.out";
public class Cell
{
public int Row { get; set; }
public int Column { get; set; }
public Cell(int row, int column)
{
this.Row = row;
this.Column = column;
}
}
private char[,] maze;
private int size;
private Cell startCell = null;
public void ReadFromFile(string fileName)
{
using (StreamReader reader = new StreamReader(fileName))
{
// Read maze size and create maze
this.size = int.Parse(reader.ReadLine());
this.maze = new char[this.size, this.size];
// Read the maze cells from the file
for (int row = 0; row < this.size; row++)
{
string line = reader.ReadLine();
for (int col = 0; col < this.size; col++)
{
this.maze[row, col] = line[col];
if (line[col] == '*')
{
this.startCell = new Cell(row, col);
}
}
}
}
}
public void FindAllPathsAndPrintThem()
{
if (this.startCell == null)
{
// Start cell is missing -> no path
SaveResult(OutputFileName, "");
return;
}
VisitCell(this.startCell.Row,
this.startCell.Column, "");
}
private void VisitCell(int row, int column, string path)
{
if (row < 0 || column < 0 || row >= maze.GetLength(0) || column >= maze.GetLength(1))
{
return;
}
if (row < 0 || row > maze.GetLength(0) - 1 ||
column < 0 || column > maze.GetLength(1) - 1)
{
SaveResult(OutputFileName, path);
return;
}
if (this.maze[row, column] != '#')
{
// The cell is free --> visit it
char x = this.maze[row, column];
this.maze[row, column] = '#';
VisitCell(row, column + 1, path + x);
VisitCell(row, column - 1, path + x);
VisitCell(row + 1, column, path + x);
VisitCell(row - 1, column, path + x);
this.maze[row, column] = x;
}
}
public void SaveResult(string fileName, string result)
{
using (StreamWriter writer = new StreamWriter(fileName, true))
{
writer.WriteLine(result);
}
}
static void Main()
{
Maze maze = new Maze();
maze.ReadFromFile(InputFileName);
maze.FindAllPathsAndPrintThem();
}
}
The comments on the question where correct I was modifying the string, so now it works, the string is not modified in process.
I'm currently having a hard time swapping two variables. I'd like to be able swap values once the user enters a value which is next to a blank cell
Apologies for my extremely messy code, I'm just picking up C#.
static void SwapNums(string[,] theBoard)
{
int col, row;
string swap;
string number = ReadNumber();
for (col = 0; col if (theBoard[col, row] == "")
{
theBoard[col, row] = number;
}
}
}
}
}
}
}
} < 6; col++)
{
for (row = 0; row < 6; row++)
{
if (theBoard[col,row] == number)
{
if (theBoard[col + 1, row] == "-" || theBoard[col - 1, row] == "-" || theBoard[col, row + 1] == "" || theBoard[col, row - 1] == "-")
{
swap = theBoard[col, row];
theBoard[col, row] = "";
for (col = 0; col < 6; col++)
{
for (row = 0; row < 6; row++)
{
if (theBoard[col, row] == "")
{
theBoard[col, row] = number;
}
}
}
}
} if (theBoard[col, row] == "")
{
theBoard[col, row] = number;
}
}
}
}
}
}
}
}
}
}
}
At the moment, this code is replacing the blank cellwith what the user entered, but is not replacing the cell that contains the number to p.
Make a function that gets the "position" of an element. Something like this?
const int ROWS = 6;
const int COLUMNS = 6;
static Tuple<int, int> GetPosition(string[,] theBoard, string value)
{
for (int i = 0; i < ROWS; i++)
for (int j = 0; j < COLUMNS; j++)
if (theBoard[i, j] == value)
return new Tuple<int, int>(i, j);
return new Tuple<int, int>(-1, -1);
}
Then, just swap the elements, something like this:
var numberPosition = GetPosition(theBoard, number);
var minusPosition = GetPosition(theBoard, "-");
theBoard[numberPosition.Item1, numberPosition.Item2] = "-";
theBoard[minusPosition.Item1, minusPosition.Item2] = number;
Make sure to check if the element was found! (Item1 and Item2 will be -1 if not)
Here you go, complete code that illustrates the concept: http://pastebin.com/5kjDPeX8
Edit:
Oh yeah, it should be only swapped if the element is next to it, so then, just do a check on the returned positions. Here is a replacement for the SwapNums method: (I didn't update the pastebin code above)
static void SwapNums(string[,] theBoard, string number)
{
var numberPosition = GetPosition(theBoard, number);
var minusPosition = GetPosition(theBoard, "-");
if (numberPosition.Item1 == -1 || minusPosition.Item1 == -1)
throw new Exception("Element " + number + " or - was not found in theBoard!");
if (numberPosition.Item1 == minusPosition.Item1) //they are in the same row
{
if (numberPosition.Item2 + 1 == minusPosition.Item2 ||
numberPosition.Item2 - 1 == minusPosition.Item2) // if they are next to eachother
{
theBoard[numberPosition.Item1, numberPosition.Item2] = "-";
theBoard[minusPosition.Item1, minusPosition.Item2] = number;
}
}
else if (numberPosition.Item2 == minusPosition.Item2) // same column
{
if (numberPosition.Item1 + 1 == minusPosition.Item1 ||
numberPosition.Item1 - 1 == minusPosition.Item1) //if they are above or below
{
theBoard[numberPosition.Item1, numberPosition.Item2] = "-";
theBoard[minusPosition.Item1, minusPosition.Item2] = number;
}
}
}
A slight digression (might be educational):
That Tuple<int, int> thing is just a class that contains two elements (namely int Item1 and int Item2), which is really convenient to use when your function needs to return two things (in our case, the row and column position of the element).
The <int, int> part means that the types of Item1 and Item2 will be int. The <something something etc.> things on classes generally are a part of something called generics, which is an advanced programming concept.
In short (about generics), it allows you to create a 'general' kind of an object, which could manipulate different types of object. Tuple here can contain pairs of any type of object; Tuple<string, int> would have string Item1 and int Item2.
But this isn't something you should worry about right now. When you've made a few classes of your own, you'll understand why this is convenient. For now, the Tuple class is the thing when you need to return 2 somethings from a function quick and easy.
So I'm new to working with browsers in Win-forms and I'm stuck a particular point.
What i want to do, for the browser to open a page(I've gotten this far). Once the page is open it must navigate to a particular part(It'somewhere in the middle of the page) and select it. Then copy and store it for when i need it, just the text.
I've been able to select all the text on a page by using the following code just as an example:
WebBrowser wb = (WebBrowser)sender;
wb.Document.ExecCommand("SelectAll", false, null);
wb.Document.ExecCommand("Copy", false, null);
richTextBox1.Text = Clipboard.GetText();
It can work for my program but I want to know if there is better way that will select just the text or info I need. If i can, place them in textboxes, or straight into my database.
This is the link to the page: http://www.lolking.net/news/league-trends-jul30
I want to select and get the info from these sections of the page:
Champion Pick Rates - Top 5 Increases and Decreases
Champion Win Rates - Top 5 Increases and Decreases
Champion Ban Rates - Top 5 Increases and Decreases
Any help would be appreciated.
Your foreach loop will look like this:
foreach (var item in list_ban)
{
string rtbpicker = item.ToString();
foreach (var comp in list_Comp)
{
int count = 0; //Counts for the number of occurences
foreach (Match m in Regex.Matches(rtbpicker, "" + comp.ToString() + ""))
{
int matchindex = m.Index;
int matchlength = m.Length;
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, " "); //Count just moves the index forward by however many postions the original index was shifted
if(Regex.Matches(rtbpicker, "" + comp.ToString() + "").Count > 1)
{
count++;
}
}
}
richTextBox6.Text += rtbpicker + "\n";
//rtbBan.AppendText(rtbpicker + System.Environment.NewLine);
}
I haven't got (yet) the whole solution but I can help you a bit:
Once you got the plain text from the FULLY LOADED webBrowser, and wrote in richTextBox1, then you can print the 3 part to other textboxes:
private void button_Click(object sender, EventArgs e)
{
List<string> rawhtml = new List<string>(); //List for the whole page
List<string> list_pick = new List<string>(); //PICK section
List<string> list_win = new List<string>(); //WIN section
List<string> list_ban = new List<string>(); //BAN section
rawhtml = richTextBox1.Lines.ToList(); //FILL the page to list
int ID_pick = 0;
int ID_win = 0;
int ID_ban = 0;
int ID_cmt = 0; // We need to specify the end of BAN section
for (int i = 0; i < rawhtml.Count; i++) //Search for the line number of section-start
{
if (rawhtml[i] == "Champion Pick Rates") ID_pick = i;
if (rawhtml[i] == "Champion Win Rates") ID_win = i;
if (rawhtml[i] == "Champion Ban Rates") ID_ban = i;
if (rawhtml[i].Contains("Comments")) ID_cmt = i;
}
// PICK
for (int i = ID_pick; i < ID_pick + (ID_win - ID_pick); i++) //Calculate the start and the end line-number
{
list_pick.AddRange(Regex.Split(rawhtml[i], "(?<=[)])")); //Split the five characters, without losing the ')'
}
foreach (var item in list_pick)
{
richTextBox2.AppendText(item + System.Environment.NewLine); //Optinal: Add to richtextbox
}
// WIN
for (int i = ID_win; i < ID_win + (ID_ban - ID_win); i++)
{
list_win.AddRange(Regex.Split(rawhtml[i], "(?<=[)])"));
}
foreach (var item in list_win)
{
richTextBox3.AppendText(item + System.Environment.NewLine);
}
// BAN
for (int i = ID_ban; i < ID_ban + (ID_cmt - ID_ban); i++)
{
list_ban.AddRange(Regex.Split(rawhtml[i], "(?<=[)])"));
}
foreach (var item in list_ban)
{
richTextBox4.AppendText(item + System.Environment.NewLine);
}
}
This code will make output from "Champion Win Rates" like:
Champion Win Rates
Top Five Biggest Increases
Urgot41.38%->43.67%(+2.29%)
Kennen47.7%->49.28%(+1.58%)
Lucian51.61%->53.1%(+1.49%)
Singed48.95%->50.31%(+1.36%)
Fiora53.48%->54.71%(+1.23%)
Top Five Biggest Decreases
Kassadin48.7%->46.67%(-2.03%)
Galio53.18%->51.42%(-1.76%)
Cho'Gath48.03%->46.37%(-1.66%)
Corki50.05%->48.43%(-1.62%)
Graves49.49%->47.98%(-1.51%)
Much better... ;)
I'm faced a problem with spaces, but i can't solve it yet.
I hope you understand this, if you have any question please comment!
Ps.: Sorry for bad eng
Pss.: I know this isn't the full solution, but i must share with you :)
And now the full solution, with perfect spaces. Regex was difficult to me, but I think this is more simple, however longer too.
private void btnspace_Click(object sender, EventArgs e)
{
richTextBox6.Text = null;
for (int i = 0; i < list_ban.Count; i++)
{
string rebuilder = ""; //for the output string (one line)
List<char> temp_chars = list_ban[i].ToCharArray().ToList(); //split one line into char sequence
int number_occur = 0; //occurence counter for numbers
int minus_occur = 0;// occurence counter for '-'
for (int j = 0; j < temp_chars.Count; j++)
{
// NUMBERS
// I don't wanted to hardcode the champions :/
if (number_occur < 2 && (temp_chars[j] == '1' || temp_chars[j] == '2' || temp_chars[j] == '3' || temp_chars[j] == '4' || temp_chars[j] == '5' || temp_chars[j] == '6' || temp_chars[j] == '7' || temp_chars[j] == '8' || temp_chars[j] == '9' || temp_chars[j] == '0')) //looks pretty, isn't?
{
temp_chars.Insert(j, ' '); //insert a space into char seq
j = j + 5; // in the longest case: 12.34, so skip 5 char, or 1 2. 3 4
number_occur = number_occur + 1; //for the difference percentage we don't need spaces, so insert by number only twice
}
// NUMBERS DONE
}
for (int j = 0; j < temp_chars.Count; j++)
{
// ( and -
if (temp_chars[j] == '-' || temp_chars[j] == '(')
{
if (temp_chars[j] == '-') minus_occur = minus_occur + 1; //if the difference is negative, there will be one more minus, which doesn't need space
if (minus_occur <= 1) temp_chars.Insert(j, ' ');
j = j + 1; //avoid endless loop
}
// ( and - DONE
}
foreach (var item in temp_chars)
{
rebuilder = rebuilder + item; //rebuild the line from the char list, with spaces
}
list_ban.RemoveAt(i); //replace the old spaceless lines...
list_ban.Insert(i, rebuilder);
richTextBox1.AppendText(list_ban[i] + System.Environment.NewLine);
}
}
I hope it's clear, i tried to comment everything. Good luck, and feel free to ask. Please mention if it's working because i want to answer this question perfectly :D
Ok, so this is my final solution, it works 100%, it takes your first answer, which you can see ;p and uses my regex.matches. I think the part that i added to the foreach loops, could be done in a method so you can just call it whenever you need it. I just haven't got to that yet! :)
private void button3_Click(object sender, EventArgs e)
{
List<string> rawhtml = new List<string>(); //List for the whole page
List<string> list_pick = new List<string>(); //PICK section
List<string> list_win = new List<string>(); //WIN section
List<string> list_ban = new List<string>(); //BAN section
List<string> list_Comp = new List<string>(); //Champion names
fillchamplist(list_Comp);
rawhtml = richTextBox1.Lines.ToList(); //FILL the page to list
int ID_pick = 0;
int ID_win = 0;
int ID_ban = 0;
int ID_cmt = 0; // We need to specify the end of BAN section
for (int i = 0; i < rawhtml.Count; i++) //Search for the line number of section-start
{
if (rawhtml[i] == "Champion Pick Rates") ID_pick = i;
if (rawhtml[i] == "Champion Win Rates") ID_win = i;
if (rawhtml[i] == "Champion Ban Rates") ID_ban = i;
if (rawhtml[i].Contains("Comments")) ID_cmt = i;
}
// PICK
for (int i = ID_pick; i < ID_pick + (ID_win - ID_pick); i++) //Calculate the start and the end line-number
{
list_pick.AddRange(Regex.Split(rawhtml[i], "(?<=[)])")); //Split the five characters, without losing the ')'
}
foreach (var item in list_pick)
{
string rtbpicker = item.ToString();
foreach (var comp in list_Comp)
{
int count = 0; //To see which match we working with later
foreach (Match m in Regex.Matches(rtbpicker, "" + comp.ToString() + "")) // Checks for all matches and cycles through them
{
if (count == 2) // if the count == 2, it means that its on its 3rd match(the one we dont wana give a space to
{
}
else // puts the space in
{
int matchindex = m.Index;
int matchlength = m.Length;
if (m.Length >= 2) // only champ names are >=2
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, "\t");
}
else
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, " "); // the count variable updates he index so the space doesnt occur before the % sign
}
if (Regex.Matches(rtbpicker, "" + comp.ToString() + "").Count > 0)// just to update the index for the 2nd %
{
count++;
}
}
}
}
rtbPick.AppendText(rtbpicker + System.Environment.NewLine); //Optinal: Add to richtextbox
}
// WIN
for (int i = ID_win; i < ID_win + (ID_ban - ID_win); i++)
{
list_win.AddRange(Regex.Split(rawhtml[i], "(?<=[)])"));
}
foreach (var item in list_win)
{
string rtbpicker = item.ToString();
foreach (var comp in list_Comp)
{
int count = 0;
foreach (Match m in Regex.Matches(rtbpicker, "" + comp.ToString() + ""))
{
if (count == 2)
{
}
else
{
int matchindex = m.Index;
int matchlength = m.Length;
if (m.Length >= 2)
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, "\t");
}
else
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, " ");
}
if (Regex.Matches(rtbpicker, "" + comp.ToString() + "").Count > 0)
{
count++;
}
}
}
}
rtbWin.AppendText(rtbpicker + System.Environment.NewLine);
}
// BAN
for (int i = ID_ban; i < ID_ban + (ID_cmt - ID_ban); i++)
{
list_ban.AddRange(Regex.Split(rawhtml[i], "(?<=[)])"));
}
foreach (var item in list_ban)
{
string rtbpicker = item.ToString();
foreach (var comp in list_Comp)
{
int count = 0;
foreach (Match m in Regex.Matches(rtbpicker, "" + comp.ToString() + ""))
{
if (count == 2)
{
}
else
{
int matchindex = m.Index;
int matchlength = m.Length;
if (m.Length >= 2)
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, "\t");
}
else
{
rtbpicker = rtbpicker.Insert(matchindex + matchlength + count, " ");
}
if (Regex.Matches(rtbpicker, "" + comp.ToString() + "").Count > 0)
{
count++;
}
}
}
}
rtbBan.AppendText(rtbpicker + System.Environment.NewLine);
}
}
This is the outcome:(doesn't show the tabs here for some reason)
Champion Pick Rates
Top Five Biggest Increases
Lucian 27.75% -> 32.3% (+4.55%)
Ahri 8.7% -> 11.3% (+2.6%)
Rengar 11.25% -> 13.84% (+2.59%)
Nidalee 10.7% -> 12.93% (+2.23%)
Tristana 30.07% -> 32.02% (+1.95%)
Top Five Biggest Decreases
Caitlyn 34.44% -> 30.63% (-3.81%)
Vayne 17.25% -> 15.69% (-1.56%)
Ezreal 15.08% -> 13.6% (-1.48%)
Renekton 13.84% -> 12.6% (-1.24%)
Lee Sin 30.54% -> 23.36% (-7.18%)
Ok so :D that works perfect for me, but that's cause i know what i want the outcome to be for this specific thing. Your method also works and i would actually recommend it for scenarios.
If you got any questions, dont be afraid to ask hey :)
I have a listBox1 object and it contains some items. I have a button to move selected item up and another to move selected item down. What should the code be to the two buttons?
public void MoveUp()
{
MoveItem(-1);
}
public void MoveDown()
{
MoveItem(1);
}
public void MoveItem(int direction)
{
// Checking selected item
if (listBox1.SelectedItem == null || listBox1.SelectedIndex < 0)
return; // No selected item - nothing to do
// Calculate new index using move direction
int newIndex = listBox1.SelectedIndex + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= listBox1.Items.Count)
return; // Index out of range - nothing to do
object selected = listBox1.SelectedItem;
// Removing removable element
listBox1.Items.Remove(selected);
// Insert it in new position
listBox1.Items.Insert(newIndex, selected);
// Restore selection
listBox1.SetSelected(newIndex, true);
}
UPD 2020-03-24: Extension class for simple reuse and it also supports CheckedListBox (if CheckedListBox is not needed for you, please remove appropriate lines of code). Thanks #dognose and #Chad
public static class ListBoxExtension
{
public static void MoveSelectedItemUp(this ListBox listBox)
{
_MoveSelectedItem(listBox, -1);
}
public static void MoveSelectedItemDown(this ListBox listBox)
{
_MoveSelectedItem(listBox, 1);
}
static void _MoveSelectedItem(ListBox listBox, int direction)
{
// Checking selected item
if (listBox.SelectedItem == null || listBox.SelectedIndex < 0)
return; // No selected item - nothing to do
// Calculate new index using move direction
int newIndex = listBox.SelectedIndex + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= listBox.Items.Count)
return; // Index out of range - nothing to do
object selected = listBox.SelectedItem;
// Save checked state if it is applicable
var checkedListBox = listBox as CheckedListBox;
var checkState = CheckState.Unchecked;
if (checkedListBox != null)
checkState = checkedListBox.GetItemCheckState(checkedListBox.SelectedIndex);
// Removing removable element
listBox.Items.Remove(selected);
// Insert it in new position
listBox.Items.Insert(newIndex, selected);
// Restore selection
listBox.SetSelected(newIndex, true);
// Restore checked state if it is applicable
if (checkedListBox != null)
checkedListBox.SetItemCheckState(newIndex, checkState);
}
}
private void UpClick()
{
// only if the first item isn't the current one
if(listBox1.ListIndex > 0)
{
// add a duplicate item up in the listbox
listBox1.AddItem(listBox1.Text, listBox1.ListIndex - 1);
// make it the current item
listBox1.ListIndex = (listBox1.ListIndex - 2);
// delete the old occurrence of this item
listBox1.RemoveItem(listBox1.ListIndex + 2);
}
}
private void DownClick()
{
// only if the last item isn't the current one
if((listBox1.ListIndex != -1) && (listBox1.ListIndex < listBox1.ListCount - 1))
{
// add a duplicate item down in the listbox
listBox1.AddItem(listBox1.Text, listBox1.ListIndex + 2);
// make it the current item
listBox1.ListIndex = listBox1.ListIndex + 2;
// delete the old occurrence of this item
listBox1.RemoveItem(listBox1.ListIndex - 2);
}
}
Did you try searching it in google? Move Items up/dowm in listbox control for example.
public class SmartListBox : ListBox
{
//Moves the selected items up one level
public MoveUp()
{
for(int i = 0; i < Items.Count; i++)
{
if (Items[i].Selected)//identify the selected item
{
//swap with the top item(move up)
if (i > 0 && !Items[i - 1].Selected)
{
ListItem bottom = Items[i];
Items.Remove(bottom);
Items.Insert(i - 1, bottom);
Items[i - 1].Selected = true;
}
}
}
}
//Moves the selected items one level down
public MoveDown()
{
int startindex = Items.Count -1;
for (int i = startindex; i > -1; i--)
{
if (Items[i].Selected)//identify the selected item
{
//swap with the lower item(move down)
if (i < startindex && !Items[i + 1].Selected)
{
ListItem bottom = Items[i];
Items.Remove(bottom);
Items.Insert(i + 1, bottom);
Items[i + 1].Selected = true;
}
}
}
}
}
Modified #Save code to allow for moving items that are data bound to a ListBox using DataSource property.
public void MoveItem(int direction)
{
// Checking selected item
if (listBox1.SelectedItem == null || listBox1.SelectedIndex < 0)
return; // No selected item - nothing to do
// Calculate new index using move direction
int newIndex = listBox1.SelectedIndex + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= listBox1.Items.Count)
return; // Index out of range - nothing to do
UnifyCamera selected = listBox1.SelectedItem as UnifyCamera;
// modify the data source list
inputData.Cameras.RemoveAt(listBox1.SelectedIndex);
inputData.Cameras.Insert(newIndex, selected);
// re-bind your data source
((ListBox)listBox1).DataSource = null;
((ListBox)listBox1).DataSource = this.inputData.Cameras;
((ListBox)listBox1).DisplayMember = "Name";
// Restore selection
listBox1.SetSelected(newIndex, true);
}
Where UnifyCamera is my custom class that is stored in a list inputData.Cameras that returns a List<UnifyCamera>.
Modified Desolator code above to pass the control as a parameter...reusable
private void MoveUp()
{
MoveItem(-1,listBox1);
}
private void MoveDown()
{
MoveItem(1,listBox1);
}
public void MoveItem(int direction,ListBox listBox)
{
// Checking selected item
if (listBox.SelectedItem == null || listBox.SelectedIndex < 0)
return; // No selected item - nothing to do
// Calculate new index using move direction
int newIndex = listBox.SelectedIndex + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= listBox.Items.Count)
return; // Index out of range - nothing to do
object selected = listBox.SelectedItem;
// Removing removable element
listBox.Items.Remove(selected);
// Insert it in new position
listBox.Items.Insert(newIndex, selected);
// Restore selection
listBox.SetSelected(newIndex, true);
}
public static void MoveUpOrDownSelectedItem(ListBox LisBox, bool MoveUp)
{
if (LisBox.SelectedIndex > 0 && MoveUp)
{
// add a duplicate item up in the listbox
LisBox.Items.Insert(LisBox.SelectedIndex - 1, LisBox.SelectedItem);
// make it the current item
LisBox.SelectedIndex = (LisBox.SelectedIndex - 2);
// delete the old occurrence of this item
LisBox.Items.RemoveAt(LisBox.SelectedIndex + 2);
}
if ((LisBox.SelectedIndex != -1) && (LisBox.SelectedIndex < LisBox.Items.Count- 1) && MoveUp == false)
{
// add a duplicate item down in the listbox
int IndexToRemove = LisBox.SelectedIndex;
LisBox.Items.Insert(LisBox.SelectedIndex + 2, LisBox.SelectedItem);
// make it the current item
LisBox.SelectedIndex = (LisBox.SelectedIndex + 2);
// delete the old occurrence of this item
LisBox.Items.RemoveAt(IndexToRemove);
}
}
All the other answers are fine, but you should also consider this one :)
The idea of it is close to the one from SwDevMan81 but this is real code (not pseudo)
and you could move more than one item (multiple selected items)
and with an improvement when moving down.
private void MoveUp_listBox_button_Click(object sender, EventArgs e)
{
listBox.BeginUpdate();
int numberOfSelectedItems = listBox.SelectedItems.Count;
for (int i = 0; i < numberOfSelectedItems; i++)
{
// only if it's not the first item
if (listBox.SelectedIndices[i] > 0)
{
// the index of the item above the item that we wanna move up
int indexToInsertIn = listBox.SelectedIndices[i] - 1;
// insert UP the item that we want to move up
listBox.Items.Insert(indexToInsertIn, listBox.SelectedItems[i]);
// removing it from its old place
listBox.Items.RemoveAt(indexToInsertIn + 2);
// highlighting it in its new place
listBox.SelectedItem = listBox.Items[indexToInsertIn];
}
}
listBox.EndUpdate();
}
private void MoveDown_listBox_button_Click(object sender, EventArgs e)
{
listBox.BeginUpdate();
int numberOfSelectedItems = listBox.SelectedItems.Count;
// when going down, instead of moving through the selected items from top to bottom
// we'll go from bottom to top, it's easier to handle this way.
for (int i = numberOfSelectedItems-1; i >= 0; i--)
{
// only if it's not the last item
if (listBox.SelectedIndices[i] < listBox.Items.Count - 1)
{
// the index of the item that is currently below the selected item
int indexToInsertIn = listBox.SelectedIndices[i] + 2;
// insert DOWN the item that we want to move down
listBox.Items.Insert(indexToInsertIn, listBox.SelectedItems[i]);
// removing it from its old place
listBox.Items.RemoveAt(indexToInsertIn - 2);
// highlighting it in its new place
listBox.SelectedItem = listBox.Items[indexToInsertIn - 1];
}
}
listBox.EndUpdate();
}
Vexe's answer worked the best for me, but I had to modify it to fix a couple of issues. This solution will highlight the correct object if the same object is in the list box multiple times. Also, this solution prevents multi selected objects from flip flopping when they hit the top or bottom of the list box and the button continues to be pressed multiple times.
private void btnMoveUp_Click(object sender, EventArgs e)
{
// find the lowest index of non selected items
int lowestIndexNotSelected = listBox.Items.Count - 1;
for (int i = listBox.Items.Count - 1; i >= 0; i--)
{
if (!listBox.SelectedIndices.Contains(i))
{
lowestIndexNotSelected = i;
}
}
listBox.BeginUpdate();
int numberOfSelectedItems = listBox.SelectedItems.Count;
for (int i = 0; i < numberOfSelectedItems; i++)
{
// only if it's not a lower inde than the lowest non selected index
if (listBox.SelectedIndices[i] > lowestIndexNotSelected)
{
// the index of the item above the item that we wanna move up
int indexToInsertIn = listBox.SelectedIndices[i] - 1;
// insert UP the item that we want to move up
listBox.Items.Insert(indexToInsertIn, listBox.SelectedItems[i]);
// removing it from its old place
listBox.Items.RemoveAt(indexToInsertIn + 2);
// highlighting it in its new place (by index, to prevent highlighting wrong instance)
listBox.SelectedIndex = indexToInsertIn;
}
}
listBox.EndUpdate();
}
private void btnMoveDown_Click(object sender, EventArgs e)
{
// find the highest index of non selected items
int highestIndexNonSelected = 0;
for (int i = 0; i < listBox.Items.Count; i++)
{
if (!listBox.SelectedIndices.Contains(i))
{
highestIndexNonSelected = i;
}
}
listBox.BeginUpdate();
int numberOfSelectedItems = listBox.SelectedItems.Count;
// when going down, instead of moving through the selected items from top to bottom
// we'll go from bottom to top, it's easier to handle this way.
for (int i = numberOfSelectedItems - 1; i >= 0; i--)
{
// only if it's not a higher index than the highest index not selected
if (listBox.SelectedIndices[i] < highestIndexNonSelected)
{
// the index of the item that is currently below the selected item
int indexToInsertIn = listBox.SelectedIndices[i] + 2;
// insert DOWN the item that we want to move down
listBox.Items.Insert(indexToInsertIn, listBox.SelectedItems[i]);
// removing it from its old place
listBox.Items.RemoveAt(indexToInsertIn - 2);
// highlighting it in its new place (by index, to prevent highlighting wrong instance)
listBox.SelectedIndex = indexToInsertIn - 1;
}
}
listBox.EndUpdate();
}
// Options is a list box
private void MoveUpButton_Click(object sender,EventArgs e) {
int index = Options.SelectedIndex;
if (index <= 0) return;
string item = (string)Options.Items[index - 1];
Options.Items.RemoveAt(index - 1);
Options.Items.Insert(index,item);
selectedIndexChanged(null,null);
}
private void MoveDnButton_Click(object sender,EventArgs e) {
int index = Options.SelectedIndex;
if (index + 1 >= Options.Items.Count) return;
string item = (string)Options.Items[index];
Options.Items.RemoveAt(index);
Options.Items.Insert(index + 1,item);
Options.SelectedIndex = index + 1;
}
// sent when user makes a selection or when he moves an item up or down
private void selectedIndexChanged(object sender,EventArgs e) {
int index = Selected.SelectedIndex;
MoveUpButton.Enabled = index > 0;
MoveDnButton.Enabled = index + 1 < Selected.Items.Count;
}
private void btnUp_Click(object sender, System.EventArgs e)
{
if (this.lbItems.SelectedIndex == -1 || this.lbItems.SelectedIndex == 0)
return;
Object select, previous, temp;
select = lbItems.Items[lbItems.SelectedIndex];
previous = lbItems.Items[lbItems.SelectedIndex-1];
temp = select;
select = previous;
previous = temp;
lbItems.Items[lbItems.SelectedIndex] = select;
lbItems.Items[lbItems.SelectedIndex-1] = previous;
lbItems.SelectedIndex--;
}
private void btnDown_Click(object sender, System.EventArgs e)
{
if (this.lbItems.SelectedIndex == -1 || this.lbItems.SelectedIndex == lbItems.Items.Count-1)
return;
Object select, next, temp;
select = lbItems.Items[lbItems.SelectedIndex];
next = lbItems.Items[lbItems.SelectedIndex+1];
temp = select;
select = next;
next = temp;
lbItems.Items[lbItems.SelectedIndex] = select;
lbItems.Items[lbItems.SelectedIndex+1] = next;
lbItems.SelectedIndex++;
}
For those who are looking for a generic way to deal with ListBox that could be bound to a DataSource here's a generic extension based on Save's answer that will handle regular and binded ListBox.
public static void MoveUp(this ListBox listBox)
{
listBox.MoveItem(-1);
}
public static void MoveDown(this ListBox listBox)
{
listBox.MoveItem(1);
}
public static void MoveItem(this ListBox listBox, int direction)
{
// Checking selected item
if (listBox.SelectedItem == null || listBox.SelectedIndex < 0)
return; // No selected item - nothing to do
// Calculate new index using move direction
int newIndex = listBox.SelectedIndex + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= listBox.Items.Count)
return; // Index out of range - nothing to do
//Find our if we're dealing with a BindingSource
bool isBindingSource = listBox.DataSource is BindingSource;
//Get the list
System.Collections.IList list = isBindingSource ? ((BindingSource)listBox.DataSource).List : listBox.Items;
object selected = listBox.SelectedItem;
// Removing removable element
list.Remove(selected);
// Insert it in new position
list.Insert(newIndex, selected);
// Restore selection
listBox.SetSelected(newIndex, true);
if (isBindingSource)
{
//Reset the binding if needed
((BindingSource)listBox.DataSource).ResetBindings(false);
}
}
Get a collection of the selected items and then move them as follows:
private void btnMoveUp_Click(object sender, EventArgs e)
{
HashSet<KeyValuePair<int, object>> ItemsToMove = new HashSet<KeyValuePair<int, object>>();
foreach (object o in lstMyListView.SelectedItems)
ItemsToMove.Add(new KeyValuePair<int, object>(lstMyListView.Items.IndexOf(o), o));
foreach (KeyValuePair<int, object> kvp in ItemsToMove)
{
if (kvp.Key > 0) // check if its the first item before moving
{
lstMyListView.Items.Remove(kvp.Value);
lstMyListView.Items.Insert(kvp.Key - 1, kvp.Value);
}
}
}
private void btnMoveDown_Click(object sender, EventArgs e)
{
HashSet<KeyValuePair<int, object>> ItemsToMove = new HashSet<KeyValuePair<int, object>>();
foreach (object o in lstMyListView.SelectedItems)
ItemsToMove.Add(new KeyValuePair<int, object>(lstMyListView.Items.IndexOf(o), o));
foreach (KeyValuePair<int, object> kvp in ItemsToMove)
{
if (kvp.Key < lstMyListView.Items.Count - 1) // check if its the last item before moving
{
lstMyListView.Items.Remove(kvp.Value);
lstMyListView.Items.Insert(kvp.Key + 1, kvp.Value);
}
}
}
I wrote this function to move my selected items:
using System.Collections;
using System.Collections.Generic;
private void MoveListboxItems(int step, ListBox lb) {
/* 'step' should be:
* -1 for moving selected items up
* 1 for moving selected items down
* 'lb' is your ListBox
* see examples how to call below this function
*/
try {
// do only something when really an item is selected
if (lb.SelectedIndex > -1) {
// get some needed values - they change while we manipulate the listbox
// but we need them as they was original
IList SelectedItems = lb.SelectedItems;
IList SelectedIndices = lb.SelectedIndices;
// set some default values
int selIndex = -1;
int newIndex = -1;
int selCount = SelectedItems.Count;
int lc = 0;
int mc = 0;
string moveOldValue = string.Empty;
string selectedItemValue = string.Empty;
if (step == 1) {
mc = selCount - 1;
} else {
mc = lc;
}
// enter the loop through the selected items
while (lc < selCount) {
selectedItemValue = string.Empty;
moveOldValue = string.Empty;
try {
// get the item that should get moved
selectedItemValue = SelectedItems[mc].ToString();
selIndex = Convert.ToInt32(SelectedIndices[mc]);
} catch {
selIndex = -1;
}
// gen index for new place
newIndex = selIndex + step;
try {
// get the old value from the place where the item get moved
moveOldValue = lb.Items[newIndex].ToString();
} catch { /* do nothing */ }
try {
if (!String.IsNullOrEmpty(selectedItemValue) && !String.IsNullOrEmpty(moveOldValue) && selIndex != -1 && newIndex != -1 && !lb.SelectedIndices.Contains(newIndex)) {
// move selected item
lb.Items.RemoveAt(newIndex);
lb.Items.Insert(newIndex, selectedItemValue);
// write old value back to the old place from selected item
lb.Items.RemoveAt(selIndex);
lb.Items.Insert(selIndex, moveOldValue);
// hold the moved item selected
lb.SetSelected(newIndex, true);
}
} catch { /* do nothing */ }
lc++;
if (step == 1) {
mc -= step;
} else {
mc = lc;
}
}
}
} catch { /* do nothing */ };
}
// examples how i call the function above
void BtnLbUp_Click(object sender, EventArgs e) {
MoveListboxItems(-1, this.lbMyList);
}
void BtnLbDown_Click(object sender, EventArgs e) {
MoveListboxItems(1, this.lbMyList);
}
For Up Button:
private void UpBottom_Click(object sender, EventArgs e)
{
//this.Options is ListBox
if (this.Options.SelectedIndex == -1 ||
this.Options.SelectedIndex == 0)
return;
string item, aboveItem;
int itemIndex, aboveItemIndex;
itemIndex = this.Options.SelectedIndex;
aboveItemIndex = this.Options.SelectedIndex - 1;
item = (string)this.Options.Items[itemIndex];
aboveItem = (string)this.Options.Items[aboveItemIndex];
this.Options.Items.RemoveAt(aboveItemIndex);
this.Options.Items.Insert(itemIndex, aboveItem);
}
For Down Button:
private void DownButton_Click(object sender, EventArgs e)
{
//this.Options is ListBox
if (this.Options.SelectedIndex == -1 ||
this.Options.SelectedIndex >= this.Options.Items.Count)
return;
string item, belowItem;
int itemIndex, belowItemIndex;
itemIndex = this.Options.SelectedIndex;
belowItemIndex = this.Options.SelectedIndex + 1;
if (belowItemIndex >= this.Options.Items.Count)
return;
item = (string)this.Options.Items[itemIndex];
belowItem = (string)this.Options.Items[belowItemIndex];
this.Options.Items.RemoveAt(itemIndex);
this.Options.Items.Insert(belowItemIndex, item);
this.Options.SelectedIndex = belowItemIndex;
}
I Use this with multiple selections.
It also works with interleaved selections.
private void Order_buttons_Click(object sender, EventArgs e)
{
//If noselection return
if (Layouts_listBox.SelectedItems.Count == 0) return;
//Determines wether up or down
int movement = (sender as Button) == Order_Upbutton? - 1 : 1;
//creates a dictionary associating the original Index (ListBox) to the text
Dictionary<int, string> Items = new Dictionary<int, string>();
//Also creates a list with the Index for sorting
List<int> DesiredOrder = new List<int>();
//Cycle through the selection and fill both the list and dictionary
ListBox.SelectedObjectCollection NN = Layouts_listBox.SelectedItems;
foreach (object n in NN)
{
DesiredOrder.Add(Layouts_listBox.Items.IndexOf(n));
Items.Add(Layouts_listBox.Items.IndexOf(n), (string)n);
}
//Sort the List according to the desired button (Up or Down)
DesiredOrder.Sort();
if ((sender as Button) != Order_Upbutton) DesiredOrder.Reverse();
//I'm using this ErrorHandling but thats up to you
try
{
//Call the MoveItem (Credits to Save) according to the sorted order
foreach (int n in DesiredOrder) MoveItem(movement, Items[n]);
}
catch (Exception)
{
SystemSounds.Asterisk.Play();
}
}
public void MoveItem(int direction, string Selected)
{
// Checking selected item
if (!Layouts_listBox.Items.Contains(Selected) || Layouts_listBox.Items.IndexOf(Selected) < 0)
throw new System.Exception(); // No selected item - Cancel entire Func
// Calculate new index using move direction
int newIndex = Layouts_listBox.Items.IndexOf(Selected) + direction;
// Checking bounds of the range
if (newIndex < 0 || newIndex >= Layouts_listBox.Items.Count)
throw new System.Exception(); // Index out of range - Cancel entire Func
object selected = Layouts_listBox.Items[Layouts_listBox.Items.IndexOf(Selected)];
// Removing removable element
Layouts_listBox.Items.Remove(selected);
// Insert it in new position
Layouts_listBox.Items.Insert(newIndex, selected);
// Restore selection
Layouts_listBox.SetSelected(newIndex, true);
}