Two dimensional ReadOnlyObservableCollection - c#

I want to bind Foo.Data property and I need it to be two dimensional ObservableCollection. Since I don't want Data property to be changed outside of the Foo class I'm trying to expose it as ReadOnlyObservableCollection. But updating the encapsulated _data field don't update the Data property.
public sealed class Foo
{
private readonly ObservableCollection<ObservableCollection<string>> _data;
public Foo()
{
_data = new ObservableCollection<ObservableCollection<string>>();
// this line gives compile time error
// cannot convert from ObservableCollection<ObservableCollection<string>> to ObservableCollection<ReadOnlyObservableCollection<string>>
// Data = new ReadOnlyObservableCollection<ReadOnlyObservableCollection<string>>(_data);
// Fill the _data variable
for (var i = 0; i<10; i++)
{
var t = new ObservableCollection<string>();
for (var j = 0; j<10; j++)
{
t.Add(i + "+" + j);
}
_data.Add(t);
}
var tmp = new ObservableCollection<ReadOnlyObservableCollection<string>>();
foreach (var row in _data)
{
tmp.Add(new ReadOnlyObservableCollection<string>(row));
}
Data = new ReadOnlyObservableCollection<ReadOnlyObservableCollection<string>>(tmp);
}
public ReadOnlyObservableCollection<ReadOnlyObservableCollection<string>> Data { get; }
public void RemoveItem(int x, int y)
{
_data[x].RemoveAt(y); // This does update the Data collection.
}
public void RemoveRow(int x)
{
_data.RemoveAt(x); // This does not update the Data collection.
}
}
Here is a working sample.
FYI: If you want to edit or rerun the code, just click on fork.
I've searched the web about it but I couldn't find anything. Is not two dimensional ObservableCollections a thing, is there a better practice?

The RemoveRow method does not update the Data collection because _data is not related to Data (except that they share some content). The collection related to Data is tmp, so I would try the following:
Make tmp class member and give it a name (e.g. _rowData)
rename _data to _itemData
use _rowData in RemoveRow
This is the result:
public sealed class Foo
{
private readonly ObservableCollection<ObservableCollection<string>> _itemData;
private readonly ObservableCollection<ReadOnlyObservableCollection<string>> _rowData;
public Foo()
{
_itemData = new ObservableCollection<ObservableCollection<string>>();
// Fill the _itemData variable
for (var i = 0; i<10; i++)
{
var t = new ObservableCollection<string>();
for (var j = 0; j<10; j++)
{
t.Add(i + "+" + j);
}
_itemData .Add(t);
}
_rowData = new ObservableCollection<ReadOnlyObservableCollection<string>>();
foreach (var row in _itemData )
{
_rowData.Add(new ReadOnlyObservableCollection<string>(row));
}
Data = new ReadOnlyObservableCollection<ReadOnlyObservableCollection<string>>(_rowData);
}
public ReadOnlyObservableCollection<ReadOnlyObservableCollection<string>> Data { get; }
public void RemoveItem(int x, int y)
{
_itemData[x].RemoveAt(y);
}
public void RemoveRow(int x)
{
_rowData.RemoveAt(x);
}
}

Related

Getting an error when trying to set data in nested classes

I have created some nested classes but don't unterstand how to set the variables in the classes. My code so far only gives me a error:
System.NullReferenceException: Object reference not set to an instance of an object
Code:
class Felddaten
{
public string data;
}
class Feld
{
public string fieldName;
public Felddaten[] fieldData;
}
class Tabelle
{
public string tableName;
public Feld[] field;
}
class Program
{
static void Main(string[] args)
{
Tabelle table = new Tabelle();
table.tableName = "T100";
RFCConnector connector = new RFCConnector();
connector.getFieldNames(table.tableName, out List<string> fieldN);
table.field = new Feld[fieldN.Capacity];
for (int i = 0; i < fieldN.Capacity; i++)
{
table.field[0].fieldName = fieldN[0];
}
}
}
The error is at this line of code:
table.field[0].fieldName = fieldN[0];
You have only initialized the array table.field, not the ITEMS in the array. You need to initialize each item before you can access its members:
for(int i=0; i<table.field.Length; i++)
table.field[i] = new Feld();

How to read a List from one class in another

Ok, so in a console application I'm working on, I have a list (myList) in Class01
class Class01
{
public List<string> myList = new List<string>();
public void _addsList()
{
myList.Add("0001Test01");
myList.Add("0002Test02");
myList.Add("0003Test03");
myList.Add("0004Test04");
for (int i = 0; i < myList.Count; i++)
{
Console.WriteLine(myList[i] + ",");
}
}
}
and I need to read that list in Class02
class Class02
{
public void _callList()
{
var class02 = new Class01();
string wits2;
List<string> buffer = new List<string>();
for (int i = 0; i < class02.myList.Count; i++)
{
wits2 = class02.myList[i].Substring(0, 4);
Console.WriteLine(class02.myList[i]);
}
Console.ReadKey();
}
}
The Output of this program should write this to the Console:
0001Test01,
0002Test02,
0003Test03,
0004Test04,
0001
0002
0003
0004
Now I've seen GetList used to do this
public class MyClass {
private List<string> myList = new List<string>();
public List<string> GetList()
{
return myList;
}
}
public class CallingClass {
MyClass myClass = new MyClass();
public void GetList()
{
List<string> calledList = myClass.GetList();
///More code here...
}
}
But I for the life of me can not get this to work. I don't know if I'm missing a namespace or what. I don't even know if GetList works in console application.
So I would really appreciate the help.
Thanks-
Though not totally clear from your question what is not working. But I suspect you need to put values in the list. If you mean it is not displaying anything in the console try this:
var class02 = new Class01();
string wits2;
class02._addsList();
EDIT: after reading your comment, I think this will remedy it:
for (int i = 0; i < class02.myList.Count; i++)
{
wits2 = class02.myList[i].Substring(0, 4);
Console.WriteLine(class02.myList[i]);
buffer.Add(wits2);//Add it to list declared in this function
}
NOTE: If you mean you are getting output when calling _addsList on class object the make sure that you have called this function once before using an object of Class01. From second block of code for Class02 you are not calling _addsList function on object of Class01.
On your Class02:
var class02 = new Class01();
class02._addsList(); //Add this on your declaration
string wits2;
Then change the variable you are passing on your Console.WriteLine:
List<string> buffer = new List<string>();
for (int i = 0; i < class02.myList.Count; i++)
{
wits2 = class02.myList[i].Substring(0, 4);
//Console.WriteLine(class02.myList[i]); //Remove this
Console.WriteLine(wits2); //Use wits2 instead since this is what you get on your Substring
}
I think most important thing you are missing is that your classes are not marked as public
so please add public before class declarations as below
public class Class02
{
......
One more thing, to get your required output, in your class02 you are assigning substring to 'wits' variable but not printing it. You are instead passing value from list to Console.Writeline.
Hope this helps
Thanks to TheVillageIdiot. I got this working now. So thank you. My code looks like this now:
public class Class01
{
public List<string> myList = new List<string>();
public void _addsList()
{
myList.Add("0001Test01");
myList.Add("0002Test02");
myList.Add("0003Test03");
myList.Add("0004Test04");
}
}
class Class02
{
public void _callList()
{
var class01 = new Class01();
class01._addsList(); //<--- Had to add this line
for (int i = 0; i < class01.myList.Count; i++)
{
Console.WriteLine(class01.myList[i] + ",");
}
string wits2;
List<string> buffer = new List<string>();
for (int i = 0; i < class01.myList.Count; i++)
{
wits2 = class01.myList[i].Substring(0, 4);
Console.WriteLine(wits2);
}
Console.ReadKey();
}
}
For toughs who didn't understand the problem, Class02 could not read the List in Class01.
Thanks for the Help!

Creating list/array of class instances?

I'm fairly new to C# and I have just learned about creating custom classes. The problem is, I can't figure out how to take the 40~65 instances of this class and put them in a list/array (whichever one I need) where I can locate and choose one based on an attribute defined in it.
Here's the class I have created right now:
public class Team
{
protected int teamNum;
protected double averageMatchPoints;
protected string location;
protected int matchesPlayed;
protected int matchesPending;
protected int blowouts;
//Team Number
public void SetNumber(int num)
{
teamNum = num;
}
public int GetNumber()
{
return teamNum;
}
//Average Points per match
public void AverageMatchPoints(double p)
{
averageMatchPoints = p;
}
public double GetAverageMatchPoints()
{
return averageMatchPoints;
}
//location information
public void SetLocation(string l)
{
location = l;
}
public string GetLocation()
{
return location;
}
//Number of Played Matches
public void PlayedMatches(int mat)
{
matchesPlayed = mat;
}
public int GetPlayedMatches()
{
return matchesPlayed;
}
//Number of matches pending (not played)
public void PendingMatches(int pen)
{
matchesPending = pen;
}
public int GetPendingMatches()
{
return matchesPending;
}
//Number of Blowouts (matches where the robot was disbaled for any number of reasons)
public void SetBlowouts(int b)
{
blowouts = b;
}
public int GetBlowouts()
{
return blowouts;
}
}
Now, if I had 40~65 of these teams competing at an event and I made an instance of this class for each one, how would I populate a combobox with each team number (teamNum) and then locate one specific team out of all the instances in the program by their team numbers?
I recommend a dictionary!
// Declared somewhere
private Dictionary<int, Team> _teamDictionary = new Dictionary<int, Team>();
.
.
.
//Initialization code - I assume you have gotten your teams from a database or somewhere?
foreach (var team in myTeamsList)
{
_teamDictionary.Add(team.teamNum, team);
}
.
.
.
// Later when you want to locate a team:
var team = _teamDictionary[selectedTeamNum];
Have you tried creating a List yet?
List<Team> Teams { get; set; }
You can then bind your combobox to the list/collection/IEnumerable of all the teams that you have. To initialize the teams up to 40/60 do the following?
for(int i = 0; i < 60; i++)
{
Team t = new Team();
t.Name = "Team 1";
t.TeamNumber = i + 1;
Teams.Add(t);
}
List<Team> allTheTeams = new List<Team>();
for(var i = 0; i < 65; i++){
allTheTeams.Add(new Team { teamNum = i });
}
And to get the team with number 34:
allTheTeams.FirstOrDefault(x => x.teamNum == 34);
Like this:
Add a constructor to your class that takes the teamnumber:
(this is the best solution if every team needs to have a number. So you can not forget to set the team number as you can not create an object of type team without setting the number in the constructor)
public class Team
{
protected int _teamNum;
public Team(int teamNum)
{
_teamNum = teamNum;
}
public int getTeamNum()
{
return _teamNum;
}
//more logic
}
Populate a dictionary, the comboBox and get a team for its number:
Dictionary<int, Team> dictionary = new Dictionary<int, Team>();
int teamNum = 1;
// Add your Teams to a dictionary (example)
dictionary.Add(teamNum ,new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
// Populate a comboBox
foreach(KeyValuePair<int,Team> kvp in dictionary)
{
comboBox1.Items.Add(kvp.Value.getTeamNum().ToString());
}
// get a team for a given teamNumer
int targetTeamNumber = 2;
if (dictionary.ContainsKey(targetTeamNumber))
{
Team team = dictionary[targetTeamNumber];
// do something with the team
}

how can i get an access to the elements of array in the class

I have a problem which I don't know how to solve. I have a class. This class has two arrays. I would like to get access via properties. How can I do it? I tried to use indexers, but it is possible if I have only one array. Here what I want to do:
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
}
public string X //It is just simple variable
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
public double Y //it's too
{
set { this.myY[i] = value; }
get { return this.myY[i]; }
}
}
With this code, my X and Y are only simple variables, but not arrays.
If I use indexers, I get access only to one array:
public string this[int i]
{
set { this.myX[i] = value; }
get { return this.myX[i]; }
}
But how can I get access to second array?
Or I can't use property in this case? And I need only use:
public string[] myX;
public double[] myY;
An example with Tuples.
public class pointCollection
{
Tuple<String,Double>[] myPoints;
int maxArray;
int i;
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myPoints = new Tuple<String,Double>[maxArray];
}
public Tuple<String,Double> this[int i]
{
set { this.myPoints[i] = value; }
get { return this.myPoints[i]; }
}
}
And to access the points you do...
pointCollection pc = new pointCollection(10);
// add some data
String x = pc[4].Item1; // the first entry in a tuple is accessed via the Item1 property
Double y = pc[4].Item2; // the second entry in a tuple is accessed via the Item2 property
If I got it right, you need some kind or read/write-only wrapper for arrays to be exposed as properties.
public class ReadWriteOnlyArray<T>{
private T[] _array;
public ReadWriteOnlyArray(T[] array){
this._array = array;
}
public T this[int i]{
get { return _array[i]; }
set { _array[i] = value; }
}
}
public class pointCollection
{
string[] myX;
double[] myY;
int maxArray;
public ReadWriteOnlyArray<string> X {get; private set;}
public ReadWriteOnlyArray<double> Y {get; private set;}
public pointCollection(int maxArray)
{
this.maxArray = maxArray;
this.myX = new string[maxArray];
this.myY = new double[maxArray];
X = new ReadWriteOnlyArray<string>(myX);
Y = new ReadWriteOnlyArray<double>(myY);
}
}
and usage
var c = new pointCollection(100);
c.X[10] = "hello world";
c.Y[20] = c.Y[30] + c.Y[40];
The closest you'll come without either changing your data structure or moving to methods is to make a property that returns each array, much like you did in your first code block, except without the [i].
Then, you do var x = instanceOfPointCollection.MyX[someI]; for example.

List inside object becomes empty again after exiting function block

I have an object 'ForValidation' that has List of int as a property,
and an object 'Validator' which has a Verify(IEnumerable ForValidation) method. Verify method adds numbers in ForValidation list property.
In main function, I have IEnumerable of Validator and IEnumerable of ForValidation
every time Verify(IEnumerable) exits, the list inside ForValidation is back at 0 count.
From my understanding, objects are reference types in C# and modifications from anywhere should reflect in the same object.
I tried running visual studio debugger line by line to check that list inside 'ForValidation' is in fact being added data and then disappears after Verify method.
public class ForValidation
{
private readonly object #lock = new object();
private readonly List<int> ExistenceChecks = new List<int>();
public IEnumerable<int> ExistsPlaces => ExistenceChecks;
public string CheckProperty { get; }
public ForValidation(string checkProperty )
{
CheckProperty = checkProperty ;
}
public void ConfirmExistence(int place)
{
lock (#lock)
{
ExistenceChecks.Add(place);
}
}
}
public class Validator
{
public int ValidatorNumber { get; }
private readonly Datasource somedatasource;
public Validator(int number, Datasource someds)
{
ValidatorNumber = number;
somedatasource = someds;
}
public void Verify(IEnumerable<ForValidation> forValidations)
{
ForValidation[] copy = forValidations.ToArray();
IEnumerable<string> checkProperties = from member in copy
select member.CheckProperty;
IEnumerable<CompareAgainst> existingMembers
= somedatasource.Filter(new CheckPropertiesFilter(checkProperties)).Execute();
foreach (ForValidation forValidation in copy)
{
if (existingMembers.FirstOrDefault(m => m.CheckProperty == forValidation.CheckProperty) != null)
{
forValidation.ConfirmExistence(ValidatorNumber);
}
}
int x = copy.Length;
//each forValidation.ExistsPlaces has items until this code block
}
}
main
{
private readonly IEnumerable<ForValidation> forValidations {...}
private readonly IEnumerable<Validator> validators {...}
foreach (Validator validator in validators)
{
validator.Verify(forValidations);
// each forValidation.ExistsPlaces count is 0 again in this block
}
}
IExpect every ForValidation items inside forValidations will have remembered items inside its IEnumerable ExistsPlaces property after each Verify method by validators but it becomes 0 count after each iteration of Verify method in the foreach loop
I cannot reproduce your problem. Here is my code.
public static void Main(string[] args)
{
var validators = new[] { new Validator(666), new Validator(667) };
var forValidations = new [] { new ForValidation("v1"), new ForValidation("v2") };
Console.WriteLine("Before Verify");
foreach (var fv in forValidations)
Console.WriteLine($"Object: {fv.CheckProperty} - count of places: {fv.ExistsPlaces.Count()}");
foreach (Validator validator in validators)
validator.Verify(forValidations);
Console.WriteLine("After Verify");
foreach (var fv in forValidations)
Console.WriteLine($"Object: {fv.CheckProperty} - count of places: {fv.ExistsPlaces.Count()}");
}
Result
Before Verify
Object: v1 - count of places: 0
Object: v2 - count of places: 0
After Verify
Object: v1 - count of places: 2
Object: v2 - count of places: 2
Classes:
public class ForValidation
{
private readonly object #lock = new object();
private readonly List<int> ExistenceChecks = new List<int>();
public IEnumerable<int> ExistsPlaces => ExistenceChecks;
public string CheckProperty { get; }
public ForValidation(string checkProperty)
{
CheckProperty = checkProperty;
}
public void ConfirmExistence(int place)
{
lock (#lock)
{
ExistenceChecks.Add(place);
}
}
}
public class Validator
{
public Validator(int validatorNumber)
{
ValidatorNumber = validatorNumber;
}
public int ValidatorNumber { get; }
public void Verify(IEnumerable<ForValidation> forValidations)
{
ForValidation[] copy = forValidations.ToArray();
IEnumerable<string> checkProperties = from member in copy
select member.CheckProperty;
foreach (ForValidation forValidation in copy)
{
//if (existingMembers.FirstOrDefault(m => m.CheckProperty == forValidation.CheckProperty) != null)
{
forValidation.ConfirmExistence(ValidatorNumber);
}
}
int x = copy.Length;
//each forValidation.ExistsPlaces has items until this code block
}
}

Categories