Plot Multiple Y-Axis against one X-Axis of DateTime in LiveCharts - c#

In my application, I have four series that I want to plot to a Line Graph. Each series is of the same size, three of which are double and the last one is a DateTime list. The three double series come in a list of class objects of type GraphData which look like this:
public class GraphData
{
public string Name { get; set; }
public List<double> Data { get; set; }
}
As an additional requirement, I want to have a Y-Axis of its own for each of these.
Here's my entire program so far, and it plots the three graphs on its own axes with no problem.
public partial class MainWindow : Window
{
public SeriesCollection SeriesCollection { get; set; }
public AxesCollection YAxesCollection { get; set; }
public List<GraphData> GraphDatas { get; set; }
public List<DateTime> TimeStamps { get; set; }
public MainWindow()
{
InitializeComponent();
GraphDatas = GetGraphData();
TimeStamps = GetTimeStamps(GraphDatas[0].Data.Count);
Plot();
}
private void Plot()
{
SeriesCollection = new SeriesCollection();
YAxesCollection = new AxesCollection();
var count = 0;
foreach (var data in GraphDatas)
{
var gLineSeries = new GLineSeries
{
Title = data.Name,
Values = data.Data.AsGearedValues().WithQuality(Quality.Low),
PointGeometry = null,
Fill = Brushes.Transparent,
ScalesYAt = count
};
SeriesCollection.Add(gLineSeries);
YAxesCollection.Add(new Axis() { Title = data.Name });
count++;
}
DataContext = this;
}
private List<GraphData> GetGraphData()
{
var dataList = new List<GraphData>
{
new GraphData() { Name = "DataA", Data = new List<double>() { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 11.0, 11.0, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, } },
new GraphData() { Name = "DataB", Data = new List<double>() { 26, 33, 65, 28, 34, 55, 25, 44, 50, 36, 26, 37, 43, 62, 35, 38, 45, 32, 28, 34 } },
new GraphData() { Name = "DataC", Data = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 } }
};
return dataList;
}
private List<DateTime> GetTimeStamps(int limit)
{
var timeStamps = new List<DateTime>();
var now = DateTime.Now;
for (int i = 0; i < limit; i++)
{
if (i == 0)
timeStamps.Add(now);
else
{
now = now.AddDays(1);
timeStamps.Add(now);
}
}
return timeStamps;
}
}
My XAML looks simple:
<Grid>
<lvc:CartesianChart Series="{Binding SeriesCollection}"
AxisY="{Binding YAxesCollection}"
DisableAnimations="True"
LegendLocation="Right">
</lvc:CartesianChart>
</Grid>
GetGraphData() and GetTimeStamps() are dummy functions here that simulates my original functions.
Now this works fine, except that the X-axis is not DateTime since obviously I haven't plotted it so. But how would I go about doing this?
The official documentation as well as this SO Post only shows how to do this with only one Y-Axis.

I'd start with some changes to the model in order for it to show the full picture. The timestamp is part of the data point and you'll need to wrap them together to allow the Live Charts mapper to plot the data.
public class DataPoint
{
public DataPoint(DateTime timeStamp, double value)
{
TimeStamp = timeStamp;
Value = value;
}
public double Value { get; }
public DateTime TimeStamp { get; }
}
public class GraphData
{
public string Name { get; set; }
public List<DataPoint> Data { get; set; }
}
If you want to keep the current flow of extraction (CSV), you can simply LINQ Zip the data in to its plottable form.
public partial class MainWindow : Window
{
public SeriesCollection SeriesCollection { get; set; }
public Func<double, string> Formatter { get; set; }
public AxesCollection YAxesCollection { get; set; }
public List<GraphData> GraphDatas { get; set; }
public MainWindow()
{
InitializeComponent();
var timeStamps = GetTimeStamps(20);
GraphDatas = GetGraphData(timeStamps);
Plot();
}
private List<GraphData> GetGraphData(List<DateTime> timeStamps)
{
var valuesA = new List<double>() { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 11.0, 11.0, 9.9, 8.8, 7.7, 6.6, 5.5, 4.4, 3.3, 2.2, 1.1, };
var valuesB = new List<double>() { 26, 33, 65, 28, 34, 55, 25, 44, 50, 36, 26, 37, 43, 62, 35, 38, 45, 32, 28, 34 };
var valuesC = new List<double>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
List<DataPoint> MergeData(List<double> values) => timeStamps.Zip(values, (x, y) => new DataPoint(x, y)).ToList();
var dataList = new List<GraphData>
{
new GraphData() { Name = "DataA", Data = MergeData(valuesA) },
new GraphData() { Name = "DataB", Data = MergeData(valuesB) },
new GraphData() { Name = "DataC", Data = MergeData(valuesC) },
};
return dataList;
}
private void Plot()
{
var mapper = Mappers.Xy<DataPoint>()
.X(dp => (double)dp.TimeStamp.Ticks)
.Y(dp => dp.Value);
SeriesCollection = new SeriesCollection(mapper);
YAxesCollection = new AxesCollection();
var count = 0;
foreach (var data in GraphDatas)
{
var gLineSeries = new GLineSeries
{
Title = data.Name,
Values = data.Data.AsGearedValues().WithQuality(Quality.Low),
PointGeometry = null,
Fill = Brushes.Transparent,
ScalesYAt = count
};
SeriesCollection.Add(gLineSeries);
YAxesCollection.Add(new Axis() { Title = data.Name });
count++;
}
Formatter = value => new DateTime((long)value).ToString("yyyy-MM:dd HH:mm:ss");
DataContext = this;
}
private List<DateTime> GetTimeStamps(int limit)
{
var timeStamps = new List<DateTime>();
var now = DateTime.Now;
for (int i = 0; i < limit; i++)
{
if (i == 0)
timeStamps.Add(now);
else
{
now = now.AddDays(1);
timeStamps.Add(now);
}
}
return timeStamps;
}
}
XAML
<lvc:CartesianChart Series="{Binding SeriesCollection}"
AxisY="{Binding YAxesCollection}"
DisableAnimations="True"
LegendLocation="Right">
<lvc:CartesianChart.AxisX>
<lvc:Axis LabelFormatter="{Binding Formatter}" />
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>

Related

Merge 2 Different List with order condition in C# Linq

So I want to join from 2 different list without any key and order that list some condition.
I have Class like this:
public class DataA
{
public string Data { get; set; }
public int Order { get; set; }
public DateTime CreatedOn { get; set; }
}
public class DataB
{
public string Data { get; set; }
public int Order { get; set; }
public DateTime CreatedOn { get; set; }
}
And the list like this:
var listDataA = new List<DataA>
{
new DataA { Data = "data a 1", Order = 1, CreatedOn = new DateTime(2020, 11, 12, 00, 00, 00) },
new DataA { Data = "data a 2", Order = 2, CreatedOn = new DateTime(2020, 11, 13, 00, 00, 00) },
new DataA { Data = "data a 5", Order = 5, CreatedOn = new DateTime(2020, 11, 14, 16, 00, 00) },
new DataA { Data = "data a 6", Order = 6, CreatedOn = new DateTime(2020, 11, 15, 00, 00, 00) },
new DataA { Data = "data a 7", Order = 7, CreatedOn = new DateTime(2020, 11, 16, 00, 00, 00) }
};
var listDataB = new List<DataB>
{
new DataB { Data = "data b 5", Order = 5, CreatedOn = new DateTime(2020, 11, 14, 14, 00, 00) },
new DataB { Data = "data b 8", Order = 8, CreatedOn = new DateTime(2020, 11, 16, 00, 00, 00) }
};
I want to order the list from each other Order property, if the Order value is the same it will check the CreatedOn property. so the data will look like this:
[
{ Data = "data a 1", Order = 1, CreatedOn = new DateTime(2020, 11, 12, 00, 00, 00) },
{ Data = "data a 2", Order = 2, CreatedOn = new DateTime(2020, 11, 13, 00, 00, 00) },
{ Data = "data b 5", Order = 5, CreatedOn = new DateTime(2020, 11, 14, 14, 00, 00) },
{ Data = "data a 5", Order = 5, CreatedOn = new DateTime(2020, 11, 14, 16, 00, 00) },
{ Data = "data a 6", Order = 6, CreatedOn = new DateTime(2020, 11, 15, 00, 00, 00) },
{ Data = "data a 7", Order = 7, CreatedOn = new DateTime(2020, 11, 16, 00, 00, 00) },
{ Data = "data b 8", Order = 8, CreatedOn = new DateTime(2020, 11, 16, 00, 00, 00) }
]
How can I do this in Linq?
Really, i think you'd be better off defining an interface for these classes that mentions the common parts you want to use
interface IDataX
{
int Order { get; set; }
DateTime CreatedOn { get; set; }
}
public class DataA: IDataX ...
public class DataB: IDataX ...
Then you could do like
var output = listDataA.Cast<IDataX>().Concat(listDataB).OrderBy(d => d.Order).ThenBy(d => d.CreatedOn);
as Data is common to both, you could add that to the interface too, and then you could use Data from IDataX without having to know specifically whether the object underneath is a DataA or a DataB.
If you're against that (and also against a similar idea of having a parent class for A/B that has the common properties Order/Created..), you might have to project to some common holder (which could equally be anonymous but presented here as not for sake of example):
class Holder{
public int Order { get; set; }
public DateTime CreatedOn { get; set; }
public object OriginalData { get; set; }
}
And concat projections..
listA
.Select(a => new Holder { Order = a.Order, CreatedOn = a.CreatedOn, OriginalData = a})
.Concat(
listB.Select(b => new Holder { Order = b.Order, CreatedOn = b.CreatedOn, OriginalData = b})
).OrderBy(x => x.Order).ThenBy(x => x.CreatedOn)
..then cast back the OriginalData when you want to use it.. (if(x.OriginalData is DataA a) ... else if(x.OriginalData is DataB b) which is a bit.. ugh)
Personally, I'd definitely be leveraging some kind of polymorphism for this

Hierarchical Data - Composite Pattern C#

I have a recurring pattern that I see in some of the reports that I have to generate. These reports are date range based reports and need to be aggregated by different levels.
For the sake of simplicity, let's assume that this report produces a title and a quota at the leaf node level (lowest level). At the line item level (which is a collection of various leaf nodes), I would like to aggregate the Quota and provide a separate title. These line items would further roll up to another level which would again aggregate the quota and have a unique title.
So the report would be something like this:
ROOT LEVEL | Title = "Main Report" | Quota = 100
Month Level | Title = "Jan" | Quota = 100
Week Level | Title = "Week 1" | Quota = 25
Week Level | Title = "Week 2" | Quota = 75
Is there way for me to build this using a composite pattern? I have tried numerous approaches. Most of them fall short because I cannot effectively aggregate/sum up the quota to the higher level.
I can build an interface like so:
public interface IInventoryReportItem
{
string Title { get; set; }
int Quota { get; set; }
}
Then I can build a Line Item like so:
public class LineItem : IInventoryReportItem
I can also build a collection like so:
public class LineItems : IList<IInventoryReportItem>, IInventoryReportItem
{
private readonly List<IInventoryReportItem> _subLineItems;
public LineItems()
{
_subLineItems = new List<IInventoryReportItem>();
}
And my report would be like so:
public class InventoryReport
{
public DateRange DateRange { get; set; }
public LineItems LineItems { get; set; }
}
I can build the report easily in a hierarchical fashion now, but I still need to call the aggregation functions from the outside as opposed to it auto-calculating this for me:
var report = new InventoryReport();
var week1Days = new LineItems
{
new LineItem {Quota = 20, Title = "Day 1"},
new LineItem {Quota = 10, Title = "Day 2"}
};
var week2Days = new LineItems
{
new LineItem {Quota = 10, Title = "Day 1"},
new LineItem {Quota = 10, Title = "Day 2"}
};
var week1 = new LineItems {week1Days};
week1.Quota = week1.Sum(x => x.Quota);
week1.Title = "Week1";
var week2 = new LineItems {week2Days};
week2.Quota = week2.Sum(x => x.Quota);
week2.Title = "Week2";
var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2});
month1.Title = "January";
month1.Quota = month1.Sum(x => x.Quota);
report.LineItems = new LineItems(new List<IInventoryReportItem> {month1});
Is there a way I can still have the flexibility of adding either a single line item or a range of items, and it also auto-calculate/aggregate the data for me using the composite?
Any help would be great!
Thank You,
Anup
For me, it seems you're looking for RX (Reactive eXtensions) so you don't have to sum manually each time at each level. Instead, just setup necessary subscriptions and get re-calculations automatically. For example:
Good example of Reactive Extensions Use
I solved this problem. For those who are interested, here's how I solved it:
I built an interface as shown:
public interface IInventoryReportItem
{
string Title { get; set; }
int Quota { get; }
int TotalTicketsSold { get; }
int TotalUnitsSold { get; }
decimal TotalSalesAmount { get; }
}
I implemented this interface in a class called as a LineItem as follows:
public class LineItem : IInventoryReportItem
{
public LineItem(string title, int quota, int totalTicketsSold, int totalUnitsSold, int totalCheckedIn,
decimal totalSalesAmount)
{
Title = title;
Quota = quota;
TotalUnitsSold = totalUnitsSold;
TotalTicketsSold = totalTicketsSold;
TotalCheckedIn = totalCheckedIn;
TotalSalesAmount = totalSalesAmount;
}
public string Title { get; set; }
public int Quota { get; }
public int TotalTicketsSold { get; }
public int TotalUnitsSold { get; }
public int TotalCheckedIn { get; }
public decimal TotalSalesAmount { get; }
}
I also created a custom collection class called LineItems as shown. Note that the collection is of the type IInventoryReportItem itself:
public class LineItems : IInventoryReportItem
{
public string Title { get; set; }
public int Quota => Contents?.Sum(x => x.Quota) ?? 0;
public int TotalTicketsSold => Contents?.Sum(x => x.TotalTicketsSold) ?? 0;
public int TotalUnitsSold => Contents?.Sum(x => x.TotalUnitsSold) ?? 0;
public decimal TotalSalesAmount => Contents?.Sum(x => x.TotalSalesAmount) ?? 0;
public readonly List<IInventoryReportItem> Contents;
public LineItems(List<IInventoryReportItem> lineItems)
{
Contents = lineItems ?? new List<IInventoryReportItem>();
}
}
All of the aggregation is done at this collection class level.
The report class is as follows:
public class InventoryReport
{
public DateRange DateRange { get; set; }
public IInventoryReportItem LineItems { get; set; }
}
I was then able to build the report like so:
Report = new InventoryReport();
var week1 = new LineItems(new List<IInventoryReportItem>
{
new LineItem("Day1", 10, 10, 10, 4, 100),
new LineItem("Day2", 10, 5, 5, 1, 50)
})
{Title = "Week1"};
var week2 = new LineItems(new List<IInventoryReportItem>
{
new LineItem("Day1", 20, 20, 20, 20, 200),
new LineItem("Day2", 20, 5, 5, 5, 50)
}) {Title = "Week2"};
var month1 = new LineItems(new List<IInventoryReportItem> {week1, week2}) {Title = "January"};
Report.LineItems = new LineItems(new List<IInventoryReportItem> {month1}) {Title = "Daily Report"};
The final output (JSON) that I receive from my API is like so:
{
"lineItems": {
"contents": [
{
"contents": [
{
"contents": [
{
"title": "Day1",
"quota": 10,
"totalTicketsSold": 10,
"totalUnitsSold": 10,
"totalCheckedIn": 4,
"totalSalesAmount": 100
},
{
"title": "Day2",
"quota": 10,
"totalTicketsSold": 5,
"totalUnitsSold": 5,
"totalCheckedIn": 1,
"totalSalesAmount": 50
}
],
"title": "Week1",
"quota": 20,
"totalTicketsSold": 15,
"totalUnitsSold": 15,
"totalSalesAmount": 150
},
{
"contents": [
{
"title": "Day1",
"quota": 20,
"totalTicketsSold": 20,
"totalUnitsSold": 20,
"totalCheckedIn": 20,
"totalSalesAmount": 200
},
{
"title": "Day2",
"quota": 20,
"totalTicketsSold": 5,
"totalUnitsSold": 5,
"totalCheckedIn": 5,
"totalSalesAmount": 50
}
],
"title": "Week2",
"quota": 40,
"totalTicketsSold": 25,
"totalUnitsSold": 25,
"totalSalesAmount": 250
}
],
"title": "January",
"quota": 60,
"totalTicketsSold": 40,
"totalUnitsSold": 40,
"totalSalesAmount": 400
}
],
"title": "Daily Report",
"quota": 60,
"totalTicketsSold": 40,
"totalUnitsSold": 40,
"totalSalesAmount": 400
}
}
Using this approach, I was able to eliminate the overhead of performing an aggregation and was still able to use a collection or individual items using the same signature.
Hopefully someone finds this helpful!

Group By Multiple Column In LINQ in C#

I have a class like as follows:
public class ActualClass
{
public string BookName { get; set; }
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public bool Status { get; set; }
}
It has following data in the table:
I would like to group them by IssuerName and DateOfIssue for the following viewModel class:
public class ViewModel
{
public string IssuerName { get; set; }
public DateTime DateOfIssue { get; set; }
public List<string> Books { get; set; }
}
And data will be displayed as follows: (Screenshot data will be replaced by the previous table data after successful grouping)
Special attention: Is there anything wrong in my ViewModel according to my expectation?
I tried a lot after following some stackoverflow answers but none did work for me. Any help will be highly appreciated.
The code I have tried:
var viewmodel = from b in db.BookIssues
group b by new
{
b.IssuerName,
b.DateOfIssue
}
into g
select new ViewModel()
{
Name = g.key.IssuerName,
DateOfIssue = g.Key.DateOfIssue,
Books = g.ToList() //Actually this line of code is not working
};
Books = g.ToList() //Actually this line of is not working
Probably because Books property is type of List<string>, not List<ActualClass>.
Can you please try this query, I added b.Select(bn => bn.BookName).ToList() to extract only names of books:
var books = new List<ActualClass>
{
new ActualClass { BookName = "A", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "B", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "C", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "1" },
new ActualClass { BookName = "D", DateOfIssue = new DateTime(2015, 10, 10, 10, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "E", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" },
new ActualClass { BookName = "F", DateOfIssue = new DateTime(2015, 10, 10, 12, 10, 0), IssuerName = "2" }
};
var result = books.GroupBy(x => new { x.IssuerName, x.DateOfIssue })
.Select(b => new ViewModel
{
Books = b.Select(bn => bn.BookName).ToList(),
// Accessing to DateOfIssue and IssuerName from Key.
DateOfIssue = b.Key.DateOfIssue,
IssuerName = b.Key.IssuerName
});
I grouped by: x.IssuerName, x.DateOfIssue. I did that by passing anonymous type in GroupBy() with following manner: x => new { x.IssuerName, x.DateOfIssue }.
Now they are in key and you can access to IssuerName and DateOfIssue from KEY in SELECT statement like in following: b.Key.IssuerName and b.Key.DateOfIssue.
if you need to select list of books from group result, you need Books = v.Select(c=>c.BookName).ToList() also note that in case of you have time in issue date time you may need to group by only the date part using EntityFunctions.TruncateTime function. if you only storing date only then you can ignore this function.
var viewmodel = db.BookIssues.GroupBy(x=>new {IssuerName =x.IssuerName, DateOfIssue=EntityFunctions.TruncateTime(x.DateOfIssue) })
.Select(v=>new ViewModel(){IssuerName =v.Key.IssuerName, DateOfIssue = v.Key.DateOfIssue, Books = v.Select(c=>c.BookName).ToList() })
.ToList();

Distinct rows by their values from sub list LINQ C#

I would like to remove duplicate rows by their values from sub list using LINQ syntax. Below I attached code which does that by different way.
xxxxx xxxxxx xxxxxxx xxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxx xxxxxxxxxxxxxxxxxx
List<ZRowCollection> zListCollection = new List<ZRowCollection>();
zListCollection = zListCollection.OrderBy(p => p.P).ToList();
int i1 = 1;
foreach (var item in zListCollection.ToList())
{
var subList1 = item.XRowModified.Select(p => p).ToList();
foreach (var item2 in zListCollection.Skip(i1).ToList())
{
var subList2 = item2.XRowModified.Select(p => p).ToList();
int i = 0;
foreach (var item3 in subList1)
{
var t2 = subList2.Select(p => p.Average).ToList();
decimal average = t2[i];
if (item3.Average == average)
{
i++;
}
else break;
}
if (i == item2.XRowModified.Count)
{
zListCollection.Remove(item2);
}
}
i1++;
}
properties
class XRowModified
{
public decimal Id { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public DateTime Time { get; set; }
public decimal Average { get; set; }
}
class ZRowCollection
{
public ZRowCollection()
{
this.XRowModified = new HashSet<XRowModified>();
}
public int P { get; set; }
public int High { get; set; }
public int Low { get; set; }
public virtual ICollection<XRowModified> XRowModified { get; set; }
}
expected input/output,
as a comparer column Average in List<XRowModified>
List<ZRowCollection> zListInput = new List<ZRowCollection>(){
new ZRowCollection(){P = 0,High = 4,Low = 0, XRowModified = new List<XRowModified>(){
new XRowModified(){ Id = 1550, Open = 1.22M,High = 1.24M,Low = 1.21M,Close = 1.23M,Average = 1.225M,
Time = new DateTime(2012, 11, 9, 12, 23, 23, 222)},
new XRowModified(){ Id = 1551, Open = 1.20M,High = 1.24M,Low = 1.22M,Close = 1.20M,Average = 1.23M,
Time = new DateTime(2012, 11, 9, 12, 23, 25, 122)}}},
new ZRowCollection(){P = 1,High = 3,Low = 0, XRowModified = new List<XRowModified>(){
new XRowModified(){ Id = 1555, Open = 1.22M,High = 1.24M,Low = 1.21M,Close = 1.23M,Average = 1.225M,
Time = new DateTime(2012, 11, 9, 12, 23, 40, 422)},
new XRowModified(){ Id = 1556, Open = 1.20M,High = 1.25M,Low = 1.20M,Close = 1.20M,Average = 1.23M,
Time = new DateTime(2012, 11, 9, 12, 23, 46, 522)}}},
new ZRowCollection(){P = 2,High = 2,Low = 0, XRowModified = new List<XRowModified>(){
new XRowModified(){ Id = 1558, Open = 1.22M,High = 1.24M,Low = 1.21M,Close = 1.23M,Average = 1.225M,
Time = new DateTime(2012, 11, 9, 12, 30, 11, 622)},
new XRowModified(){ Id = 1559, Open = 1.20M,High = 1.24M,Low = 1.22M,Close = 1.20M,Average = 1.23M,
Time = new DateTime(2012, 11, 9, 12, 30, 12, 822)}}}
};
List<ZRowCollection> zListOutput = new List<ZRowCollection>(){
new ZRowCollection(){P = 0,High = 4,Low = 0, XRowModified = new List<XRowModified>(){
new XRowModified(){ Id = 1550, Open = 1.22M,High = 1.24M,Low = 1.21M,Close = 1.23M,Average = 1.225M,
Time = new DateTime(2012, 11, 9, 12, 23, 23, 222)},
new XRowModified(){ Id = 1551, Open = 1.20M,High = 1.24M,Low = 1.22M,Close = 1.20M,Average = 1.23M,
Time = new DateTime(2012, 11, 9, 12, 23, 25, 122)}}}
};
In this case I would consider writing a custom equality comparer, which could then be plugged in to the Distinct method. To do this you need two functions Equals and GetHashCode.
Note: It is important the GetHashCode returns the same hash for two equal objects as this is the first thing that Distinct checks.
From what I gather from your code two ZRow's are equal if their XRow's have the same sequence of averages, so our equality is that averageSequence1.SequenceEqual(averageSequence2) which would be implemented as so:
public class CustomComparer : IEqualityComparer<ZRowCollection>
{
public static CustomComparer Instance { get { return new CustomComparer(); } }
Int32 IEqualityComparer<ZRowCollection>.GetHashCode(ZRowCollection value)
{
//One could also use the sum of the averages here, but went for simplicity...
return value.XRowModified.Count;
}
Boolean IEqualityComparer<ZRowCollection>.Equals(ZRowCollection z1, ZRowCollection z2)
{
return z1.XRowModified.Select(x => x.Average)
.SequenceEqual(z2.XRowModified.Select(x => x.Average));
}
}
and which would be used like so:
var distinctList = zListCollection.Distinct(CustomComparer.Instance);

Nothing is showing up in my chart, is something wrong with my databinding?

I've got the following XAML:
<amq:SerialChart Grid.Column="0" Grid.Row="0" DataSource="{Binding Data}" CategoryValueMemberPath="Date"
AxisForeground="White"
PlotAreaBackground="Black"
GridStroke="DarkGray">
<amq:SerialChart.Graphs>
<amq:LineGraph ValueMemberPath="Downfall" Title="Downfall" Brush="Blue" />
</amq:SerialChart.Graphs>
</amq:SerialChart>
And my code looks like this:
namespace DownfallControl
{
public partial class MainPage : PhoneApplicationPage
{
private ObservableCollection<DownfallLog> Data = new ObservableCollection<DownfallLog>()
{
new DownfallLog() { Date = new DateTime(2012, 2, 2), Downfall = 90.1 },
new DownfallLog() { Date = new DateTime(2012, 2, 3), Downfall = 89.6 },
new DownfallLog() { Date = new DateTime(2012, 2, 4), Downfall = 85.6 }
};
public MainPage()
{
InitializeComponent();
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this;
Data.Add(new DownfallLog() { Date = new DateTime(2012, 2, 6), Downfall = 85.6 });
}
}
public class DownfallLog
{
public double Downfall;
public DateTime Date;
}
}
Any clue why nothing is showing up?
The ObservableCollection has to be public and a property, so this:
private ObservableCollection<DownfallLog> Data = new ObservableCollection<DownfallLog>()
{
new DownfallLog() { Date = new DateTime(2012, 2, 2), Downfall = 90.1 },
new DownfallLog() { Date = new DateTime(2012, 2, 3), Downfall = 89.6 },
new DownfallLog() { Date = new DateTime(2012, 2, 4), Downfall = 85.6 }
};
should be this:
public ObservableCollection<DownfallLog> Data {get;set;}
You can initialize the collection in your constructor.
These have to be properties as well:
public class DownfallLog
{
public double Downfall {get;set;}
public DateTime Date {get;set;}
}

Categories