I am creating a simple "Pairs" game in WPF. I have 12 Image controls on MainWindow. What I need to do, is to use OpenFileDialog to select multiple Images (can be less then all 6) and then randomly place them into Image controls. Each picture should appear twice. How would I be able to achieve this? I am stuck here for a while and only have following code at the moment. I am not asking for a solution, I only need a few pointers on how to deal with this. Thank you.
> public ObservableCollection<Image> GetImages()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Multiselect = true;
ObservableCollection<Image> imagesList = new ObservableCollection<Image>();
if (dlg.ShowDialog() == true)
{
foreach (String img in dlg.FileNames)
{
Image image = new Image();
image.Name = "";
image.Location = img;
imagesList.Add(image);
}
}
return imagesList;
}
There are many ways to achieve your required results. A good way would be to use the Directory.GetFiles method, which will return a collection of string file paths:
string [] filePaths = Directory.GetFiles(targetDirectory);
You can then use a method to randomise the order of the collection. From the C# Shuffle Array page on DotNETPerls:
public string[] RandomizeStrings(string[] arr)
{
List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
// Add all strings from array
// Add new random int each time
foreach (string s in arr)
{
list.Add(new KeyValuePair<int, string>(_random.Next(), s));
}
// Sort the list by the random number
var sorted = from item in list
orderby item.Key
select item;
// Allocate new string array
string[] result = new string[arr.Length];
// Copy values to array
int index = 0;
foreach (KeyValuePair<int, string> pair in sorted)
{
result[index] = pair.Value;
index++;
}
// Return copied array
return result;
}
Then add your duplicate file paths, re-randomise the order again and populate your UI property with the items:
string[] filePathsToUse = new string[filePaths.Length * 2];
filePaths = RandomizeStrings(filePaths);
for (int count = 0; count < yourRequiredNumber; count++)
{
filePathsToUse.Add(filePaths(count));
filePathsToUse.Add(filePaths(count));
}
// Finally, randomize the collection again:
ObservableCollection<string> filePathsToBindTo = new
ObservableCollection<string>(RandomizeStrings(filePathsToUse));
Of course, you could also do it in many other ways, some easier to understand, some more efficient. Just pick a method that you feel comfortable with.
Related
Note: in converting my long untidy code into pseudo-code I have not explained that I want to return an array/list that contains the data that as been loaded into arFiles.
That is what line...
// look at the contents of each file and store selected information in
means.
I have simple form with a button to load a directory of files
and scan them and load an array with the selected data at runtime.
How do I load the dataGridView1 with the contents of an array?
I have searched the web and cannot find a method to do it at runtime.
The examples I have seen assume one is loading static data.
Please be gentle with me as I have not coded for approx 10 years.
private void btnReadLogFiles_Click(object sender, EventArgs e)
{
string[,] arFiles;
arFiles = this.getFileInfo();
// watch window shows arFiles is loaded correctly
// how do I load a dataGridView1 with the data from the array at runtime?
}
private string[,] getFileInfo()
{
string[] oFiles = Directory.GetFiles(sPath, "*.csv");
nColumns = 4;
nRows = Directory.GetFiles(sPath, "*.csv").Length;
// create an array of rows that matches the files in the directory
string[,] arFiles = new string[nRows, 4];
// look at the contents of each file and store selected information in arFiles.
return arFiles;
}
Unfortunately if you just assign an array of strings into DataGridView the result might not be the one you expect. Therefore, you can workaround this via converting the array of strings into list of anonymous objects and then assign it to the DataGridView
Example:
private void button1_Click(object sender, EventArgs e)
{
this.dataGridView1.DataSource = GetFileInfo();
}
private List<object> GetFileInfo()
{
string[] allPaths = Directory.GetFiles(#"C:\Program Files (x86)\Microsoft Visual Studio 10.0", "*.txt", SearchOption.AllDirectories);
List<object> list = new List<object>();
foreach (var path in allPaths)
{
// Create a new anonymous object
list.Add(new { File = Path.GetFileName(path) });
}
return list;
}
Result:
The following code will put the files into 4 columns
private List<List<string>> getFileInfo()
{
List<List<string>> arFiles = new List<List<string>>();
string[] oFiles = Directory.GetFiles(sPath, "*.csv");
nColumns = 4;
List<string> newRow = null;
for(int index = 0; index < oFiles.Count(); index++)
{
if ((index % nColumns) == 0)
{
newRow = new List<string>();
arFiles.Add(newRow);
}
newRow.Add(oFiles[index]);
}
return arFiles;
}
how do I make the foreach instruction iterate both in the "files" variable and in the "names" array?
var files = Directory.GetFiles(#".\GalleryImages");
string[] names = new string[8] { "Matt", "Joanne", "Robert","Andrei","Mihai","Radu","Ionica","Vasile"};
I've tried 2 options.. the first one gives me lots of errors and the second one displays 8 images of each kind
foreach(var file in files,var i in names)
{
//Do stuff
}
and
foreach(var file in files)
{
foreach (var i in names)
{
//Do stuff
}
}
You can try using the Zip Extension method of LINQ:
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
foreach (var item in numbersAndWords)
Console.WriteLine(item);
Would look something like this:
var files = Directory.GetFiles(#".\GalleryImages");
string[] names = new string[] { "Matt", "Joanne", "Robert", "Andrei", "Mihai","Radu","Ionica","Vasile"};
var zipped = files.Zip(names, (f, n) => new { File = f, Name = n });
foreach(var fn in zipped)
Console.WriteLine(fn.File + " " + fn.Name);
But I haven't tested this one.
It's not clear what you're asking. But, you can't iterate two iterators with foreach; but you can increment another variable in the foreach body:
int i = 0;
foreach(var file in files)
{
var name = names[i++];
// TODO: do something with name and file
}
This, of course, assumes that files and names are of the same length.
You can't. Use a for loop instead.
for(int i = 0; i < files.Length; i++)
{
var file = files[i];
var name = names[i];
}
If the both array have the same length this should work.
You have two options here; the first works if you are iterating over something that has an indexer, like an array or List, in which case use a simple for loop and access things by index:
for (int i = 0; i < files.Length && i < names.Length; i++)
{
var file = files[i];
var name = names[i];
// Do stuff with names.
}
If you have a collection that doesn't have an indexer, e.g. you just have an IEnumerable and you don't know what it is, you can use the IEnumerable interface directly. Behind the scenes, that's all foreach is doing, it just hides the slightly messier syntax. That would look like:
var filesEnum = files.GetEnumerator();
var namesEnum = names.GetEnumerator();
while (filesEnum.MoveNext() && namesEnum.MoveNext())
{
var file = filesEnum.Current;
var name = namesEnum.Current;
// Do stuff with files and names.
}
Both of these assume that both collections have the same number of items. The for loop will only iterate as many times as the smaller one, and the smaller enumerator will return false from MoveNext when it runs out of items. If one collection is bigger than the other, the 'extra' items won't get processed, and you'll need to figure out what to do with them.
I guess the files array and the names array have the same indices.
When this is the case AND you always want the same index at one time you do this:
for (int key = 0; key < files.Length; ++key)
{
// access names[key] and files[key] here
}
You can try something like this:
var pairs = files.Zip(names, (f,n) => new {File=f, Name=n});
foreach (var item in pairs)
{
Console.Write(item.File);
Console.Write(item.Name);
}
I'm building an android application where I need to create a simple list of String items, which i will then add a specific control for each item in the list.
This is the list I want to create:
List<String> projects = new List<String>(); // How?
The code I was trying:
String projects = new string[] { "hey","yo","app","xamarin","c","xaml" };
I need to count the items, something like this:
int amount = projects.Count(); // Can I do this?
Then adding the controls for each item in the list
// Add the tiles, one by one
for (int i = 0; i < amount; i++)
{
// Inflate the tile
var tile = LayoutInflater.Inflate (Resource.Layout.Tile, null);
// Set its attributes
tile.FindViewById<TextView> (Resource.Id.projectName).Text = currentProject;
// Add the tile
projectScrollView.AddView (tile);
}
"currentProject" string is retrieved from SharedPreferences, just haven't got that far yet
var projects = new List<String>() { "hey","yo","app","xamarin","c","xaml" };
if you are using the array to store what values you want in your list use the foreach
List<string>project = new List<string>();
string[] projects = { "hey","yo","app","xamarin","c","xaml" };
foreach(string str in projects)
{
project.Add(str);
}
for (int i = 0; i < projects.Length; i++)
{
// Inflate the tile
var tile = LayoutInflater.Inflate (Resource.Layout.Tile, null);
// Set its attributes
tile.FindViewById<TextView> (Resource.Id.projectName).Text = currentProject;
// Add the tile
projectScrollView.AddView (tile);
}
// you can get items from your list by using project.Count, your List<string> instead of projects.Length your array and take information from your list and output your tiles that way
To initialize a List<string> with collection initializer use the below syntax.
List<String> projects = new List<String>(){"hey","yo","app","xamarin","c","xaml"};
Count is not a method it is a property. You need property syntax.
int amount = projects.Count;
int amount = projects.Length;
that is how you can set the value of the int. It doesnt appear that you are populating your list with that code.
After trying a few error checking methods I have come to the conclusion I need help solving this problem.
How am I not catch this "index out of range" error. What can I do to avoid this problem in the future for good practice?
public void loadFromFile()
{
OpenFileDialog oFile = new OpenFileDialog();
oFile.Title = "Open text file";
oFile.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*";
oFile.FilterIndex = 1;
oFile.InitialDirectory = Application.StartupPath;
oFile.AddExtension = true;
oFile.CheckFileExists = true;
oFile.CheckPathExists = true;
// Open and clean Duplicates
String[] lines;
List<string> temp = new List<string>();
List<string> newlist = new List<string>();
if(oFile.ShowDialog() == DialogResult.OK)
{
// Dummy file has 6 lines of text. Filename:DuplicatFile.txt
// 3 duplicate lines and 3 not.
lines = File.ReadAllLines(oFile.FileName, System.Text.Encoding.UTF8);
// Copy array to temporary array
for (int index=0; index < lines.Length; index++)
{
// System.ArgumentOutOfRangeException was unhandled
// Index was out of range. Must be non-negative and less than the size of the collection.
if (lines[index].Length >= 0)
{
temp[index] = lines[index];
}
}
// Check for duplicates. If duplicate ignore if non-duplicate add to list.
foreach (string line in temp)
{
if (!newlist.Contains(line))
{
newlist.Add(line);
}
}
// Clear listbox and add new list to listbox.
lstBox.Items.Clear();
foreach (string strNewLine in newlist)
{
lstBox.Items.Add(strNewLine);
}
}
}
List<string> temp = new List<string>();
...
temp[index] = lines[index];
temp starts out with 0 size. Any index is out of range.
You can fix this by using temp.Add, to make the list grow dynamically:
temp.Add(lines[index]);
Mud has the correct answer for the ArgumentOutOfRangeException. You can simplify all the code in your if statement with Linq to be the following:
lines = File.ReadAllLines(oFile.FileName, System.Text.Encoding.UTF8);
lstBox.Items.AddRange(lines.Distinct().ToArray());
The problem isn't that the "lines" index is out of range - it's that the "temp" index is out of range... you have created a new List called "temp" but there is nothing in it.. it's length is 0!
Instead of copying from one index to another, you should use the .Add method:
temp.Add(lines[index])
of course... there better ways to duplicate an array, but this is closest to what you present above and answers your question directly.
You get that error, because there is no elements at that index in list temp. (temp is empty). You can fill it with temp.Add(value).
Another way to create temp list is to use temp = newlist.ToList().
I would suggest to use LINQ: You can use
lstBox.Items.Clear();
foreach (var line in lines.Distinct())
lstBox.Items.Add(line);
instead of all this code:
// Copy array to temporary array
for (int index=0; index < lines.Length; index++)
{
// System.ArgumentOutOfRangeException was unhandled
// Index was out of range. Must be non-negative and less than the size of the collection.
if (lines[index].Length >= 0)
{
temp[index] = lines[index];
}
}
// Check for duplicates. If duplicate ignore if non-duplicate add to list.
foreach (string line in temp)
{
if (!newlist.Contains(line))
{
newlist.Add(line);
}
}
lstBox.Items.Clear();
foreach (string strNewLine in newlist)
{
lstBox.Items.Add(strNewLine);
}
to simple:
I want to remove the element form the array. Actually I don't know the the index of the element and want to remove through it's value. I have tried a lot but fail. This is the function which i used to add element in the Array
string [] Arr;
int i = 0;
public void AddTOList(string ItemName)
{
Arr[i] = ItemName;
i++;
}
And I want to remove the element by the value. I know the below function is wrong but I want to explain what I want:
public void RemoveFromList(string ItemName)
{
A["Some_String"] = null;
}
Thanks
If you want to remove items by a string key then use a Dictionary
var d = new Dictionary<string, int>();
d.Add("Key1", 3);
int t = d["Key1"];
Or something like that.
Array has a fixed size, which is not suitable for your requirement. Instead you can use List<string>.
List<string> myList = new List<string>();
//add an item
myList.Add("hi");
//remove an item by its value
myList.Remove("hi");
List<string> list = new List<string>(A);
list.Remove(ItemName);
A = list.ToArray();
and #see Array.Resize
and #see Array.IndexOf
You can iterate through every value in array and if found then remove it. Something like this
string[] arr = new string[] { "apple", "ball", "cat", "dog", "elephant", "fan", "goat", "hat" };
string itemToRemove = "fan";
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == itemToRemove)
{
arr[i]=null;
break;
}
}