Generate checkboxes in or selectable buttons in WPF from database - c#

I am new to C# and need a little bit of help. I want to import a table with three columns from a csv(semicolon separated) database. Then I want to go through all rows of the table, get all the rows with a "value" and then make checkboxes or multi-selectable buttons of some sort appear depending on "value". I am not sure how to do this, so I have just started with trying to import the data from the database. Here is the code I have:
class Nettstasjoner
{
public string NS { get; set; }
public string Sek { get; set; }
public string Radial { get; set; }
public string Value { get; set; }
Value = "H812"; //this will be set from a button later
static void Main(string[] args)
{
IEnumerable<string> strCSV =
File.ReadLines(#"C:\Users\thomoe\Desktop\SMSvarsel\nsdatabase.csv");
var results = from str in strCSV
let tmp = str.Split(';')
select new
{
NS = tmp[0],
Sek = tmp[1],
Radial = tmp[2]
};
foreach (var tmp in results)
{
//here I need to select all rows with the Value value in it and make a checkbox or something with the captin from the row NS(tmp[0]).
}
}
}
I am very open to other ways of doing this, including MVVM, I have just tried to do what I can with googling and so on. Now I am stuck though. Thank you so much for helping and please be very specific when answering ;) My understanding of C# is still very slim :)

You are already have a collection of data.
First, make your collection more "object-like" by explicit class decloration:
public class MyModel
{
public string NS { get; set; }
public string Sek { get; set; }
public string Radial { get; set; }
}
and change your code to
select new MyModel
{
NS = tmp[0],
Sek = tmp[1],
Radial = tmp[2]
};
Next you need to use one of item containers in your view, for example ListBox or https://msdn.microsoft.com/en-us/library/system.windows.controls.listview(v=vs.110).aspx with your custom template which will show an checkbox and anything else for your model class.
Finally you need to bind your collection from ViewModel to the ListView by using binding.
It's not completed solution but I hope it'll help you to understand the main principle.

You have to use TextFieldParser
TextFieldParser parser = new TextFieldParser(#"C:\Users\thomoe\Desktop\SMSvarsel\nsdatabase.csv");
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(";");
List<MyStruct> myData = new List<MysTruct>();
while (!parser.EndOfData)
{
//Process row
string[] fields = parser.ReadFields();
myData.Add(new MyStruct()
{
NS = fields[0],
Sek = fields[1],
Radial = fields[2]
});
}
now you will have the list of your data objects;
You can do it in many way, but I would do it like this:
creating your own control like subwindow, in that control you will have one grid where you will create rows and columns as you want and placing there your button/checkbox etc. with your params, or of course create just some grid on your window and making it in window.cs

Related

ML.net Load From Enumerable Trouble

I have been struggling to create the proper data structure for ML.net and get it to load into my application. Essentially, I have an application where the training data will be dynamic and the type and/or size will not be known prior to runtime. In addition, I have to convert the training data from a non-standard primitive types (ie. App_Bool, or App_Number... rather than simply using bool or double, etc.) So, this has been proving to be a problem as I try to convert my training data into a generic data type which can then be loaded from memory using the LoadFromEnumerable function.
I have four basic data type classes:
public class MLIntData
{
public MLIntData(string label, List<object> l)
{
Label = label;
foreach (App_Integer element in l)
Features.Add((int)element.Value);
}
public List<int> Features { get; set; } = new List<int>();
public string Label { get; set; } = "";
}
public class MLNumberData
{
public MLNumberData(string label, List<object> l)
{
Label = label;
foreach (App_Number element in l)
Features.Add((double)element.Value);
}
public List<double> Features { get; set; } = new List<double>();
public string Label { get; set; } = "";
}
public class MLBoolData
{
public MLBoolData(string label, List<object> l)
{
Label = label;
foreach (App_Boolean element in l)
Features.Add((bool)element.Value);
}
public List<bool> Features { get; set; } = new List<bool>();
public string Label { get; set; } = "";
}
public class MLTextData
{
public MLTextData(string label, List<object> l)
{
Label = label;
foreach (App_String element in l)
Features.Add(element.Value.ToString());
}
public List<string> Features { get; set; } = new List<string>();
public string Label { get; set; } = "";
}
So, each base class will contain a label for the data and then a list of features which will either be of type bool, double, int, or string.
Now, in my ML.net code I'm trying to load in the training data and then create an IDataView object of the data. First I loop through the input data (which is originally of the generic type object) then create the new classes of data.
List<object> data = new List<object>();
for(int i = 0; i < input.Count; i++)
{
MLCodifiedData codifiedData = input[i].Value as MLCodifiedData;
Type dataType = codifiedData.Features[0].GetType();
if (dataType == typeof(App_Boolean))
{
data.Add(new MLBoolData(codifiedData.Label, codifiedData.Features));
}
else if (dataType == typeof(App_Number))
{
data.Add(new MLNumberData(codifiedData.Label, codifiedData.Features));
}
else if (dataType == typeof(App_Integer))
{
data.Add(new MLIntData(codifiedData.Label, codifiedData.Features));
}
if (dataType == typeof(App_String))
{
data.Add(new MLTextData(codifiedData.Label, codifiedData.Features));
}
}
IDataView TrainingData = mlContext.Data.LoadFromEnumerable<object>(data);
I have tried creating a schema definition (which can be passed in as the second parameter in the LoadFromEnumerable method, but I can't seem to get that to work. I've also tried creating a schema using the schema builder to create a schema, but that doesn't seem to work either. Right now, I'm using one of the datasets that is included in one of the sample files. And to preempt questions, yes, I know I could simply load the data as file and read it in that way... However, in my app I need to first read in the CSV into memory, then create the data structure so I can't really use many of the examples which are geared toward reading in a CSV file using the LoadFromTextFile method. Can anyone provide support as to how I could setup a dynamic in-memory collection and get it converted into a IDataView object?

c# how to decide whether to add to sub-list or start new one

I have an sql query that provides me my data where I sometimes have lines that should be clustered (the data is aligned with an order by). The data is grouped by the field CAPName. Going through those rows line by line, I need to decide whether a new list should be initiated (content of CAPName differs to previous itteration), or whether the (already) initated list (from the previous iteration) should be added, too.
My pain lays with the location of the declaration of the relatedCapabilitySystem list.
I wanted to declare it within the if statement (Because, as I stated I need to decide whether the list from the previous iteration should be added too, or whether it should start a new list), but I can't as the compiler throws an exception, as the RLCapSys.Add(rCs); is non-existing in this content (which is only theoretically true). I understand why the compiler throws this exception. But if I declare the list on a "higher" level, than I always have a new list, which I don't want in the case that the item should be added to the list defined in the iteration(s) (1 or more) before
So what I want to achieve is, generate the list RLCapSys and add to it, in case the previous iteration contains the same CAPName (for clustering), otherwise create a new list.
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
string lastCapShown = null;
while (rdrDetail.Read())
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
//List<relatedCapabilitySystem> RLCapSys2 = new List<relatedCapabilitySystem>();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
and for reason of completness (but I think it is not needed here):
internal class CapabilitiesC
{
public List<Capability>Capabilities{ get;set;}
}
public class Capability
{
public string name { get; internal set; }
public string tower { get; internal set; }
public string color { get; internal set; }
public List<relatedCapility> related { get; set; }
}
public class relatedCapility
{
public string Capability { get; set; }
public List<relatedCapabilitySystem> systemsRelated { get; set; }
}
public class relatedCapabilitySystem
{
public string system { get; set; }
public string start { get; set; }
public string end { get; set; }
}
The purpose of your code is to take the input data and group it by capability. However, that is not immediately obvious. You can change your code to use LINQ so it becomes easier to understand and in the process solving your problem.
First you need a type to represent a record in your database. For lack of better name I will use Record:
class Record
{
public string System { get; set; }
public string Start { get; set; }
public string End { get; set; }
public string Capabilty { get; set; }
}
You can then create an iterator block to return all the records from the database (using an OR mapper like Entity Framework avoids most of this code and you can even shift some of the work from your computer to the database server):
IEnumerable<Record> GetRecords()
{
// Code to create connection and command (preferably in a using statement)
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
while (rdrDetail.Read())
{
yield return new Record {
System = rdrDetail["name"].ToString(),
Start = rdrDetail["SysStart"].ToString(),
End = rdrDetail["SysEnd"].ToString(),
Capability = rdrDetail["CAPName"].ToString()
};
}
// Close connection (proper using statement will do this)
}
Finally, you can use LINQ to perform the grouping:
var RLCaps = GetRecords()
.GroupBy(
record => record.Capability,
(capability, records) => new relatedCapility
{
Capability = capability ,
systemsRelated = records
.Select(record => new relatedCapabilitySystem
{
system = record.System,
start = record.Start,
end = record.End
})
.ToList()
})
.ToList();
Why not just assign it as NULL. The pattern would be
List<> myList = null;
if(condition)
{
myList = new List<>();
}
else
{
myList = previousList;
}
myList.Add();
previousList = myList;
I've got it working now. Thx everyone for your help. #martin, thx for your solution, you have put quite some effort into this, but that would have required for me to completely re-write my code. I am sure your approach would work and will be my next approach should I have a similar problem again.
It was a combination of the other answers that helped me figure it out. Let me show you what I ended up with:
SqlCommand cmdDetail = new SqlCommand(SQL_SubSytemsToCapability, DBConDetail);
SqlDataReader rdrDetail = cmdDetail.ExecuteReader();
List<relatedCapility> RLCaps = new List<relatedCapility>();
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
string lastCapShown = null;
while (rdrDetail.Read())
{
if (lastCapShown != rdrDetail["CAPName"].ToString())
{
RLCapSys = relatedCapabilitySystemList();
relatedCapility rC = new relatedCapility
{
Capability = rdrDetail["CAPName"].ToString(),
systemsRelated = RLCapSys,
};
RLCaps.Add(rC);
}
relatedCapabilitySystem rCs = new relatedCapabilitySystem
{
system = rdrDetail["name"].ToString(),
start = rdrDetail["SysStart"].ToString(),
end = rdrDetail["SysEnd"].ToString(),
};
RLCapSys.Add(rCs);
// method to compare the last related Capability shown create a new related Capabilty entry or add to the existing releated Capabilty related system list
lastCapShown = rdrDetail["CAPName"].ToString();
}
DBConDetail.Close();
So that's the section already shown bevor including my changes. Plus I added this:
private List<relatedCapabilitySystem> relatedCapabilitySystemList()
{
List<relatedCapabilitySystem> RLCapSys = new List<relatedCapabilitySystem>();
return RLCapSys;
}
Now I have new list reference everytime the CapName changes that is then added to the "higher" list. Before I had the issue of the very same list repeatedly assigned rather than a fresh one started. So thx again for your effort.

C# lists and datagridviews

I'm trying to populate a datagrid with info out of a list, and then after pressing a save button it saves the datagrid changes back into the list.
int pos = MainMenu.myList.FindIndex(x => x.ID == validID);
var tempStu = MainMenu.myList[pos];
if (tempStu is DormStudent)
{
DormStudent tempDorm = tempStu as DormStudent;
nameTextBox.Text = tempDorm.Name;
var blist = new BindingList<Student>(tempDorm.Grades);
var source = new BindingSource(blist, null);
gradesDataGridView.DataSource = source;
}
else
{
nameTextBox.Text = tempStu.Name;
var blist = new BindingList<Student>(tempStu.Grades);
var source = new BindingSource(blist, null);
gradesDataGridView.DataSource = source;
}
and when you push the save button (i don't have the save part as nice)
int pos = MainMenu.myList.FindIndex(x => x.ID == validID);
var tempStu = MainMenu.myList[pos];
if (tempStu is DormStudent)
{
DormStudent tempDorm = tempStu as DormStudent;
tempDorm.Grades = gradesDataGridView;
MainMenu.myList[pos] = tempDorm;
}
else
{
tempStu.Grades = gradesDataGridView;
MainMenu.myList[pos] = tempStu;
}
here's the Student class that the grades is in.
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public List<int> Grades;
public Student()
{
ID = 0;
Name = "No Student";
Grades = new List<int>();
}
public Student(int i, string n)
{
ID = i;
Name = n;
Grades = new List<int>();
}
}
I can't get either the converting the list to the datagrid when the check button is pressed or back when the save button is pressed. can anyone help with that?
Looking at your code I see mainly one issue. Note, however, that I am far from being an expert in Data Binding!
I see that you are creating a BindingList<> for type Student. I hope you actually want to display one Student's grades top down.
If instead you really wanted to display all Students top down and their grades in the columns from left to right you would have to do some serious twisting!
Now the BindingList should bind the Grades not a Student, so the first idea would be to change it to BindingList<int>.
However afaik unnamed variables make bad DataMembers. To display them the data should be Properties.
So I created a new class Grade that wraps the integer. For good measure I also added a string Test to hold a description of the the test. Leave it out if you don't want it!
public class Grade
{
public int Points { get; set; }
public string Test { get; set; }
public Grade()
{ Points = 0; Test = "No Test"; }
public Grade(int points, string test)
{ Points = points; Test = test; }
}
This calls for changes in the Student class:
public List<Grade> Grades;
and (twice)
Grades = new List<Grade>();
The two lines creating the BindingList now look like this:
var blist = new BindingList<Grade>(tempDorm.Grades);
var blist = new BindingList<Grade>(tempStu.Grades);
The good news is that not only the data now load fine into the DataGridView; there is also no need at all for the save action, as the Data are bound two-way and every change actually happens in the List!
Only in the event of needing an undo function will you need some more coding, but that is another matter..

Adding list to list and reading from it?

I have a problem where I need to store a List in another Class/List.
I have this class:
public class InformationMyTravels
{
public string MyTravelsDate { get; set; }
public string MyTravelsFromLocation { get; set; }
public string MyTravelsToLocation { get; set; }
}
And I can populate a List and then save it to my isolated storage (AppSettings).
The problem is that when I have more than one "Overview" below, then the following code will just append the travel history.
What I need is a separation of "Overview", so that the List I populate for each Overview is saved in another Class/List, which can contain the x-number of "Overview" lists I fetch.
private async Task Fetch()
{
AppSettings localStorage = new AppSettings();
List<InformationMyTravels> mytravelsreturned = new List<InformationMyTravels>();
// I need to separate the returned data per Overview
foreach (Overview loaded in localStorage.OverviewSetting)
{
string mytravelsHtml = await WebRequests.LoadPageAsyncSpecificRKMyTravels(loaded.CardOverviewID);
HtmlDocument htmlDocumentmytravels = new HtmlDocument();
htmlDocumentmytravels.LoadHtml(mytravelsHtml);
foreach (HtmlNode table in htmlDocumentmytravels.DocumentNode.SelectNodes("//table[#class='table']"))
{
foreach (HtmlNode row in table.SelectNodes("tr"))
{
InformationMyTravels newTravel = new InformationMyTravels();
newTravel.MyTravelsDate = row.SelectSingleNode("td[1]").InnerText.Trim();
newTravel.MyTravelsFromLocation = row.SelectSingleNode("td[3]").InnerText.Trim();
newTravel.MyTravelsToLocation = row.SelectSingleNode("td[5]").InnerText.Trim();
// Here it just appends with newTravel's
mytravelsreturned.Add(newTravel);
}
}
mytravelsreturned.Reverse();
}
localStorage.MyTravelsSetting = mytravelsreturned;
}
So how do I take "mytravelsreturned" and add this to another Class/List?
And afterwards I need to select the specific listindex from the new class and load the travels into a listbox.ItemsSource
Wanted hierarchy:
Class/List
InformationMyTravels (0)
MyTravelsDate(0)
MyTravelsFromLocation (0)
MyTravelsToLocation(0)
MyTravelsDate(1)
MyTravelsFromLocation (1)
MyTravelsToLocation(1)
etc.
InformationMyTravels (1)
MyTravelsDate(0)
MyTravelsFromLocation (0)
MyTravelsToLocation(0)
MyTravelsDate(1)
MyTravelsFromLocation (1)
MyTravelsToLocation(1)
etc.
I then need to load e.g InformationMyTravels (1) into a listbox.ItemsSource
I hope it makes sense.
Below is the untested code(I am not on Windows currently). It should give you the Idea.
Class InformationMyTravelsList<T> : List<T>
{
public int id{get; private set;}
public void AddInfo(int ID, InformationMyTravels info)
{
this.id = ID;
this.Add(info);
}
}
Class Main
{
void myLogic()
{
InformationMyTravels info = new InformationMyTravels();
InformationMyTravelsList<InformationMyTravels> infoList = new InformationMyTravelsList<InformationMyTravels>();
infoList.AddInfo(info); //add as many you want
List<InformationMyTravelsList<InformationMyTravels>> myList = new InformationMyTravelsList<InformationMyTravels>>();
myList.Add(infoList);
myListBox.DataSource = myList;
}
}

C# List - Sorting Data

I am trying to add elements into a list, order them and then output them, there a number of "columns" if you like, per list
List<Info> infoList = new List<Info>();
while (dr.Read())
{
meeting_id = dr.GetValue(0).ToString();
try
{
Appointment appointment = Appointment.Bind(service, new ItemId(meeting_id));
Info data = new Info();
data.Start = appointment.Start;
data.Fruit = Convert.ToInt32(dr.GetValue(1));
data.Nuts = Convert.ToInt32(dr.GetValue(2));
infoList.Add(data);
}
Then to output it I want to order it by Start and then display all associated columns
for (int i = 0; i < infoList.Count; i++)
{
meet = meet + infoList[i];
}
First question: is the way I am inputting the data right?
Second question: How to I output all the columns to display all the associated columns? Is this possible? Is there a better practice?
Thanks
EDIT:
The class if you are interested:
public class Info
{
public DateTime Start { get; set; }
public int Fruit { get; set; }
public int Nuts { get; set; }
}
You can use Enumerable.OrderBy extension for enumerating your collection in some particular order (e.g. ordered by Start property value):
foreach(var info in infoList.OrderBy(i => i.Start))
{
// use info object here
// info.Fruits
// info.Nuts
}
BTW consider to add sorting on database side - that will be more efficient

Categories

Resources