Need help populating Listbox with json, currently displaying empty values - c#

im completly new to programing,
im trying to create something like a manga organizing tool in windows forms, but im getting stuck in populating a listbox with data from a deserialized json string, it currently is only displaying boolean values correctly all other values are "0" even strings.
i have a button to do this:
using (OpenFileDialog openFileDialog = new OpenFileDialog() { Filter = "Json Files|*.json", ValidateNames = true, Multiselect = false })
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var fileStream = openFileDialog.OpenFile();
using (StreamReader sr = new StreamReader(fileStream))
{
string fileContent = sr.ReadToEnd();
ComicList comics = JsonConvert.DeserializeObject<ComicList>(fileContent);
Manga_listBox.DataSource = DisplayComic.FullList;
//ignore this little bit it's just so i can see what's happening
label1.Text = Convert.ToString(comics.Comics.Count);
label1.Text = Convert.ToString(DisplayComic.FullList);
}
}
}
and i have the following classes like so:
public class Comic
{
private string Manga;
private int Chapters;
private bool isFinished;
private int LastReadCH;
public string Manga1 { get => Manga; set => Manga = value; }
public int Chapters1 { get => Chapters; set => Chapters = value; }
public bool IsFinished { get => isFinished; set => isFinished = value; }
public int LastReadCH1 { get => LastReadCH; set => LastReadCH = value; }
public Comic(Comic asd)
{
this.Manga = Manga1;
this.Chapters = Chapters1;
this.IsFinished = IsFinished;
this.LastReadCH = LastReadCH1;
}
public override string ToString()
{
return string.Format("{0}, {1}, {2}, {3}",
this.Manga, this.Chapters, this.IsFinished, this.LastReadCH);
}
}
and
public class ComicList
{
private List<Comic> comics;
public List<Comic> Comics { get => comics; set => comics = value; }
}
and
public class DisplayComic
{
static DisplayComic()
{
using (OpenFileDialog openFileDialog = new OpenFileDialog() { Filter = "Json Files|*.json", ValidateNames = true, Multiselect = false })
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var fileStream = openFileDialog.OpenFile();
using (StreamReader sr = new StreamReader(fileStream))
{
string fileContent = sr.ReadToEnd();
ComicList comics = JsonConvert.DeserializeObject<ComicList>(fileContent);
FullList = comics.Comics;
}
}
}
}
private static List<Comic> fullList;
public static List<Comic> FullList { get => fullList; set => fullList = value; }
public static List<Comic> GetComcis()
{
return FullList;
}
}
code is probably quite messy, then again I'm completely new and have been following all kind of information online, also for some reason, the open dialog windows is opening twice I don't understand why.
here is the json file I'm using:
{
"Comics": [
{
"Manga": "MangaNumber1",
"Chapters": 85,
"isFinished": true,
"LastReadCH": 85
},
{
"Manga": "MangaNumber2",
"Chapters": 112,
"isFinished": true,
"LastReadCH": 112
},
{
"Manga": "MangaNumber3",
"Chapters": 117,
"isFinished": true,
"LastReadCH": 117
},
{
"Manga": "MangaNumber4",
"Chapters": 74,
"isFinished": true,
"LastReadCH": 74
}
]
}
I've tried pretty much anyone with my "expertise" could, changing all kind of variable names and so on, would really appreciate some help.
here's a screenshot of the problem:
as you can see only the boolean values are actually correct, otherwise they'd be false, all other values though...
EDIT:
the result im hopping for is to populate the listbox with the manga names, (thank you #beeker for that property thing) and once i select said manga then i want to create some other objects such as labels and text boxes to view and edit the values of the chapters etc, also i would like to be able to see what is being parsed by the json file how ever when i do this:
label1.Text = Convert.ToString(comics);
i get the label with the text "Manga_Organizer_2.ComicList"
By the way when i say im new, i mean i only ever did stuff with console apps using only "if" functions this whole parsing json, openfiledialog, and even classes usage is completly new. I also have no background in programing with any other language c# is the first and im loving it even though having terrible difficulties :)
Answer/Update:
All is good now :)
all i had to do was set
DisplayComic.FullList = comics.Comics;
in the openfiledialog right before setting the datasource for the listbox.
In the end it looks like this:
string fileContent = sr.ReadToEnd();
ComicList comics = JsonConvert.DeserializeObject<ComicList>(fileContent);
DisplayComic.FullList = comics.Comics;
Manga_listBox.DataSource = DisplayComic.FullList;
Manga_listBox.DisplayMember = "manga";
also removed the encapsulations alltogether in the comic class, in the end it looks like this:
public class Comic
{
public string Manga { get; set; }
public double Chapters { get; set; }
public bool IsFinished { get; set; }
public double LastReadCH { get; set; }
public string StartedOn { get; set; }
public string FinishedOn { get; set; }
}
and also the displaycomic class looks like this:
public class DisplayComic
{
public static List<Comic> FullList { get; set; }
public static List<Comic> GetComcis()
{
return FullList;
}
}
Also, after all this trouble came even more, i could deserialize the json, parse it to a string and then to a list, load, save and edit it and i also managed to serialize it back together, and with a savefiledialog create a file for it, however when i did, i would be unable to re-deserialize it once again, something about the json had changed (from object to array or vice versa), aside from that i also had problems with datetime stuff, couldn't load it correctly from a string and so on, anyway after a bunch of mishaps, and litteraly 17hours of looking at code with a puzzled face i finnaly finished my app :D!
It does all i want it to, load a json, save and edit it, put it back together, add and remove from it, and i learned a bunch from all this trouble, thank you all for helping, thanks to you guys learned how to set stuff to display on listboxes, and also very importantly "somewhat" learned how to debug code.
Thanks.

Try setting the listbox "DisplayMember" property so that the control knows which property of the class you want to see. Something like this...
Manga_listBox.DataSource = DisplayComic.FullList;
Manga_listBox.DisplayMember = "Manga";
Ref:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listcontrol.displaymember?view=netframework-4.8

Answer:
i removed that whole openfiledialog in the display comic class, renamed the encapsulations in the Comic class and done :D, now the openfiledialog only opens once and the values from the json are being correctly parsed to the comics list, thus enabling me to use them however i want, thank you, you pushed me in the right direction with the idea to learn debug stuff :D.
Now all that's left is learn how to create objects by selecting the different lines from the listbox, another challenge awaits this newcomer.

Related

Unable to save and then load app settings via JSON - 'System.Text.Json.JsonElement' to type 'System.Collections.Generic.List`1[System.String]'.'

Using the below code, I'm trying to save app settings data in JSON format so it's easily readable and (in theory) easy to load back directly into data structures. Unfortunately it's not working out that way.
My general strategy is to ahve a series of lists representing different types of settings which I'll drop into one ListDictionary and then save as a single JSON object. Then, in theory, I load it back to a ListDictionary and recast the values into the lists they started as.
// Paths to pin to quick menu in Windows Explorer
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
ListDictionary prefs = LoadPrefs();
quickPaths = (List<string>)prefs["quickPaths"];
}
public ListDictionary LoadPrefs()
{
if (!File.Exists(settingsFile)) return new ListDictionary();
string json = File.ReadAllText(settingsFile);
return JsonSerializer.Deserialize<ListDictionary>(json);
}
public void SavePrefs()
{
ListDictionary toSave = new ListDictionary();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonSerializer.Serialize(toSave));
}
Instead, I'm getting the error in the title on the quickPaths assignment in the Prefs() constructor. I've looked it up and there's nothing else out there about this error specifically and no workarounds I've been able to find.
I've tried iterating over the prefs["quickPaths"] value and manually adding them one at a time to the List, but that's both inelegant and doesn't work anyway. Clearly I'm doing something wrong, but I don't know what. I thought I would get from deserialize exactly what I serialized, but it seems it doesn't work that way.
Here's what the output of the save function looks like:
{"quickPaths":["C:\\output","C:\\Users","C:\\Windows"]}
Try with the Newtonsoft like this
public class Prefs
{
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
ListDictionary prefs = LoadPrefs();
quickPaths = ((JArray)prefs["quickPaths"]).ToObject<List<string>>();
}
public ListDictionary LoadPrefs()
{
if (!File.Exists(settingsFile)) return new ListDictionary();
string json = File.ReadAllText(settingsFile);
return JsonConvert.DeserializeObject<ListDictionary>(json);
}
public void SavePrefs()
{
ListDictionary toSave = new ListDictionary();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonConvert.SerializeObject(toSave));
}
}
or you can deserialize one more time, like
quickPaths = JsonSerializer.Deserialize<List<string>>(((JsonElement)prefs["quickPaths"]).GetRawText());
I tried the Newtonsoft method including the deserializing of the sub-element and it didn't work (at least not how I implemented it).
Instead I re-evaluated my data structures and got rid of the ListDictionary in favor of Dictionary<string,List> since that's what I was doing anyway.
From there, I just needed to convert the comma-separated string to a list which can be done with built-in functions like so:
// Paths to pin to quick menu in Windows Explorer
public List<string> quickPaths = new List<string>();
public string diag = "";
public string settingsFile = System.AppDomain.CurrentDomain.BaseDirectory + "customizer_settings.json";
public Prefs()
{
Dictionary<string,List<string>> prefs;
prefs = LoadPrefs();
quickPaths = prefs["quickPaths"].ToList<string>();
}
public Dictionary<string,List<string>> LoadPrefs()
{
if (!File.Exists(settingsFile)) return new Dictionary<string,List<string>>();
string json = File.ReadAllText(settingsFile);
return JsonSerializer.Deserialize<Dictionary<string,List<string>>>(json);
}
public void SavePrefs()
{
Dictionary<string,List<string>> toSave = new Dictionary<string,List<string>>();
toSave["quickPaths"] = quickPaths;
File.WriteAllText(settingsFile, JsonSerializer.Serialize(toSave));
}

Print the content from nested json in C#

I'm in the process of learning C# and now trying to learn how to work with JSON objects. For the purpose of that I'm writing a quiz game. I have managed to console writeline the content of simple JSON objects, but when they are more complex I only can log the object name. Example is shown below in the code.
The JSON object:
{
"QuestionId": "1",
"QuestionString": "What is blue?",
"Options": [
{
"Option": "The sky",
"Correct": false
},
{
"Option": "The limit",
"Correct": false
},
{
"Option": "A color",
"Correct": true
}
]
}
The class:
{
public class OptionType
{
public OptionType()
{
}
public string Option { get; set; }
public bool Correct { get; set; }
}
public class Question
{
public Question()
{
}
public string QuestionId { get; set; }
public string QuestionString { get; set; }
public List<OptionType> Options { get; set; }
}
}
And the code:
string fileName = #"C:\QuizGame\QuestionAnswer.json";
string jsonTxt = string.Empty;
using (StreamReader r = new StreamReader(fileName))
{
var json = r.ReadToEnd();
jsonTxt = json.ToString();
}
var question = JsonConvert.DeserializeObject<Question>(jsonTxt);
Console.WriteLine(question.Question)
// prints "What is blue?" // works like a charm
Console.WriteLine(question.OptionType)
// prints "System.Generic.List`1[QuizGame.OptionType]"
// and if I do a foreach:
foreach (object o in question.Options)
{
Console.WriteLine(o);
}
// prints QuizGame.OptionType x 3
My expected result is to be able to print the options for the question. It seems like at least something in the code is working since I'm able to see that there are 3 options for answer, so I guess it's something with my understanding of object oriented code / C# that is missing. Thankful for all replies.
Solved: I changed the "object" keyword to "var" (or "OptionType") and could then get to my nested objects. I've struggled for days with this. I googled and tried new things base on your input, so thanks alot!
foreach (var o in question.Options)
{
Console.WriteLine(o.Option); // loops through "Options"
Console.WriteLine(o.Correct); // loops through "Correct"
}
You are trying to print out a List. If you just want to print out all information then you maybe just want to add another foreach in your foreach-loop. Maybe sth. like this:
foreach (object o in question.Options)
{
foreach (object ob in o)
{
Console.WriteLine(ob.ToString());
}
}
But there are better approaches for that. You could for example override the ToString()-Method in your model-class and just call question.ToString().

fetch details from csv file on basis of name search c#

Step 1: I have created a C# application called : Student details
Step 2: Added four TextBoxes and named them as :
Image below to refer:
Studentname.Text
StudentSurname.Text
StudentCity.Text
StudentState.Text
DATA INSIDE CSV FILE
vikas,gadhi,mumbai,maharashtra
prem,yogi,kolkata,maha
roja,goal,orissa,oya
ram,kala,goa,barka
Issue is How do I fetch all the data(surname,city,state) of user prem into above textboxes studentsurname,studentcity,studentstate from csv file when I search the name in textbox 1 => studentname.Text as prem
Below is the Code where I am stuck at return null and code inside Load_Script_Click
void Connection_fetch_details(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
if (line.Split(',')[0].Equals(searchName))
{
Connection_fetch_details cd = new Connection_fetch_details()
{
username = line.Split(',')[1]
};
}
}
return;
}
private void Load_Script_Click(object sender, EventArgs e)
{
// load script is button
String con_env = textenv.Text.ToString();
//Address Address = GetAddress("vikas");
//textsurname.text = Address.Surname
Connection_fetch_details cd = Connection_fetch_details(con_env);
textusername.Text = cd.username;
}
==============================================================
Class file name : Address.class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DDL_SCRIPT_GENERATOR
{
public class Connection_fetch_details
{
public string username { get; set; }
}
}
The main problem is that your method is void, which means it doesn't return any value. So even though you may be finding a match, and creating a Connection_fetch_details object, you aren't returning that result back to the calling method.
This will fix that problem:
Connection_fetch_details Connection_fetch_details(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
if (line.Split(',')[0].Equals(searchName))
{
Connection_fetch_details cd = new Connection_fetch_details()
{
username = line.Split(',')[1]
};
return cd; //return the object containing the matched username
}
}
return null;
}
Now it will return a Connection_fetch_details object if there is a match, or null if there is no match.
Next, you asked about returning all the fields, not just one. For that you would need to
a) add more properties to your object
b) add more code to populate those properties from the CSV
c) add code to populate the textboxes with the results from the object.
I'm also going to rename "username" to something more relevant, since none of the field names you described in the question match that. I'm also going to rename your class to "Student", and rename your search method, for the same reason.
Here's an example:
Student searchStudent(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
var split = line.Split(',');
if (split[0].Equals(searchName))
{
Student s = new Student()
{
firstname = searchName,
surname = split[1],
city = split[2],
state = split[3]
};
return s; //return the object containing the matched name
}
}
return null;
}
private void Load_Script_Click(object sender, EventArgs e)
{
// load script is button
String con_env = textenv.Text.ToString();
//Address Address = GetAddress("vikas");
//textsurname.text = Address.Surname
Student st = searchStudent(con_env);
textsurname.Text = st.surname;
txtcity.Text = st.city;
txtstate.Text = st.state;
}
namespace DDL_SCRIPT_GENERATOR
{
public class Student
{
public string firstname { get; set; }
public string surname { get; set; }
public string city { get; set; }
public string state { get; set; }
}
}
To accomplish your goal you have to further separate your problem in more granular steps and also distinguish between what you show in your UI and what informations you hold in the background in which format.
Create a class with the desired properties
public class Student { public string Name { get; set; } ... }
Learn how to read a csv file into such an object by using an existing library like CsvHelper or CsvReader.
When you have something like List<Student> from this part. Learn how you can visualize such a thing by using some Binding (also depends on the visualization you use Winforms, WPF, etc.).
Depending on the visualization component it already supports filtering or you need to filter by yourself by using e.g. LINQ to get the matching elements students.Where(student => student.Name.StartsWith(search)).
So far a lot of smaller problems which is simply to much to answer in a single one. Please try to break down your problems into smaller ones and search for their solutions. If you get stuck, ask a new question. That's all I can do for you now.

onClick fetch data c# xml reader

I am trying to have a button that on button1_click it opens an URL to fetch live exchange rates. i am a little unsure where to actually place the algorithm. so for this purpose can we assume its a blank windows form with a single button that upon clicking prints the fetched data next to the clicked button.This is what i have keep in mind im new to c# and visual studio changed a few things that made my initial code much more confusing to me any simplification helps.
public partial class eth: Form
{
private const string InputUri = #"https://api.bitfinex.com/v1/ticker/ethusd";
public eth()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
public static List<KeyValuePair<string, decimal>> GetCurrencyListFromWeb(out DateTime currencyDate)
{
List<KeyValuePair<string, decimal>> returnList = new List<KeyValuePair<string, decimal>>();
string date = string.Empty;
currencyDate = NewMethod(returnList, ref date);
returnList.Add(new KeyValuePair<string, decimal>("usd", 1));
return returnList;
}
}
private static DateTime NewMethod(List<KeyValuePair<string, decimal>> returnList, ref string date)
{
DateTime currencyDate;
using (XmlReader xmlr = XmlReader.Create(InputUri))
{
xmlr.ReadToFollowing("bid");
while (xmlr.Read())
{
if (xmlr.NodeType != XmlNodeType.Element) continue;
if (xmlr.GetAttribute("timestamp") != null)
{
date = xmlr.GetAttribute("timestamp");
}
else returnList.Add(new KeyValuePair<string, decimal>(xmlr.GetAttribute("ask"), decimal.Parse(xmlr.GetAttribute("last_price"), CultureInfo.InvariantCulture)));
}
currencyDate = DateTime.Parse(date);
}
return currencyDate;
}
}
}
The below code is tested and working fine. You are calling a REST endpoint and it is returning a list of values as a json. I am using the HttpClient to call the endpoint and will need to add the other properties as needed from the documentation. One thing to be mindful is that the results change with the symbols provided. So, will need to provide that responsibility to another class. In fact, you should always move the external library calling to another class to comply with the SRP.
public partial class ExchangeRates : Form
{
public ExchangeRates()
{
InitializeComponent();
}
private const string InputUri = #"https://api.bitfinex.com/v1/ticker/ethusd";
private async void button1_Click(object sender, EventArgs e)
{
var result = await GetTicker();
richTextBox1.AppendText($"mid: {result.Mid}\n");
richTextBox1.AppendText($"bid: {result.Bid}\n");
richTextBox1.AppendText($"ask: {result.Ask}");
}
private static async Task<Ticker> GetTicker()
{
var httpClient = new HttpClient();
var response = await httpClient.GetAsync(InputUri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var output = JsonConvert.DeserializeObject<Ticker>(content);
return output;
}
throw new Exception("Ticker exception.");
}
}
public class Ticker
{
[DataMember(Name = "mid")]
public decimal Mid { get; set; }
[DataMember(Name = "bid")]
public decimal Bid { get; set; }
[DataMember(Name = "ask")]
public decimal Ask { get; set; }
// other properties
}
Update 1
Sorry about the issue. I have used Newtonsoft.Json package here. We can use string manipulation also but it is a bit ugly. So, use the JArray from the package to convert the result to an array.
Update 2
Ok, I scrapped the JArray part and went back to string splits.
Update 3
Really sorry, I have used a different end point earlier. I have updated answer according to the required end point. Here you will have to use the JsonConvert class from the same Newtonsoft.Json package. I have also added DataMember attributes for easy deserialization. DataMember is part of System.Runtime.Serialization namespace.
Update 4
Appended the results to a rich text box as you have requested. But, why do want them in a rich text box? These are read only data that you want to display to the users. I believe you should have different labels for each field and display them separately.

Generate checkboxes in or selectable buttons in WPF from database

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

Categories

Resources