I'm stuck on following:
I have an application gui as follows:
I created a seperate class library for 'klant' which contains a string 'Naam' and an integer 'Consumpties'and linked this to my project.
When the user adds a name (Naam) and a number of consumptions (Consumpties), these values are addes to a listbox.
Here is my code so far:
private void btnKlantToevoegen_Click(object sender, RoutedEventArgs e)
{
List<Klant> klanten = new List<Klant>();
klanten.Add(new Klant
{
Naam = txtKlantNaam.Text,
Consumpties = int.Parse(txtKlantConsumpties.Text)
}
);
for (int i = 0; i < klanten.Count; i++)
{
lbOverzicht.Items.Add(klanten[i]);
}
I need to calculate the sum of the entered clients and consumptions, but i'm stuck on how to properly achieve this.
Also, there needs to be set a maximum number of consumptions per client and a maximum of total consumptions.
How to call this?
I tried creating a method to calculate the total of consumptions, but am not able to accomplish results. The code i have for this so far is as follows:
int BerekenTotaalConsumpties(int totaalconsumpties)
{
totaalconsumpties = 0;
int consumpties = int.Parse(txtKlantConsumpties.Text);
for (int i = 0; i <= consumpties; i++)
{
totaalconsumpties += Convert.ToInt32(lbOverzicht.Items[i].ToString());
}
return totaalconsumpties;
}
The result of this method then needs to be places in a label. I would do this as follows:
lblOverzicht.Content = BerekenTotaalConsumpties();
Klant class as follows:
public class Klant
{
public string Naam;
public int Consumpties;
public override string ToString()
{
return string.Format("{0} ({1})", this.Naam, this.Consumpties);
}
}
How can the names (Naam) be sorted alphabetically?
Untill now we have
lbOverzicht.Items.SortDescriptions.Add(new SortDescription("", ListSortDirection.Ascending));
But this doens't seem to work?
How can we verify that the users enters letters in field Naam and numbers in field Consumptions?
Use the power of MVVM and the CollectionChange event of an ObservableCollection to handle the summing of any and all changes to that collection.
In your view model (which adheres to INotifyPropertyChange) declare these properties
// Zehn Kleine Jagermeister!
public ObservableCollection<Klant> Overzicht
{
get { return _Overzicht; }
set { _Overzicht = value; OnPropertyChanged(); }
}
public int AantalKlanten { get { return Overzicht?.Count ?? 0; } }
public int AantalConsumpties { get; set; }
So we have the list of Klants and the totals. Then in the constructor of the viewmodel we allocate the observable collection and subscribe to the change of collection event:
Overzicht = new ObservableCollection<Klant>();
Overzicht.CollectionChanged += (o, args) =>
{
AantalConsumpties = Overzicht.Sum(klnt => klnt.Consumpties);
OnPropertyChanged("AantalConsumpties");
OnPropertyChanged("AantalKlanten");
};
So whenever we add or remove something from the collection, we recalculate the totals. By directly calling the OnPropertyChanged methods for the totals we announce to the XAML controls which have bound to those properties, that the values have changed and to update them for the user to see.
Here is the binding in XAML
<ListBox ItemsSource="{Binding Overzicht}"/>
<TextBlock Text="{Binding AantalKlanten }"/>
<TextBlock Text="{Binding AantalConsumpties}"/>
Then to limit things on add of the button click, simply check AantalKlanten and AantalCompties before allowing an insertion (Add) into the Overzicht collection.
The below shows the add in a button click with a max of 100. You can change it as needed.
if ((myViewModel.AantalConsumpties + newvalue) < 100)
myViewModel.Overzicht.Add(new Klant() { Naam = ..., Consumpties = newvalue});
This is the Klant
public class Klant
{
public string Naam { get; set; }
public int Consumpties { get; set; }
}
New to MVVM? Check out my quick example here Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding for more information.
For the counting, try this:
int totalNaams = 0;
int totalConsumpties = 0;
foreach (Klant item in klanten)
{
totalConsumpties += item.Consumpties;
totalNaams += 0;
}
As for limiting the number of Consumpties per client, that needs to be done before you add it to the listbox. Just check to see if it's less than or equal to your max.
For limiting the total Comsumpties, You will need to calculate the new total after each new addition to the ListView. That would probably require a different approach to the counting so you're not executing the foreach more times that you need to.
New XAML
<ListView x:Name="lbOverzicht" HorizontalAlignment="Left" Height="200" Margin="435,372,0,0" VerticalAlignment="Top" Width="177">
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Naam}" />
<GridViewColumn DisplayMemberBinding="{Binding Consumpties}" />
</GridView>
</ListView.View>
</ListView>
New Code-Behind
int totalNaams = 0;
int totalConsumpties = 0;
int consumptiesTotalMax = 0;
int consumptiesMax = 0;
List<Klant> klanten = new List<Klant>();
public class Klant
{
public string Naam { get; set; }
public int Consumpties { get; set; }
public override string ToString()
{
return string.Format("{0} ({1})", this.Naam, this.Consumpties);
}
}
public void btnKlantToevoegen_Click(object sender, RoutedEventArgs e)
{
int current = int.Parse(txtKlantConsumpties.Text);
consumptiesTotalMax = int.Parse(txtTotalMax.Text);
consumptiesMax = int.Parse(txtConsumpMax.Text);
if (!string.IsNullOrEmpty(txtKlantConsumpties.Text.Trim()) && !string.IsNullOrEmpty(txtKlantNaam.Text.Trim()))
{
if (((totalConsumpties + current) <= consumptiesTotalMax) && (current <= consumptiesMax))
{
klanten.Add(new Klant
{
Naam = txtKlantNaam.Text,
Consumpties = current
}
);
lbOverzicht.ItemsSource = klanten;
totalConsumpties += current;
totalNaams += 1;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lbOverzicht.ItemsSource);
view.SortDescriptions.Add(new SortDescription("Naam", ListSortDirection.Ascending));
}
}
}
public int NumConsumptionsPerClient(string client)
{
var aux = listBox1.Items;
List<int> consumptions = new List<int>();
foreach (var i in aux)
{
if (i.ToString().Contains(client))
{
consumptions.Add(Convert.ToInt32(Regex.Match(i.ToString(), #"\d+").Value)); // only gets numbers
}
}
int sumconsumptions = 0;
foreach (int k in consumptions)
{
sumconsumptions = sumconsumptions + k;
}
return sumconsumptions;
}
public int NumConsumptions()
{
var aux = listBox1.Items;
List<int> consumptions = new List<int>();
foreach (var i in aux)
{
consumptions.Add(Convert.ToInt32(Regex.Match(i.ToString(), #"\d+").Value)); // only gets numbers
}
int sumconsumptions = 0;
foreach (int k in consumptions)
{
sumconsumptions = sumconsumptions + k;
}
return sumconsumptions;
}
public int NumClients()
{
return listBox1.Items.Count;
}
With first method you can control the max. consumptions per client. (you modify the method for your object instead of string)
Second and third methods are easy to understand.
Related
I am currently making an application which tracks information on players and monsters for a tabletop game.
Currently I've got classes for "Monsters". This class contains information such as its name, maxHP, speed, attacks etc. I've managed to make a Database which contains the default values for each type of monster. What I currently need to do is make it possible to change things such as name (Monster > Monster 1, Monster 2 etc), change its HP, and some other things.
I understand that I need to make a copy of such, but I am uncertain on how to do this.
What I currently tried is the following:
public class DatabaseService
{
public List<Player> Players { get; set; }
public List<Monster> AllMonsters { get; set; }
public List<Monster> ActiveMonsters = new List<Monster>();
public bool RollForHP = false;
//Main Database Service
public DatabaseService()
{
Players = GetPlayers();
AllMonsters = GetAllMonsters();
}
public void DoStuff()
{
AddMonsterByName("Goblin", 2);
AddMonsterByName("Adult White Dragon", 1);
AddMonsterByName("Duergar", 4);
foreach (Monster monster in ActiveMonsters) { Console.WriteLine(monster.Name); }
}
//Converts the .json list with all players to Classes, which are then stored in the list "Players" if the "IsInParty" is true
private List<Player> GetPlayers()
{
var path = #"C:\Users\MyName\source\repos\DndAdvancedInitiativeTracker\Logic\Database\Players.json";
var json = File.ReadAllText(path);
var players = JsonConvert.DeserializeObject<List<Player>>(json);
List<Player> inPartyPlayers = new List<Player>();
foreach (var player in players)
{
if (player.IsInParty == true) { inPartyPlayers.Add(player); }
}
return inPartyPlayers;
}
//Converts the .json list with all monsters to Classes, which are then stored in the list "AllMonsters"
private List<Monster> GetAllMonsters()
{
var path = #"C:\Users\MyName\source\repos\DndAdvancedInitiativeTracker\Logic\Database\Monsters.json";
var json = File.ReadAllText(path);
var monsters = JsonConvert.DeserializeObject<List<Monster>>(json);
return monsters;
}
//Adds a given monster to the "ActiveMonsters" list
public void AddMonsterByName(string monsterName, int amountOfMonsters)
{
for (int i = 0; i < amountOfMonsters; i++)
{
List<Monster> DatabaseCopy = AllMonsters.Clone();
DatabaseCopy = AllMonsters;
Monster monster = DatabaseCopy.Find(x => x.Name == monsterName);
Console.WriteLine(monster.Name);
var number = CheckIfNameExistsInList(monsterName);
monster.Name = monsterName + " " + (number + i).ToString();
ActiveMonsters.Add(monster);
}
}
private int CheckIfNameExistsInList(string monsterName)
{
var counter = 1;
foreach (var monster in ActiveMonsters)
{
if (monster.Name.Contains(monsterName))
{
counter += 1;
}
}
return counter;
}
}
In the "DoStuff" Method, I try to add 2 goblins, then a dragon, then a goblin again. The first goblin is named to "Goblin 1" correctly, but the second loop fails, because the AllMonsters' name for goblins is now "Goblin 1" because of the reference type, therefore, the second "Goblin" search in AllMonsters is never found, and returns null.
I'm not sure why you're copying your database (and doing so for every iteration of a for loop which is quite wasteful), but your current check code CheckIfNameExistsInList will always return 1 even if there are no matches in the list.
You can simplify your AddMonstersByName (and use a simple check for previous monster entries) as follows:
public void AddMonstersByName(string name, uint number = 1)
{
var count = AllMonsters.Count(x => x.Name.Contains(name));
for (var i = 1; i <= number; i++)
{
var num = count + i;
AllMonsters.Add(new Monster(){Name= name+num.ToString()});
}
}
This was tested in a simple Console App:
var dataService = new DataService();
dataService.AddMonstersByName("Goblin", 2);
dataService.AddMonstersByName("Dragon", 2);
dataService.AddMonstersByName("Goblin", 2);
foreach (var monster in dataService.AllMonsters)
{
Console.WriteLine($"{monster.Name}");
}
where
public class DataService
{
public List<Monster> AllMonsters = new List<Monster>();
public void AddMonstersByName(string name, uint number = 1)
{
var count = AllMonsters.Count(x => x.Name.Contains(name));
for (var i = 1; i <= number; i++)
{
var num = count + i;
AllMonsters.Add(new Monster(){Name= name+num.ToString()});
}
}
}
public class Monster
{
public string Name { get; set; }
}
I'm having hard times to show some simple labels in my Cartesian Bar Charts, I'm reading a lot around but nothing seems to work for me. I'm using the MVVM pattern in my project, so this is the code I have so far..
VIEW
<lvc:CartesianChart Grid.Row="2" Series="{Binding ChartDataSets}">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelsRotation="20" Labels="{Binding ColumnLabels}" Position="RightTop" >
<lvc:Axis.Separator >
<lvc:Separator Step="1"></lvc:Separator>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis LabelFormatter="{Binding Formatter}" Position="RightTop"></lvc:Axis>
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
DataModel
class DataModel : INotifyPropertyChanged
{
private double value;
public double Value
{
get => this.value;
set
{
this.value = value;
OnPropertyChanged();
}
}
private string label;
public string Label
{
get => this.label;
set
{
this.label = value;
OnPropertyChanged("Label");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel
class BackupStatsViewModel : INotifyPropertyChanged
{
ChartValues<DataModel> values = new ChartValues<DataModel>();
public SeriesCollection ChartDataSets { get; set; }
public ObservableCollection<string> ColumnLabels { get; set; }
public class ErrorPrt
{
public ErrorPrt(){
prtName = string.Empty;
Count = -1;
}
public string prtName { get; set; }
public int Count { get; set; }
}
public BackupStatsViewModel()
{
InitializeBarChartData();
}
private void InitializeBarChartData()
{
this.ColumnLabels = new ObservableCollection<string>(values.Select(dataModel => dataModel.Label));
var dataMapper = new CartesianMapper<DataModel>()
.Y(dataModel => dataModel.Value)
.Fill(dataModel => dataModel.Value > 15.0 ? Brushes.Red : Brushes.Green);
this.ChartDataSets = new SeriesCollection
{
new ColumnSeries
{
Values = values,
Configuration = dataMapper,
DataLabels = true
}
};
}
public ErrorPrt[] PrtCount(List<DataRow> rows)
{
IEnumerable<IGrouping<string, DataRow>> grouped = rows.GroupBy(s => s.Field<string>(2));
ErrorPrt[] err = new ErrorPrt[grouped.Count()];
//Omitted code for sake of brevity
ErrorPrt[] arr = err.Where(c => c != null).ToArray();
for (int i = 0; i < arr.Count(); i++)
values.Add(new DataModel() { Label = $"PRT {arr[i].prtName}", Value = arr[i].Count });
return arr;
}
}
But as you can see no labels are shown on the X axis.. really don't know how to bypass this problem in order to go on with my work..please can anyone show me the right way?
Your flow looks broken:
you first initialize the chart data from the constructor by calling InitializeBarChartData(), which also initializes the ColumnLabels collection. Then you create the underlying ErrorPtr items, which are the provider of the data for the column labels.
The result is that the ColumnLabels property is empty => no labels will be displayed.
Because you add the new ErrorPtr items to the values field and this field is of type ChartValues and this collection implements INotifyCollectionChanged, the chart will reflect those changes. You were lucky here.
But because you never update the ColumnLabels property after you have created the ErrorPtr items, the initially (after calling InitializeBarChartData from the constructor) empty ColumnLabels collection remains empty.
Solution 1
Fix the flow of your data model initialization and call InitializeBarChartData after PrtCount:
public ErrorPrt[] PrtCount(List<DataRow> rows)
{
IEnumerable<IGrouping<string, DataRow>> grouped = rows.GroupBy(s => s.Field<string>(2));
ErrorPrt[] err = new ErrorPrt[grouped.Count()];
//Omitted code for sake of brevity
ErrorPrt[] arr = err.Where(c => c != null).ToArray();
for (int i = 0; i < arr.Count(); i++)
this.values.Add(new DataModel() { Label = $"PRT {arr[i].prtName}", Value = arr[i].Count });
// Initialize the chat models.
// NOW the label data (the ErrorPrt.prtName) is generated
// and ready to be extracted from the ErrorPrt instances
InitializeBarChartData();
return arr;
}
Solution 2 (Recommended)
Since all involved collections implement INotifyCollectionChanged you can dynamically update every collection when new data arrives. You don't need to initialize the complete chart data like the SeriesCollection and the Mapper or the label formatter over and over again (like in Solution 1 - in case PrtCount will be called more than once).
You can continue to call InitializeBarChartData once from the constructor, like you are currently doing it.
Simply don't only update the values field, but also the ColumnLabels property:
public ErrorPrt[] PrtCount(List<DataRow> rows)
{
IEnumerable<IGrouping<string, DataRow>> grouped = rows.GroupBy(s => s.Field<string>(2));
ErrorPrt[] err = new ErrorPrt[grouped.Count()];
//Omitted code for sake of brevity
ErrorPrt[] arr = err.Where(c => c != null).ToArray();
for (int i = 0; i < arr.Count(); i++)
{
var newDataModel = new DataModel() { Label = $"PRT {arr[i].prtName}", Value = arr[i].Count };
// Here you update the column values
// and add the new items to the existing items of previous calls
this.values.Add(newDataModel);
// Also update the labels whenever new column data has arrived
this.ColumnLabels.Add(newDataModel.Label);
}
return arr;
}
As you see in the title above, i need to run a FOR loop to create 4 plumbers from the default constructor. After they are created (within the FOR loop), change their names, add them to the employee list and display in listbox. So basically, plumbers is actually a list declared in another class called EmployeeList. Wehn i tried changing their names to random ones, i get like an error msg saying 'Index is out of range'. Can someone help me with this?
Form Code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//create class-level employee list
EmployeeList plumbers = new EmployeeList();
private void Form1_Load(object sender, EventArgs e)
{
//run a for loop to create 4 plumbers from the default constructor.
for (int i = 0; i < plumbers.Count; i++)
plumbers[i] = new Employee();
//After they are created (within the FOR loop), change their names,
//plumbers[0].Name = "Nicole Fernandez";
//add them to the employee list
//display in listbox
foreach (Employee item in plumbers.Employees)
{
lstDisplay.Items.Add(item.DisplayData());
}
}
Employee List Class Code
class EmployeeList
{
//use private access modifier to create a list of employee
private List<Employee> employees = new List<Employee>();
public List<Employee> Employees
{
get { return employees; }
set { employees = value; }
}
//return the count of the employee list, use lambda operator
public int Count => employees.Count();
//create default constructor
public EmployeeList() { }
//create a method that adds employee sent to the list. No return value
public void AddEmp(Employee emp)
{
employees.Add(emp);//add employee to the list
}
//create employee from data sent. No return value
public void AddEmp(string inName, int inID, decimal inHourlyWage)
{
//declare a variable
Employee emp = new Employee(inName, inID, inHourlyWage);
//call the other AddEmp
AddEmp(emp);
}
//create a method that deletes employee from the list. No return value
public void DeleteEmp(Employee emp) => employees.Remove(emp);
//insert employee at the index
public void InsertEmp(Employee emp, int index) => employees.Insert(index, emp);
//create an indexer
public Employee this[int i]
{ //q12 - indexer property with exception
get
{
if (i < 0 || i >= Count)
throw new ArgumentOutOfRangeException(i.ToString());
return employees[i];
}
set { employees[i] = value; }
}
}
Employee Class Code
class Employee
{
//use auto-implemented property
public int ID { get; set; }
public string Name { get; set; }
public decimal HourlyWage { get; set; }
public decimal TotalPay { get; set; }
//create static integer that starts at 1
private static int NextID = 1;
//create a default constructor with default values
public Employee()
{
ID = NextID++;
Name = "John Doe";
HourlyWage = 15.25m;
TotalPay = 0.0m;
}
//create the custom constructor sending in 3 parameters
public Employee(string inName, int inID, decimal inHourlyWage)
{
Name = inName;//set name, no validation is required
//validate ID is between 1 and 250. if not, set to nextID available
if (inID <= 1 && inID >= 250)
NextID = inID;
//validate hourly wage is between 12.50 and 20. If not, set to 15.25
if (inHourlyWage <= 12.50m && inHourlyWage >= 20.0m)
inHourlyWage = 15.25m;
TotalPay = 0;//set total pay to 0
}
public string DisplayData() => ID + "\t" + Name + "\t" + HourlyWage.ToString("c") + "\t" + TotalPay.ToString("c");
}
First, if you want to create a list of 4 plumbers using a loop, then you need the loop to iterate 4 times. This is normally done by setting the initial counter value to 0, then looping while it is less than the number you want:
for (int i = 0; i < 4; i++)
Also, your plumbers object is of type EmployeeList, but you're trying to access it with an indexer as if it's a List or an Array. Instead, you should use the method you created to add new employees:
// Run a for loop to create 4 plumbers from the default constructor.
for (int i = 0; i < 4; i++)
{
plumbers.AddEmp(new Employee());
}
Since there doesn't appear to be any public method to update an existing plumber, we can just access the Employees list directly to rename the plumbers. There are a couple of ways to do it. One would be to use a loop and use part of the loop counter in the name:
// After they are created (within the FOR loop), change their names
for(int i = 0; i < plumbers.Employees.Count; i++)
{
plumbers.Employees[i].Name = string.Format("Plumber #{0}", i);
}
Another way is to do it by hand, giving each plumber a normal name:
plumbers.Employees[0].Name = "Nicole";
plumbers.Employees[1].Name = "Rufus";
plumbers.Employees[2].Name = "Mark";
plumbers.Employees[3].Name = "John";
Or, if you want to be really fancy, you can generate a list of names (I pulled these from the top baby names of 2016), then for each plumber grab a random name from the list and assign it (and remove it from the list so the plumbers all have unique names):
//After they are created (within the FOR loop), change their names
// Generate a list names
var nameCandidates = new List<string>
{
"Olivia", "Amelia", "Charlotte", "Ava", "Isla", "Arabella", "Aurora",
"Adeline", "Penelope", "Eleanor", "Ezra", "Asher", "Atticus",
"Declan", "Oliver", "Silas", "Milo", "Levi", "Henry", "Wyatt"
};
// Loop through each plumber and choose a random name
var rnd = new Random();
foreach(var plumber in plumbers.Employees)
{
// Choose a random name, assign it, and remove it from candidates
var nameIndex = rnd.Next(nameCandidates.Count);
plumber.Name = nameCandidates[nameIndex];
nameCandidates.RemoveAt(nameIndex);
}
I have to create a Matrix like datagrid of type bool using 2D Array. I have a set of boolean string that is needed to be evaluated for each cell in the datagrid as follows
for e.g
cell[0,0] = ((server1 || server 2) && server 3)
cell[0,1] = ((server1 && server 3) && server 4)
cell[1,0] = ((server3 && server 2) || server 4)
the values for server N is Running or Stopped and it is got from the database.
how to create a 2D matrix datagrid and how to evaluate the boolean string so that the final result is TRUE or FALSE for each datagrid cell.
I had a look at this link 2D array for string and took this as an example, but I dont know where should I call these evaluation strings. Do I have to store them in an XML file and then call them or is there anyother way to call them..
What I have tried :
public MatrixPage()
{
InitializeComponent();
bool[,] matrixcell = new bool[10, 22];
matrixcell[0, 0] = // should I place the Evaluation string here;
matrixcell[0, 1] = ;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 22; j++)
{
matrixcell[i, j] = // or Should I call here the evaluation boolean string for each iteration respective to the row/column from a file like XML or a any other file ??
}
}
var datsource = (from i in Enumerable.Range(0, matrixcell.GetLength(0))
select new clsdatasource(matrixcell[i, 0], matrixcell[i, 1], matrixcell[i,3])).ToList();
this.dg1.ItemsSource = datsource;
}
public class clsdatasource
{
public bool str1 { get; set; }
public bool str2 { get; set; }
public bool str3 { get; set; }
public clsdatasource(bool s1, bool s2,bool s3)
{
this.str1 = s1;
this.str2 = s2;
this.str3 = s3;
}
}
XAML
<Grid>
<DataGrid x:Name="dg1" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="System1" Binding="{Binding str1}"/>
<DataGridTextColumn Header="System2" Binding="{Binding str2}"/>
<DataGridTextColumn Header="System3" Binding="{Binding str3}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Kindly help.. if the question is not clear to understand, please comment, I will try to explain more clearly
Your class would like like this...
public class Clsdatasource
{
public bool Str1 { get; set; }
public bool Str2 { get; set; }
public bool Str3 { get; set; }
public Clsdatasource(){}
public Clsdatasource(bool s1, bool s2, bool s3)
{
Str1 = s1;
Str2 = s2;
Str3 = s3;
}
}
And your collection of those would look like this...
public class ClsdataSourceCollection : ObservableCollection<Clsdatasource>
{
private const string FileName = "MyData.xml";
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(List<Clsdatasource>));
public void LoadData(Action onCompleted)
{
using (StreamReader sr = new StreamReader(FileName))
{
var s = _serializer.Deserialize(sr) as List<Clsdatasource>;
if (s != null)
{
Clear();
s.ForEach(Add);
}
}
onCompleted();
}
public void SaveData(Action onCompleted)
{
using (StreamWriter sw = new StreamWriter(FileName))
{
List<Clsdatasource> tosave = new List<Clsdatasource>();
tosave.AddRange(this);
_serializer.Serialize(sw, tosave);
}
onCompleted();
}
}
And you could see what's going on with this snippet...
private static void TestSaving()
{
ClsdataSourceCollection collection = new ClsdataSourceCollection();
for (int i = 0; i < 100; i++)
{
collection.Add(new Clsdatasource(true, true, true));
}
collection.SaveData(()=> Console.WriteLine("Saved"));
}
private static void TestLoading()
{
ClsdataSourceCollection collection = new ClsdataSourceCollection();
collection.LoadData(() => Console.WriteLine("Loaded"));
}
Those two methods just do a create, save, and load. The artefact is an XML file in the root app directory called MyData.xml. You need to code in all the corner cases and error detection and races.
The last step in the whole shebang is to set this.dg1.ItemsSource = collection;
Remember that if you want real-time updates to your grid, the Clsdatasource class has to inherit from INotifyPropertyChanged, which is a different question altogether.
That should get you started with serialization. It comes 'out of the box' since about .NET 3 or so....
I have an custom UserControl I created
public class MyObject : UserControl
{
public MyObject()
{
}
public bool IsFinished { get; set; }
}
I added lets say 10(will be dynamic) MyObject's to a StackPanel and every other item i set IsFinished to true.
private void DoSomething()
{
StackPanel panel = new StackPanel();
for (int i = 0; i <= 10; i++)
{
int rem = 0;
Math.DivRem(i, 2, out rem);
MyObject newObj = new MyObject();
if (rem == 0)
{
newObj.IsFinished = true;
}
panel.Children.Add(newObj);
}
}
Now I can add the following and get the answer I am looking for (5)
int FinishedItems = 0;
foreach (object o in panel.Children)
{
if (o.GetType() == typeof(MyObject))
{
if (((MyObject)o).IsFinished)
{
FinishedItems++;
}
}
}
2 Questions:
A. Is there a more eloquent way? maybe with Linq I'm still learning how to use that. From what I understand, that is what LINQ technically does.
B. Am I wrong about LINQ?
So you want to count the finished items:
int FinishedItems = panel.Children.OfType<MyObject>()
.Count(mo => mo.IsFinished);
You may try:
int FinishedItems = panel.Children.OfType<MyObject>().Where(mo=>mo.IsFinished).Count();