Convert string to color for objects - c#

I am wondering how to convert string to a colour I have already set in c#? I have a class which sets a colour from a list and the colour variable name is set as a string. I have objects set as the colour but I am wondering how to set the colours using a colour converter for other objects, I realise this description is poor so hopefully my image will help explain more. Also hopefully someone can help me :)
Image 1 is my current output
Image 1
I want the colours under the "Zone legend" to be the same as the "Sequence Stratigraphy"
My Code for the colour setting of the sequence straigraphy is below along with my code for the zone legend
Sequence Stratigraphy Colours
internal static class ZoneColorizer
{
public static void Colorize(ZoneDto[] zones)
{
Color[] colorsToChooseFrom = new Color[] { Colors.DarkSlateGray, Colors.DarkBlue, Colors.DarkRed, Colors.DarkCyan, Colors.DarkGoldenrod, Colors.DarkGreen, Colors.DarkMagenta };
byte colorIndex = 0;
foreach (var zone in zones)
{
var color = colorsToChooseFrom[colorIndex];
var zoneColor = Color.FromArgb(128, color.R, color.G, color.B);
zone.ColourCode = zoneColor.ToString();
colorIndex++;
byte subzoneNumber = 50;
byte totalSubZones = (byte)zone.Subzones.LongLength;
byte multipler = (byte)(200 / totalSubZones);
foreach (var subzone in zone.Subzones)
{
var subZoneColor = Color.FromArgb(subzoneNumber, color.R, color.G, color.B);
subzone.ColourCode = subZoneColor.ToString();
subzoneNumber+= multipler;
}
}
}
}
Zone Legend code
private List<Color> colours;
public ObservableCollection<string> Zones { get; set; }
public LegendControl()
{
colours = new List<Color>() { Colors.DarkSlateGray, Colors.DarkBlue, Colors.DarkRed, Colors.DarkCyan, Colors.DarkGoldenrod, Colors.DarkGreen, Colors.DarkMagenta };
InitializeComponent();
Zones = new ObservableCollection<string>();
DataContext = Zones;
}
public void Bind(ZoneDto[] zones)
{
int zoneNumber = 0;
int rowNumber = 0;
byte subzoneNumber = 0;
byte totalsubZones = 0;
Zones.Clear();
foreach (var zone in zones)
{
Color zoneColor = GetColour(zoneNumber);
var zonerectangle = CreateZoneRectangle(zone, rowNumber, zoneColor);
Grid1.Children.Add(zonerectangle);
var zoneName = CreateZoneName(zone, rowNumber);
Grid1.Children.Add(zoneName);
zoneNumber++;
foreach (var subzone in zone.Subzones)
{
totalsubZones++;
Grid1.RowDefinitions.Add(new RowDefinition());
Color subzoneColor = GetGradeColour(subzoneNumber, totalsubZones, zoneColor);
var subzoneRectangle = CreateSubZoneRectangle(subzone, rowNumber, subzoneColor);
Grid1.Children.Add(subzoneRectangle);
var subzoneName = CreateSubZoneName(subzone, rowNumber);
Grid1.Children.Add(subzoneName);
subzoneNumber++;
rowNumber++;
}
}
}
private Rectangle CreateZoneRectangle(ZoneDto zone, int rowNumber, Color zoneColour)
{
var rectangle = new Rectangle
{
Fill = new SolidColorBrush(zoneColour),
Margin = new Thickness(2)
};
int rowSpan = 0;
foreach (var subzone in zone.Subzones)
{
rowSpan++;
}
rectangle.SetValue(Grid.RowProperty, rowNumber);
rectangle.SetValue(Grid.ColumnProperty, 0);
rectangle.SetValue(Grid.RowSpanProperty, rowSpan);
return rectangle;
}
private TextBlock CreateZoneName(ZoneDto zone, int rowNumber)
{
Zones.Clear();
var textblock = new TextBlock
{
Text = zone.Name,
RenderTransformOrigin = new Point(0.5, 0.5),
LayoutTransform = new RotateTransform(270),
VerticalAlignment = System.Windows.VerticalAlignment.Center,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
int rowSpan = 0;
foreach (var subzon in zone.Subzones)
{
rowSpan++;
}
textblock.SetValue(Grid.RowProperty, rowNumber);
textblock.SetValue(Grid.ColumnProperty, 0);
textblock.SetValue(Grid.RowSpanProperty, rowSpan);
return textblock;
}
private Rectangle CreateSubZoneRectangle(ZoneDto subzone, int rowNumber, Color subzoneColor)
{
var rectangle = new Rectangle
{
Fill = new SolidColorBrush(subzoneColor),
Margin = new Thickness(2)
};
rectangle.SetValue(Grid.RowProperty, rowNumber);
rectangle.SetValue(Grid.ColumnProperty, 1);
return rectangle;
}
private TextBlock CreateSubZoneName(ZoneDto subzone, int rowNumber)
{
var textblock = new TextBlock
{
Text = subzone.Name,
VerticalAlignment = System.Windows.VerticalAlignment.Center,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center
};
textblock.SetValue(Grid.RowProperty, rowNumber);
textblock.SetValue(Grid.ColumnProperty, 1);
return textblock;
}
private Color GetColour(int zoneNumber)
{
var numOfColours = colours.Count;
if (zoneNumber >= numOfColours)
{
return colours[zoneNumber % numOfColours];
}
return colours[zoneNumber];
}
private Color GetGradeColour(byte subzoneNumber, byte totalsubZones, Color zoneColour)
{
byte multipler = (byte)(200 / totalsubZones);
byte a = (byte)((subzoneNumber + 1) * multipler);
return Color.FromArgb(a, zoneColour.R, zoneColour.G, zoneColour.B);
}
the code for the ZoneDto
public class GridDataDto
{
public WellDto[] Wells { get; set; }
public ZoneDto[] Zones { get; set; }
public string[] Facies { get; set; }
public CellDto MinLimits { get; set; }
public CellDto MaxLimits { get; set; }
}
public class ZoneDto
{
public string Name { get; set; }
public ZoneDto[] Subzones { get; set; }
public int MinK { get; set; }
public int MaxK { get; set; }
public string ColourCode { get; set; }
}
public class WellDto
{
public string Name { get; set; }
public double X { get; set; }
public double Y { get; set; }
}
public class CellDto
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public int I { get; set; }
public int J { get; set; }
public int K { get; set; }
}

EDIT: Seems there is already a built in method for this ColorConverter.ConvertFromString https://msdn.microsoft.com/en-us/library/system.windows.media.colorconverter.convertfromstring(v=vs.110).aspx
Here's the old code anyway -
The System.Windows.Media.Color ToString() method returns a hex string of the aRGB values that looks like this: #80C8DCF0 you can just split the bytes out and create a new color:
static Color FromString(string colorString)
{
byte a = Convert.ToByte(colorString.Substring(1, 2), 16);
byte r = Convert.ToByte(colorString.Substring(3, 2), 16);
byte g = Convert.ToByte(colorString.Substring(5, 2), 16);
byte b = Convert.ToByte(colorString.Substring(7, 2), 16);
return Color.FromArgb(a, r, g, b);
}

I could be reading your question wrong but if you just want the legend to share the same colours as the "Sequence Stratigraphy" could you not just use the ZoneColorizer's Colorize method on the ZoneDto[] object in your zone legend code's Bind method?

Related

Checking if data exist in sqlite-net before inserting them to database

Im making an Xamarin.Android application that will use "templates" , template is this:
[Table("Templates")]
public class Template
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
//[TextBlob("imagesBlobbed")]
[OneToMany, Unique]
public List<TemplateImage> TemplateImages { get; set; }
public string ImagesHash { get; set; }
//public string imagesBlobbed { get; set; }
}
[Table("TemplateImages")]
public class TemplateImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
public string ImagesHash { get; set; }
public int Image { get; set; }
[ForeignKey(typeof(Template))]
public int TemplateId { get; set; }
}
i want all the TemplateImages objects to be unique in my database, the attribute Unique doesnt do anything, cause i guess because TemplateImage table has an auto increment Id will always be unique, no matter if the Image or ImageHash is the same in 2 records.
I was thinking then how else i can be sure that TemplateImages will be unique (Notice: when i say unique i mean that there isn't any other List that have the exact Image for every TemplateImage, in the same order).
Also i was using the ResourceID of the images, as image, which is wrong cause they might change on every new compiled app update.
So i decided to make a hash md5 from the images. They only way that i have find to load resource images (my images are vector .xml files) in Xamarin.Android is by converting the resource to bitmap, and then convert the bitmap to byte, and then the byte to md5.
And also i should have a hash string for the template item too. so im creating an md5 hash for the template by combining all the byte[] of the images into one and then hasing that.
I create this crazy code for this job:
public static string GetMD5Hash(byte[] content)
{
using (var md5 = MD5.Create())
{
byte[] computedHash = md5.ComputeHash(Encoding.UTF8.GetBytes(BitConverter.ToString(content).Replace("-", "")));
return new System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary(computedHash).ToString();
}
}
private static SQLiteConnection instance;
public static SQLiteConnection db()
{
if (instance == null)
instance = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "TemplatesData.db"));
return instance;
}
public static void CloseConnection()
{
if (instance != null)
{
instance.Close();
instance.Dispose();
instance = null;
}
}
}
public class TemplateDB
{
public static byte[] ConcatByteList(List<byte[]> list)
{
return list
.SelectMany(a => a)
.ToArray();
}
public static Bitmap GetBitmapFromVectorDrawable(Context context, int drawableId)
{
Drawable drawable = ContextCompat.GetDrawable(context, drawableId);
if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
{
drawable = (DrawableCompat.Wrap(drawable)).Mutate();
}
Bitmap bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth,
drawable.IntrinsicWidth, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
drawable.Draw(canvas);
return bitmap;
}
public byte[] DrawableToByteArray(int resourceId)
{
var context = AppState.ApplicationState;
using (var bitmap = GetBitmapFromVectorDrawable(context, resourceId))
{
int size = bitmap.ByteCount;
byte[] byteArray = new byte[size];
ByteBuffer byteBuffer = ByteBuffer.Allocate(size);
bitmap.CopyPixelsToBuffer(byteBuffer);
byteBuffer.Rewind();
byteBuffer.Get(byteArray);
return byteArray;
}
}
public static void AddTemplate(int category, List<int> images)
{
var templateDB = new TemplateDB();
var imageByteList = new List<byte[]>();
foreach (int image in images)
{
imageByteList.Add(templateDB.DrawableToByteArray(image));
}
var tmpl = new Template()
{
Category = category,
};
var img1 = new TemplateImage()
{
Category = category,
Image = images[0],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[0]),
};
var img2 = new TemplateImage()
{
Category = category,
Image = images[1],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[1]),
};
var img3 = new TemplateImage()
{
Category = category,
Image = images[2],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[2]),
};
var img4 = new TemplateImage()
{
Category = category,
Image = images[3],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[3]),
};
var img5 = new TemplateImage()
{
Category = category,
Image = images[4],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[4]),
};
tmpl.TemplateImages = new List<TemplateImage>() { img1, img2, img3, img4, img5 };
tmpl.ImagesHash = DatabaseHelper.GetMD5Hash(ConcatByteList(imageByteList));
var result = DatabaseHelper.db().Query<TemplateImage>("Select * from Templates where ImagesHash=?", tmpl.ImagesHash);
if (result.Count == 0)
{
DatabaseHelper.db().InsertAll(tmpl.TemplateImages);
DatabaseHelper.db().Insert(tmpl);
DatabaseHelper.db().UpdateWithChildren(tmpl);
}
}
Which all of a sudden, gives out of memory exception.
After thinking for a while that i should stop programming and start belly dancing, i think that since im giving in sqlite AddTemplate function a list of ResourceIds that i have to manually create (can't avoid it), then why not to give them my own string hash code and compare that to find if a record exist?
What is the correct approach?
In the end, i was thinking to use guid as extra id fields and add the parameter unique,in that case i could be sure which templates have been inserted, but i decided to use transactions. With transactions i can know that all my data is created or none and also save the creation into a variable in SharedPreferences and avoid recreating or even checking if the database exist.
For checking if the data is correctly updated and no recreation is needed i used this code:
public bool FirstRun { get; set; } = true;
public int DatabaseCreatedVersionOf { get; set; } = -1;
public override void OnCreate()
{
base.OnCreate();
}
public void UpdateDatabaseCreatedVersion()
{
DatabaseCreatedVersionOf = Preferences.Get(Settings.DatabaseCreatedVersionOfKey,
Settings.DatabaseCreatedVersionOfDefault);
}
public void CreateTemplateDB()
{
UpdateDatabaseCreatedVersion();
if (DatabaseCreatedVersionOf == -1)
TemplateDB.CreateDB();
FirstRun = false;
}
public Template GetTemplateById(int id)
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetTemplate(id);
}
public List<Template> GetAllTemplates()
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetAllTemplates();
}
Now all i have to do is call GetTemplateById or GetAllTemplates and if any creation is required, it will happen.

Increment through property names in object

I have the following object:
namespace BluetoothExample
{
public class Assay
{
public double Band_1 //Vis450
{
get;
set;
}
public double Band_2 //Vis500
{
get;
set;
}
public double Band_3 //Vis550
{
get;
set;
}
public double Band_4 //Vis570
{
get;
set;
}
}
}
I want to populate the 4 bands in my object. Which I currently do in the following way:
public Populate()
{
int _i = 0;
double[] nirData = new double[4];
MyDevice.Characteristic.ValueUpdated += (sender, e) =>
{
nirData[_i] = BitConverter.ToDouble(e.Characteristic.Value, 0);
_i++;
};
Assay assay = new Assay();
assay.Band_1 = nirData[0];
assay.Band_2 = nirData[1];
assay.Band_3 = nirData[2];
assay.Band_4 = nirData[3];
}
I was wondering if it was possible to do the entire thing inside the MyDevice.Characteristic.ValueUpdate method instead? My thought is that it should be possible to increment and populate the properties of my object like so:
string name = "assay.Band_" + _i;
name = BitConverter.ToDouble(e.Characteristic.Value, 0);
This is obviously wrong, but it sort of demonstrates my idea.
Just make your band an Array, or List:
public class Assay
{
public double[] Band
{
get;
set;
}
}
And then you can simply assign it like:
public Populate()
{
int _i = 0;
double[] nirData = new double[4];
MyDevice.Characteristic.ValueUpdated += (sender, e) =>
{
nirData[_i] = BitConverter.ToDouble(e.Characteristic.Value, 0);
_i++;
};
Assay assay = new Assay();
assay.Band = nirData;
}

How to Binding the array[] to the DataGridTextColumn dynamiclly wpf

When I have a Staff class ,I want to Binding the TB[] to the DataGrid Columns
Staff.cs
public class Staff
{
public string Name { get; set; }
public float? Test_Score { get; set; }
public float? Net_Income_Tax { get; set; }
public float temp { get; set; }
public float[] TB = new float[100];
public float TB_Sum { get; set; }
}
in the Page,my code are as follows:
public ObservableCollection<Staff> Staff_Show = new ObservableCollection<Staff>();
public int StaffShowSize;
public TaxBefore_Sum(ObservableCollection<Staff> StaffAccept)
{
InitializeComponent();
Staff_Show = StaffAccept;
StaffShowSize = Staff_Show.Count;
DataGrid1.ItemsSource = this.Staff_Show;
//this is to dynamiclly to create new DataGridTextColumn,but
//final ,only create the columns' header is"0",and no data to show,however,the TB[i] have data,but can't show like the picture
for (int i = 0; i < StaffShowSize; i++)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = Convert.ToString(i + 1);
c.Binding = new Binding("TB[i]");
DataGrid1.Columns.Add(c);
}
}
the "for()" sentence is to dynamiclly to create new DataGridTextColumn,but
final ,only create the columns' with header,and no data to show,however,the TB[i] have data,but can't show like the picture:
when I changed the code to
c.Binding = new Binding("TB[" + i + "]");
it showns that
Here is what you need...
c.Binding = new Binding("TB[" + i + "]");
Also definition of TB...
public float[] TB { get; set; } = new float[100];

Serialize xml in a specific format in C#?

I have a class like so:
[System.Serializable]
public class UIColor
{
public UIColor()
{
}
public UIColor(double red, double green, double blue, double alpha)
{
r = (float)red;
g = (float)green;
b = (float)blue;
a = (float)alpha;
}
public UIColor(double white, double alpha)
{
r = (float)white;
g = (float)white;
b = (float)white;
a = (float)alpha;
}
[System.Xml.Serialization.XmlElement("r", typeof(float))]
public float r
{
get;
set;
}
[System.Xml.Serialization.XmlElement("g", typeof(float))]
public float g
{
get;
set;
}
[System.Xml.Serialization.XmlElement("b", typeof(float))]
public float b
{
get;
set;
}
[System.Xml.Serialization.XmlElement("alpha", typeof(float))]
public float a
{
get;
set;
}
}
And many instances of it in a class like so:
class Colors
{
[XmlElement("Col1")]
UIColor Col1;
[XmlElement("Col2")]
UIColor Col2;
//etc etc
}
What I'd like to do is serialize out the class Colors into xml in the following format:
<Color name="Col1" r="1" g="1" b="1" alpha="1"/>
<Color name="Col2" r="2" g="2" b="2" alpha="2"/>
Currently the way it serializes out is like:
<Col1>
<r>1</r>
//etc etc
Your original class should look like:
[System.Serializable]
[System.Xml.Serialization.XmlRoot("Color")]
public class UIColor
{
public UIColor()
{
name = "Col1"
}
public UIColor(double red, double green, double blue, double alpha)
{
r = (float)red;
g = (float)green;
b = (float)blue;
a = (float)alpha;
name = "Col1";
}
public UIColor(double white, double alpha)
{
r = (float)white;
g = (float)white;
b = (float)white;
a = (float)alpha;
name = "Col1";
}
[System.Xml.Serialization.XmlAttribute]
public string name
{
get;
set;
}
[System.Xml.Serialization.XmlAttribute]
public float r
{
get;
set;
}
[System.Xml.Serialization.XmlAttribute]
public float g
{
get;
set;
}
[System.Xml.Serialization.XmlAttribute]
public float b
{
get;
set;
}
[System.Xml.Serialization.XmlAttribute("alpha")]
public float a
{
get;
set;
}
}
And the serialization code:
using (System.IO.TextWriter writer = new System.IO.StreamWriter(#"C:\temp\test.xml"))
{
System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(UIColor));
System.Xml.Serialization.XmlSerializerNamespaces namspace = new XmlSerializerNamespaces();
namespace.Add("", "");
xml.Serialize(writer, new UIColor(), namespace);
}
And the out XML will produce:
<?xml version="1.0" encoding="utf-8"?>
<Color name="Col1" r="0" g="0" b="0" alpha="0" />

How to transform list of hierarchyid into a binary tree

I am working on a multi-level marketing (binary) which looks like this:
(but the binary tree is not required to be perfect. A node can have 0-2 child)
My problem is the data that I fetch from the database is flat list.
Notice that I am using hierarchyid (sql server 2014)
Basically the TextNode column is like a breadcrumb.
every slash / represents a level.
If I have TextNode of /1/ as root. then every node that starts with /1/ belongs to that root which are /1/, /1/1/ and /1/1/1/ (the root node is included which will be the level 0)
I've tried the accepted answer in this question but its not working.
How can I transform the flatlist to a Binary Tree so that I can easily traverse and display it on a screen?
Im using C#, ASP MVC 5, SQL Server 2014 if it matters.
I implement exactly this code According to Alex implementation but as is mentioned in some case it didn't work correctly .. have a look to my Image and my code (which copied from Alex post) [data in the database are correct but in tree view seems some problems ]
public class Row : IRow<string>
{
public string TextNode { get; }
public string Value { get; }
public long Id { get; }
public string FIN { get; }
public Row(string textNode, string userName, long id, string fin)
{
FIN = fin;
Id = id;
TextNode = textNode;
Value = userName;
}
}
public interface IRow<out T>
{
string TextNode { get; }
long Id { get; }
string FIN { get; }
T Value { get; }
}
public class TreeNode<T>
{
private struct NodeDescriptor
{
public int Level { get; }
public int ParentIndex { get; }
public NodeDescriptor(IRow<T> row)
{
var split = row.TextNode.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
Level = split.Length;
ParentIndex = split.Length > 1 ? int.Parse(split[split.Length - 2]) - 1 : 0;
}
}
public T title { get; }
public long Id { get; }
public string FIN { get; }
public List<TreeNode<T>> children { get; }
private TreeNode(T value, long id, string fin)
{
Id = id;
FIN = fin;
title = value;
children = new List<TreeNode<T>>();
}
public static TreeNode<T> Parse(IReadOnlyList<IRow<T>> rows)
{
if (rows.Count == 0)
return null;
var result = new TreeNode<T>(rows[0].Value, rows[0].Id, rows[0].FIN);
FillParents(new[] { result }, rows, 1, 1);
return result;
}
private static void FillParents(IList<TreeNode<T>> parents, IReadOnlyList<IRow<T>> rows, int index, int currentLevel)
{
var result = new List<TreeNode<T>>();
for (int i = index; i < rows.Count; i++)
{
var descriptor = new NodeDescriptor(rows[i]);
if (descriptor.Level != currentLevel)
{
FillParents(result, rows, i, descriptor.Level);
return;
}
var treeNode = new TreeNode<T>(rows[i].Value, rows[i].Id, rows[i].FIN);
parents[descriptor.ParentIndex].children.Add(treeNode);
result.Add(treeNode);
}
}
}
g
this is also my JSON output for more information :
{"title":"Earth","Id":32,"FIN":"FIN","children":[{"title":"Europe","Id":33,"FIN":"FIN001","children":[{"title":"France","Id":35,"FIN":"FIN001001","children":[{"title":"Paris","Id":36,"FIN":"FIN001001001","children":[]},{"title":"Brasilia","Id":41,"FIN":"FIN002001001","children":[]},{"title":"Bahia","Id":42,"FIN":"FIN002001002","children":[]}]},{"title":"Spain","Id":38,"FIN":"FIN001002","children":[{"title":"Madrid","Id":37,"FIN":"FIN001002001","children":[{"title":"Salvador","Id":43,"FIN":"FIN002001002001","children":[]}]}]},{"title":"Italy","Id":45,"FIN":"FIN001003","children":[]},{"title":"Germany","Id":48,"FIN":"FIN001004","children":[]},{"title":"test","Id":10049,"FIN":"FIN001005","children":[]}]},{"title":"South America","Id":34,"FIN":"FIN002","children":[{"title":"Brazil","Id":40,"FIN":"FIN002001","children":[{"title":"Morano","Id":47,"FIN":"FIN001003001","children":[]}]}]},{"title":"Antarctica","Id":39,"FIN":"FIN003","children":[{"title":"McMurdo Station","Id":44,"FIN":"FIN003001","children":[]}]}]}
Here is a very simple implementation (assuming that Nodes are in the right order), which may be enhanced in multiple ways
public interface IRow<out T>
{
string TextNode { get; }
T Value { get; }
}
public class TreeNode<T>
{
private struct NodeDescriptor
{
public int Level { get; }
public int ParentIndex { get; }
public NodeDescriptor(IRow<T> row)
{
var split = row.TextNode.Split(new [] {"/"}, StringSplitOptions.RemoveEmptyEntries);
Level = split.Length;
ParentIndex = split.Length > 1 ? int.Parse(split[split.Length - 2]) - 1 : 0;
}
}
public T Value { get; }
public List<TreeNode<T>> Descendants { get; }
private TreeNode(T value)
{
Value = value;
Descendants = new List<TreeNode<T>>();
}
public static TreeNode<T> Parse(IReadOnlyList<IRow<T>> rows)
{
if (rows.Count == 0)
return null;
var result = new TreeNode<T>(rows[0].Value);
FillParents(new[] {result}, rows, 1, 1);
return result;
}
private static void FillParents(IList<TreeNode<T>> parents, IReadOnlyList<IRow<T>> rows, int index, int currentLevel)
{
var result = new List<TreeNode<T>>();
for (int i = index; i < rows.Count; i++)
{
var descriptor = new NodeDescriptor(rows[i]);
if (descriptor.Level != currentLevel)
{
FillParents(result, rows, i, descriptor.Level);
return;
}
var treeNode = new TreeNode<T>(rows[i].Value);
parents[descriptor.ParentIndex].Descendants.Add(treeNode);
result.Add(treeNode);
}
}
}
Sample usage:
public class Row : IRow<string>
{
public string TextNode { get; }
public string Value { get; }
public Row(string textNode, string userName)
{
TextNode = textNode;
Value = userName;
}
}
class Program
{
static void Main(string[] args)
{
IRow<string>[] rows =
{
new Row("/", "Ahmed"),
new Row("/1/", "Saeed"),
new Row("/2/", "Amjid"),
new Row("/1/1/", "Noura"),
new Row("/2/1/", "Noura01"),
new Row("/2/2/", "Reem01"),
new Row("/1/1/1", "Under_noura")
};
var tree = TreeNode<string>.Parse(rows);
PrintTree(tree);
}
private static void PrintTree<T>(TreeNode<T> tree, int level = 0)
{
string prefix = new string('-', level*2);
Console.WriteLine("{0}{1}", prefix, tree.Value);
foreach (var node in tree.Descendants)
{
PrintTree(node, level + 1);
}
}
}

Categories