Most effective way to Rearrange Custom Controls in a FlowLayoutPanel? - c#

I am creating a video parser in C# WinForms. You select files, they get parsed then a custom control (ViewWideNarrow ) is created showing the video's name, duration and so forth, which is then added to FlowLayoutPanel1.
The user then selects in what order to display said custom controls, alphabetically or by Bitrate. My code below works just fine, but it takes a couple of seconds to reorganize around two hundred controls, and I fear on weaker machines it might take even longer, and I wish to optimize the code, or how I handle the custom controls to begin with in respect with modest memory usage. Right now I do not have leaks or high usage.
I decided to use an ID'ing system like so to save memory:
//ID of the control, and ViewWideNarrow is the custom control itself shown in the FlowLayoutPanel
public Dictionary<int, ViewWideNarrow> Lookup_VideoView = new Dictionary<int, ViewWideNarrow>();
//ID of the control and its respective video's bitrate.
public Dictionary<int, int> Lookup_Bitrate = new Dictionary<int, int>();
//ID of the control and its respective video's title.
public Dictionary<int, string> Lookup_Alphabetical = new Dictionary<int, string>();
Which I then organize accordingly to use FI-FO kind of system using:
Lookup_Alphabetical = Lookup_Alphabetical.OrderBy(item => item.Value, new ComparerAlphabet()).ToDictionary(item => item.Key, item => item.Value);
Lookup_Bitrate = Lookup_Bitrate.OrderBy(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
And below is the actual control organization, which is what actually takes a while to complete and is what I suspect needs working on.
private void OrganizeControls()
{
if (SortingOption == Sorting.Alphabetical)
{
int h = 0;
foreach (var item in Lookup_Alphabetical)
{
int key = item.Key;
string value = item.Value;
ViewWideNarrow View = Lookup_VideoView[key];
if (FlowLayoutPanel1.Controls.Contains(View))
{
FlowLayoutPanel1.Controls.SetChildIndex(View, h);
h++;
}
}
}
}

Related

C# Merging dictionaries inside of an object

I realize there are tons of posts on this subject but I still haven't found what I am looking for reading the answers.
I get two objects A and B, that both have a dictionary and a set of booleans. Dictionaries are shaped the same, just the data differs. My end goal here is to create a 3rd object C, copy of A with its dictionary updated to have values from A and B according to a set of rules (for instance C.dictionary would have all A.dictionary.[x] if A.Dictionary.[x].Value.days < 5 and and all B.dictionary.[x] if days > 5).
If you have a solution for this that would be awesome, otherwise, here is where I am at and where I fail:
I am trying to create a new dictionary and and loop through A and B to add the values... I'll add the rules after. Once I will have it I'll find a way to place this dictionary into object C. (tell me if over engineered, I am very new to C#).
var dict = new Dictionary<DateTime, TypeData>();
foreach (var item in A.dictionary.keys)
{
dict.Add(A.Dictionary[item]);
}
This does not work for 2 reasons:
item does represents A.dictionary[x].key not A.dictionary[x] (also contains value and non public members)
"Add" gets underlines and does not show more info for the error
Thanks a lot for checking my post !
try this
var dictionariesToCombine = new Dictionary<DateTime, TypeData>[] {dictA,dictB};
Dictionary<int, int> dictC = new Dictionary<DateTime, TypeData>();
for (var i=0; i< dictionariesToCombine.Length; i++)
{
foreach (var item in dictionariesToCombine[i])
{
if(
(i==0 and your conditions)
|| (i==1 and your conditions)
)
resultDict.Add(item.Key, item.Value);
}
}

Truncate/delete first x rows from a Frame

I have a Frame<int, string> which consists of a OHLCV data. I'm calculating technical analysis indicators for that Frame and since the first few records aren't accurate due to the fact that there are at the very begin, I have to remove them. How do I do that?
public override Frame<int, string> PopulateIndicators(Frame<int, string> dataFrame)
{
var candles = dataFrame.Rows.Select(kvp => new Candle
{
Timestamp = kvp.Value.GetAs<DateTime>("Timestamp"),
Open = kvp.Value.GetAs<decimal>("Open"),
High = kvp.Value.GetAs<decimal>("High"),
Low = kvp.Value.GetAs<decimal>("Low"),
Close = kvp.Value.GetAs<decimal>("Close"),
Volume = kvp.Value.GetAs<decimal>("Volume")
}).Observations.Select(e => e.Value).ToList<IOhlcv>();
// TODO: Truncate/remove the first 50 rows
dataFrame.AddColumn("Rsi", candles.Rsi(14));
}
Most operations in Deedle are expressed in terms of row keys, rather than indices. The idea behind this is that, if you work with ordederd data, you should have some ordered row keys.
This means that this is easier to do based on row keys. However, if you have an ordered row index, you can get a key at a certain location and then use it for filtering in Where. I would try something like:
var firstKey = dataFrame.GetRowKeyAt(50);
var after50 = dataFrame.Where(kvp => kvp.Key > firstKey);

Searching dictionary for matching key from a textbox C#

I'm reading a csv file that contains abbreviations and the full version for example LOL,Laughing out Loud. I've created a dictionary where the abbreviation is the key and full version is the value.
'''
private void btnFilter_Click(object sender, RoutedEventArgs e)
{
var keys = new List<string>();
var values = new List<string>();
using (var rd = new StreamReader("textwords.csv"))
{
while (!rd.EndOfStream)
{
var splits = rd.ReadLine().Split(',');
keys.Add(splits[0]);
values.Add(splits[1]);
}
}
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v }).ToDictionary(x => x.Key, x => x.Value);
foreach (var k in dictionary)
{
//
}
aMessage.MessageContent = txtContent.Text;
}
'''
I'm using a button that will check if the textbox txtContent contains any of the abbreviations in the text and change this to the full version. So if the textbox contained the following. "Going to be AFK" after the button click it would change this to "Going to be away from keyboard".
I want to write a foreach loop that would check for any abbreviations, elongate them and then save this to a string variable MessageContent.
Would anyone be able to show me the best way to go about this as I'm not sure how to do this with the input from a textbox?
Thanks
You can just use LINQ to read and create a dictionary object:
var dictionary = File.ReadAllLines(#"textwords.csv")
.Select(x => x.Split(",",StringSplitOptions.RemoveEmptyEntries))
.ToDictionary(key => key.FirstOrDefault().Trim(),
value => value.Skip(1).FirstOrDefault().Trim());
If the abbrevs are correct, i.e. you don't need fuzzy logic, you can use a variety of .NET objects and search the keys rather quickly.
if(dict.ContainsKey(myKey))
{
}
I did that freehand, so it might be dict.Keys.Contains() or similar. The point is you can search the dictionary directly rather than loop.
If you need to do a more fuzzy search, you can utilize LINQ to write queries to iterate over collections of objects extremely fast (and few lines).
As for yours, you would iterate over dictionary.Keys to seach keys, but I still don't see why you need the extra code.

C# Windows Form: Using List<KeyValuePair> to create a Top 10 Leaderboard

I am trying to create a Top 10 Leaderboard in C# Windows Form, based on player usernames and scores read in from a text file.
E.g. of line in text file:
Denna~21
This is my code so far:
private void scrnLeaderboard_Load(object sender, EventArgs e)
{
string[] players = FileMethods.ReadLines();
// Create List<KeyValuePair> to hold all player usernames and scores
List<KeyValuePair<int, string>> playerNamesScores = new List<KeyValuePair<int, string>>();
foreach (var item in players)
{
string[] playerDetails = item.Split('~');
if (playerDetails.Length == 2)
// Player's username and score added to List<KeyValuePair> playersNamesScores
// Key is score, Value is username
playerNamesScores.Add(new KeyValuePair<int, string>(Convert.ToInt32(playerDetails[1]), playerDetails[0].ToString()));
}
// Sorting the scores in descending order
var sortedScores = playerNamesScores.OrderByDescending(x => x).ToList<KeyValuePair<int, string>>();
// Assigning the appropriate values to each label's text on the leaderboard
lblPos1.Text = String.Format("{0}: \t{1}", sortedScores[0].Value, sortedScores[0].Key);
lblPos2.Text = String.Format("{0}: \t{1}", sortedScores[1].Value, sortedScores[1].Key);
lblPos3.Text = String.Format("{0}: \t{1}", sortedScores[2].Value, sortedScores[2].Key);
lblPos4.Text = String.Format("{0}: \t{1}", sortedScores[3].Value, sortedScores[3].Key);
lblPos5.Text = String.Format("{0}: \t{1}", sortedScores[4].Value, sortedScores[4].Key);
lblPos6.Text = String.Format("{0}: \t{1}", sortedScores[5].Value, sortedScores[5].Key);
lblPos7.Text = String.Format("{0}: \t{1}", sortedScores[6].Value, sortedScores[6].Key);
lblPos8.Text = String.Format("{0}: \t{1}", sortedScores[7].Value, sortedScores[7].Key);
lblPos9.Text = String.Format("{0}: \t{1}", sortedScores[8].Value, sortedScores[8].Key);
lblPos10.Text = String.Format("{0}: \t{1}", sortedScores[9].Value, sortedScores[9].Key);
}
FileMethods.ReadLines() is just this:
string filepath = #"previousPlayers.txt";
string[] players = File.ReadLines(filepath).ToArray();
return players;
Each time I compile the code, I get this error,
'At least one object must implement IComparable.',
on this line:
var sortedScores = playerNamesScores.OrderByDescending(x => x).ToList<KeyValuePair<int, string>>();
I'm not sure what this means, or how make my code work.
Any help is much appreciated!
OrderByDescending method sorts items by key, returned by key selector (first argument of this method). It then uses these keys of individual items to compare one item with another to decide, which items comes first and which comes later. By default, it uses method of IComparable interface implemented by items for this comparison. But you specified x => x as a key selector, which returns objects of type KeyValuePair<int, string>, which does not implement IComparable. Thus there is no way to sort them and you get "At least one object must implement IComparable" error.
Since all you want is to order items by score (of type int) and int implements IComparable, you can simply use selector that returns score:
var sortedScores = playerNamesScores.OrderByDescending(x => x.Key).ToList<KeyValuePair<int, string>>();
If more sophisticated way of sorting items is needed (e.g. you need playes with the same score to be sorted alphabetically), you can declare class that implements IComparer<KeyValuePair<int, string>> interface and pass it's instance as a second argument of OrderByDescending method.
Where do you define x?
// Sorting the scores in descending order
var sortedScores = playerNamesScores.OrderByDescending(x => x).ToList<KeyValuePair<int, string>>();
In this section, you normally name the variable you want to sort on, but from the examples shown here 'x' seems to just come out of nowhere.
You are trying to sort a KeyValuePair as well, instead of trying to sort on the int or the string. A KeyValuePair itself isn't sortable.

Sorted-ception - Adding to a SortedList inside a SortedDictionary

So this is a thing I've been looking at for quite some time now. And I can't see where I did it wrong. Hope you guys can help ^^
So my problem is that I have 311 objects which I try to sort into a SortedDictionary<int, SortedList<int, Entry>> (>). However the result is a dictionary with only 112 objects. Where do the rest go and why ain't they going where the should?
public SortedDictionary<int, SortedList<int, Entry>> GetSortedByForum(int id)
{
SortedDictionary<int, SortedList<int, Entry>> result = new SortedDictionary<int, SortedList<int, Entry>>();
foreach (var e in GetByForum(id))
{
e.fk_entry = e.fk_entry == null
? 0
: e.fk_entry;
if (!result.ContainsKey((int)e.fk_entry))
result[(int)e.fk_entry] = new SortedList<int, Entry>();
if (!result[(int)e.fk_entry].ContainsKey(e.fk_language))
result[(int)e.fk_entry][e.fk_language] = new Entry();
result[(int)e.fk_entry][e.fk_language] = e;
}
return result;
}
Background info might help:
fk_entry is the objects parent. An entry can only have on parent, but can however have multiple children.
fk_language is the language of the entry, a entry can have several translations
entry is an article of some sort. The dictionary should order them by parent, and then by language.
Your problem is in the following line of code. It only adds the first entry for a particular language - later entries for the same parent and language are not added.
if (!result[(int)e.fk_entry].ContainsKey(e.fk_language))
result[(int)e.fk_entry][e.fk_language] = new Entry();
Keeping as much of your code the same as possible, I think you want a collection of entries associated with each particular language:
var result = new SortedDictionary<int, SortedList<int, List<Entry>>>();
...
...
if (!result[(int)e.fk_entry].ContainsKey(e.fk_language))
result[(int)e.fk_entry][e.fk_language] = new List<Entry>();
result[(int)e.fk_entry][e.fk_language].Add(e);
Given your comment, "How would I do it if I wanted to also sort the different translations by fk_language?", you could try incorporating something like this into your loop:
var result2 = new SortedDictionary<int, Dictionary<int, List<int>>>();
if (!result2[(int)e.fk_entry].ContainsKey(e.some_unique_entry_id))
result2[(int)e.fk_entry][e.some_unique_entry_id] = new List<int>();
result2[(int)e.fk_entry][e.some_unique_entry_id].Add(e.fk_language);
Or use LINQ to query the first SortedDictionary, to manipulate it into the format you need.

Categories