ComboBox data types supported - c#

Can I set the data type for a Value of a ComboBox?
I'm have a long list of keyCodes I want to use in the RegisterHotKey() function, which takes an uint VK data type.
RegisterHotKey(_hWnd, 1, (uint)fsModifiers.Control, (uint)Keys.Insert);
I set my combobox value as "(uint)Keys.Insert". Then I assign the value to a string variable and try to use the variable cast as (uint) in the RegisterHotKey function but I keep getting data type errors.
Either I'm not casting right or this can't be done?
Thanks...
cboHotkeyModifier.ValueMember = "Value";
cboHotkeyModifier.DisplayMember = "Text";
items = new[] {
new { Value = "", Text = "" },
new { Value = "Shift", Text = "Shift" },
new { Value = "(uint)fsModifiers.Control", Text = "Control" },
new { Value = "Alt", Text = "Alt" }
};
cboHotkey.ValueMember = "Value";
cboHotkey.DisplayMember = "Text";
items = new[] {
new { Value = "D0", Text = "0" },
new { Value = "D1", Text = "1" },
new { Value = "D2", Text = "2" },
new { Value = "D3", Text = "3" },
new { Value = "D4", Text = "4" },
};
cboHotkey.DataSource = items;
keyData = cboHotkey.SelectedItem;
keyModifier = cboHotkeyModifier.SelectedItem;
RegisterHotKey(_hWnd, 1, keyModifer, keyCode);

Related

converting int to List<double?>, ChartJS Core

I'm working on a web application. I found this interesting https://github.com/mattosaurus/ChartJSCore. to use charts in my application.
The charts are working successfully in most pages. But in one page i have the following idea:
I have 3 properties (Appropriate, Inappropriate, NoInteraction) in my model all with type (int) and i need to keep it as integer to manipulate other functionalities in the application. Each property will be represented as one series in the chart and it should be list or array of 15 integer always.
Here is my properties in Session Model:
public int DayNumber { get; set; }
public int Appropriate { get; set; }
public int NotAppropriate { get; set; }
public int NoInteraction { get; set; }
Here is my Controller:
public IActionResult Details()
{
var result = _db.Session.ToList();
//I want this appropriateLine to be passed to GenerateLineChart method but whenever i tried i came up with an error of converting types.
var AppropriateLine = result.Select(x => x.Appropriate).ToList();
Chart lineChart = GenerateLineChart();
ViewData["LineChart"] = lineChart;
return View();
}
private static Chart GenerateLineChart()
{
Chart chart = new Chart();
chart.Type = Enums.ChartType.Line;
ChartJSCore.Models.Data data = new ChartJSCore.Models.Data();
data.Labels = new List<string>() { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
LineDataset AppropriateDataset = new LineDataset()
{
Label = "Appropriate Data Line",
Data = new List<double?>() { 2, 6, 2, 6, 2, 6 }, //Here i want this to be filled with data from AppropriateLine variable, it works for the fixed value only
Fill = "false",
LineTension = 0.1,
BackgroundColor = ChartColor.FromHexString("#FF6384"),
BorderColor = ChartColor.FromHexString("#FF6384"),
BorderCapStyle = "butt",
BorderDash = new List<int> { },
BorderDashOffset = 0.0,
BorderJoinStyle = "miter",
PointBorderColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointBackgroundColor = new List<ChartColor>() { ChartColor.FromHexString("#fff") },
PointBorderWidth = new List<int> { 1 },
PointHoverRadius = new List<int> { 5 },
PointHoverBackgroundColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointHoverBorderColor = new List<ChartColor>() { ChartColor.FromHexString("#FF6384"), },
PointHoverBorderWidth = new List<int> { 2 },
PointRadius = new List<int> { 1 },
PointHitRadius = new List<int> { 10 },
SpanGaps = false
};
data.Datasets = new List<Dataset>();
data.Datasets.Add(AppropriateDataset);
Options options = new Options()
{
Scales = new Scales()
};
Scales scales = new Scales()
{
YAxes = new List<Scale>()
{
new CartesianScale()
}
};
CartesianScale yAxes = new CartesianScale()
{
Ticks = new Tick()
};
Tick tick = new Tick()
{
Callback = "function(value, index, values) {return '' + value;}"
};
yAxes.Ticks = tick;
scales.YAxes = new List<Scale>() { yAxes };
options.Scales = scales;
chart.Options = options;
chart.Data = data;
return chart;
}
How can i implement this "Explicit Casting" of appropriateLine variable which is (int) before passing it to GenerateLineChart().
Note that i don't want to change Model properties type since many functions depend on it. Also, I cannot change Data type from List<double?> since many other problems solved by adding this.
I tried many casting solutions but none of them works for me such as:
(List<double?>)result.Select(x => x.Appropriate);
private static Chart GenerateLineChart((List<double?>)AppropriateLine)
I've read about "Convert all" method but not worked.
Any help is much appreciated,
Thanks in advance.
Based on what you mention, it seems that the data passed to GenerateLineChart will only be used for UI purposes. That is, this method can get an appropriate copy of the original data. If this is the case, then, the solution you attempted with
(List<double?>)result.Select(x => x.Appropriate)
was very close, but the cast needs to be done inside Select, i.e.
result.Select(x => (double?)x.Appropriate)
Here is a sketch of your code with that change
public IActionResult Details()
{
var result = _db.Session.ToList();
var AppropriateLine = result.Select(x => (double?)x.Appropriate).ToList();
var lineChart = GenerateLineChart(AppropriateLine);
// Rest or your code
}
private static Chart GenerateLineChart(IEnumerable<double?> data)
{
// Your code as is here ....
LineDataset AppropriateDataset = new LineDataset()
{
Data = data,
// Rest of your code
}
// ....
}
maybe you can check at the moment that you need the list if the number is double or int bool isInt = d == (int)d; and parse to use it only for that time on an aux.

c# Resort a list

This is a pretty complicated sort. Basically I take a list of Answers and I order them by Question priority first. Priority is a zero based integer (0 being the highest priority).
What I want to do then, is sort each answer by it's priority.
For example, like this:
var answers = new List<AnswerRequestModel>
{
new AnswerRequestModel
{
Id = 1,
Priority = 0,
Text = "Very high"
},
new AnswerRequestModel
{
Id = 2,
Priority = 1,
Text = "High"
},
new AnswerRequestModel
{
Id = 3,
Priority = 2,
Text = "Medium"
},
new AnswerRequestModel
{
Id = 4,
Priority = 3,
Text = "Low"
}
};
This assumes there is only one question. If there were two questions:
var answers = new List<AnswerRequestModel>
{
new AnswerRequestModel
{
Id = 1,
Priority = 0,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Very high",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Very high", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 2,
Priority = 1,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "High",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "High", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 3,
Priority = 2,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Medium",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Medium", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 4,
Priority = 3,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Low",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Low", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 5,
Priority = 0,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1},
Text = "Blacks",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Blacks", Operator = "=", Field = "colour"} }
},
new AnswerRequestModel
{
Id = 6,
Priority = 1,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Silvers",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Silvers", Operator = "=", Field = "colour" } }
},
new AnswerRequestModel
{
Id = 7,
Priority = 2,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Reds",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Blues", Operator = "=", Field = "colour" } }
},
new AnswerRequestModel
{
Id = 8,
Priority = 3,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Yellows",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Yellows", Operator = "=", Field = "colour" } }
}
};
In both these examples, the order is correct. But now comes the complication.
If I select an answer. I need it to resort, also if the answers and questions come in the wrong order I want them to be sorted correctly.
The method needs to also work when selecting multiple answers.
My method currently looks like this:
public IList<AnswerRequestModel> SortAnswersByPriority(IList<AnswerRequestModel> answers)
{
if (!answers.Any()) return answers;
var chosenAnswers = answers.Where(m => m.Active).ToList();
var sortedAnswers = answers.OrderBy(m => m.Question.Priority).ThenBy(m => m.Priority);
if (!chosenAnswers.Any())
return sortedAnswers.ToList();
var questionIds = answers.GroupBy(m => m.Question.Id).Select(m => m.Key).ToList();
foreach(var questionId in questionIds)
{
var questionAnswers = answers.Where(m => m.Question.Id == questionId).ToList();
if (!questionAnswers.Any()) continue;
var highestPriority = questionAnswers.OrderBy(m => m.Priority).First().Priority;
var chosenQuestionAnswers = answers.Where(m => m.Active).ToList();
var count = chosenQuestionAnswers.Count;
var ordered = questionAnswers.OrderBy(a => 1);
switch (count)
{
case 1:
var choseHighest = chosenQuestionAnswers.SingleOrDefault(m => m.Priority.Equals(highestPriority)) != null;
ordered = choseHighest ? ordered.OrderBy(m => m.Priority) : ordered.OrderByDescending(m => m.Priority);
break;
default:
ordered = ordered.ThenByDescending(m => m.Active).ThenByDescending(m => m.Priority);
break;
}
var questionAnswerIds = ordered.Select(m => m.Id).ToList();
sortedAnswers = sortedAnswers.ThenBy(m => questionAnswerIds.IndexOf(m.Id));
}
return sortedAnswers.ToList();
}
If I put a breakpoint on sortedAnswers = sortedAnswers.ThenBy(m => questionAnswerIds.IndexOf(m.Id)); I can see that my sort is working correctly, because the answer ids are sorted. But when it executes the line with the breakpoint, nothing changes.
Can someone help me figure this out?
Unless your breakpoint is after the ToList() then you won't see a change. The OrderBy/ThenBy methods are implemented using deferred execution. Until you start enumerating the ThenBy query (i.e. using a foreach or ToList/ToArray) you won't see any changes.
sortedAnswers = sortedAnswers
.ThenBy(m => questionAnswerIds.IndexOf(m.Id))
.ToList(); // put your break point after this line
I solved this, but I think it was actually an issue with my logic rather than anything else. I changed my method to this:
public IList<AnswerRequestModel> SortAnswersByPriority(IList<AnswerRequestModel> answers)
{
if (!answers.Any()) return answers;
var chosenAnswers = answers.Where(m => m.Active).ToList();
// If we have no chosen answers, then just do a default sort by priority
if (!chosenAnswers.Any()) return answers.OrderBy(m => m.Question.Priority).ThenBy(m => m.Priority).ToList();
var sortedAnswers = answers.OrderBy(a => 1);
var questionIds = answers.GroupBy(m => m.Question.Id).Select(m => m.Key).ToList();
foreach(var questionId in questionIds)
{
var questionAnswers = answers.Where(m => m.Question.Id == questionId).ToList();
if (!questionAnswers.Any()) continue;
var lowestPriority = questionAnswers.OrderByDescending(m => m.Priority).First().Priority;
var chosenQuestionAnswers = answers.Where(m => m.Active).ToList();
var count = chosenQuestionAnswers.Count;
var ordered = questionAnswers.OrderBy(a => 1);
// Sort by our chosen answers first, then by answer priority
if (chosenQuestionAnswers.Any(m => m.Priority.Equals(lowestPriority)))
{
switch (count)
{
case 1:
ordered = ordered.OrderByDescending(m => m.Priority);
break;
default:
ordered = ordered.OrderByDescending(m => m.Active).ThenByDescending(m => m.Priority);
break;
}
} else
{
ordered = ordered.OrderByDescending(m => m.Active).ThenBy(m => m.Priority);
}
var questionAnswerIds = ordered.Select(m => m.Id).ToList();
sortedAnswers = sortedAnswers.ThenBy(m => questionAnswerIds.IndexOf(m.Id));
}
// Once we have sorted by our answer priority, do a final sort on question priority and return the list
return sortedAnswers.OrderBy(m => m.Question.Priority).ToList();
}
and in answer to #mjwills, the code I supplied should have been enough for you to run locally. If you created a model AnswerRequestModel and a the SortAnswersByPriority method and passed in the list I used in my example, you should be able to execute it without issue.
I could have supplied my unit test:
[Test]
public void ShouldSortOnMultipleQuestionsWithOneChosenAnswerPerQuestion()
{
// Assemble
var services = SortContext.GivenServices();
var sortProvider = services.WhenCreateSortProvider();
var answers = new List<AnswerRequestModel>
{
new AnswerRequestModel
{
Id = 1,
Priority = 0,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Very high",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Very high", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 2,
Priority = 1,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "High",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "High", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 3,
Priority = 2,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Medium",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Medium", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 4,
Priority = 3,
Question = new QuestionRequestModel { Type = QuestionType.Two },
Text = "Low",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Low", Operator = "=", Field = "quality"} }
},
new AnswerRequestModel
{
Id = 5,
Priority = 0,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1},
Text = "Blacks",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Blacks", Operator = "=", Field = "colour"} }
},
new AnswerRequestModel
{
Id = 6,
Priority = 1,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Silvers",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Silvers", Operator = "=", Field = "colour" } }
},
new AnswerRequestModel
{
Id = 7,
Priority = 2,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Reds",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Blues", Operator = "=", Field = "colour" } }
},
new AnswerRequestModel
{
Id = 8,
Priority = 3,
Question = new QuestionRequestModel { Type = QuestionType.Two, Priority = 1 },
Text = "Yellows",
Formulas = new List<AnswerFormula> { new AnswerFormula { Expression = "Yellows", Operator = "=", Field = "colour" } }
}
};
answers[1].Active = true;
answers[6].Active = true;
// Act
var sortedAnswers = sortProvider.SortAnswersByPriority(answers);
var firstAnswer = sortedAnswers[0];
var secondAnswer = sortedAnswers[1];
var thirdAnswer = sortedAnswers[2];
var forthAnswer = sortedAnswers[3];
var fifthAnswer = sortedAnswers[4];
var sixthAnswer = sortedAnswers[5];
var seventhAnswer = sortedAnswers[6];
var eigthAnswer = sortedAnswers[7];
// Assert
firstAnswer.Text.Should().Be("High");
secondAnswer.Text.Should().Be("Very high");
thirdAnswer.Text.Should().Be("Medium");
forthAnswer.Text.Should().Be("Low");
fifthAnswer.Text.Should().Be("Reds");
sixthAnswer.Text.Should().Be("Blacks");
seventhAnswer.Text.Should().Be("Silvers");
eigthAnswer.Text.Should().Be("Yellows");
}

How to set the value of dropdown coming from model in ASP.NET MVC

I am stuck at "how to set value in the dropdown to the value that is set in the model.
I am adding what I have till now and what I have tried.
So, I have a PaymentFormMV Model and it has another Model in it which is PaymentCCMV
public class PaymentFormMV
{
public PaymentCCMV CreditDetails { get; set; }
public string ModelPropertyPrefixName
{
get
{
return this.ModelPropertyPrefix.Replace("_", ".");
}
}
}
public class PaymentCCMV
{
[Display(Name = "Expiration Date")]
public int ExpirationMonth { get; set; }
}
cshtml file takes data from PaymentFormMV
<div class="form-group">
#Html.LabelFor(model => model.CreditDetails.ExpirationMonth, htmlAttributes: new { #class = "control-label col-md-2 required" })
<div class="col-md-10">
#Html.DropDownList(Model.ModelPropertyPrefixName + "CreditDetails.ExpirationMonth", Helpers.PossibleMonths,
new { id = Model.ModelPropertyPrefix + "CreditDetails_ExpirationMonth", #class = "form-control", style = "width:70px;" })
</div>
</div>
Helpers.PossibleMonths: displays all the months
public static ICollection<SelectListItem> PossibleMonths
{
get
{
return new List<SelectListItem>()
{
new SelectListItem() { Text = "1 - Jan", Value = "1" },
new SelectListItem() { Text = "2 - Feb", Value = "2" },
new SelectListItem() { Text = "3 - Mar", Value = "3" },
new SelectListItem() { Text = "4 - Apr", Value = "4" },
new SelectListItem() { Text = "5 - May", Value = "5" },
new SelectListItem() { Text = "6 - Jun", Value = "6" },
new SelectListItem() { Text = "7 - Jul", Value = "7" },
new SelectListItem() { Text = "8 - Aug", Value = "8" },
new SelectListItem() { Text = "9 - Sep", Value = "9" },
new SelectListItem() { Text = "10 - Oct", Value = "10" },
new SelectListItem() { Text = "11 - Nov", Value = "11" },
new SelectListItem() { Text = "12 - Dec", Value = "12" },
};
}
}
Points:
I am getting integer value model.CreditDetails.ExpirationMonth = 2 (as per DB)
But I don't know how to set it to the drop down. So when the screen loads I want that value to be tied to the dropdown. Please guide me. SO, if the value is 2 I should get something like 2 - Feb in the Dropdown UI as mentioned in the Helper Method. I am a beginner so I might not be following the best practice so bear with me on this.
HTML GENERATED:
I second #Vivien's response, but I would recommend to use the overload that takes the selected value (See MSDN:), which you can then set. To then allow passing in the default, I would also recommend using a static method, and allowing the month to be passed in:
public static SelectList PossibleMonths(string defaultValue)
{
return new SelectList(List<SelectListItem>()
{
new SelectListItem() { Text = "1 - Jan", Value = "1" },
new SelectListItem() { Text = "2 - Feb", Value = "2" },
new SelectListItem() { Text = "3 - Mar", Value = "3" },
new SelectListItem() { Text = "4 - Apr", Value = "4" },
new SelectListItem() { Text = "5 - May", Value = "5" },
new SelectListItem() { Text = "6 - Jun", Value = "6" },
new SelectListItem() { Text = "7 - Jul", Value = "7" },
new SelectListItem() { Text = "8 - Aug", Value = "8" },
new SelectListItem() { Text = "9 - Sep", Value = "9" },
new SelectListItem() { Text = "10 - Oct", Value = "10" },
new SelectListItem() { Text = "11 - Nov", Value = "11" },
new SelectListItem() { Text = "12 - Dec", Value = "12" },
}, "Value", "Text", defaultValue);
}
EDIT: Your dropdown could would then look like
#Html.DropDownList(Model.ModelPropertyPrefixName + "CreditDetails.ExpirationMonth", Helpers.PossibleMonths(Model.CreditDetails.ExpirationMonth),
new { id = Model.ModelPropertyPrefix + "CreditDetails_ExpirationMonth", #class = "form-control", style = "width:70px;" })
That assumes the CreditDetails object is always not null, otherwise null checking is a must.
EDIT: I was incorrect, apparently Html.DropDownList doesn't support SelectList, so I reverted back to the original approach with a twist for setting the selected value (note same drop down code above for this to work).
public static IEnumerable<SelectListItem> PossibleMonths(string defaultValue)
{
if (defaultValue == null)
defaultValue = "";
return new List<SelectListItem>()
{
new SelectListItem() { Text = "1 - Jan", Value = "1", Selected = (defaultValue == "1") },
new SelectListItem() { Text = "2 - Feb", Value = "2", Selected = (defaultValue == "2") },
new SelectListItem() { Text = "3 - Mar", Value = "3", Selected = (defaultValue == "3") },
new SelectListItem() { Text = "4 - Apr", Value = "4", Selected = (defaultValue == "4") },
new SelectListItem() { Text = "5 - May", Value = "5", Selected = (defaultValue == "5") },
new SelectListItem() { Text = "6 - Jun", Value = "6", Selected = (defaultValue == "6") },
new SelectListItem() { Text = "7 - Jul", Value = "7", Selected = (defaultValue == "7") },
new SelectListItem() { Text = "8 - Aug", Value = "8", Selected = (defaultValue == "8") },
new SelectListItem() { Text = "9 - Sep", Value = "9", Selected = (defaultValue == "9") },
new SelectListItem() { Text = "10 - Oct", Value = "10", Selected = (defaultValue == "10") },
new SelectListItem() { Text = "11 - Nov", Value = "11", Selected = (defaultValue == "11") },
new SelectListItem() { Text = "12 - Dec", Value = "12", Selected = (defaultValue == "12") },
};
}
The DropDownList() (and the preferred DropDownListFor() methods work by binding to the value of your property. In your case, you want to bind to the ExpirationMonth property, the code in your view should be
#Html.DropDownList("CreditDetails.ExpirationMonth", Helpers.PossibleMonths, new { #class = "form-control", style = "width:70px;" }
or better
#Html.DropDownListFor(m => m.CreditDetails.ExpirationMonth, Helpers.PossibleMonths, new { #class = "form-control", style = "width:70px;" }
and if you have set the value of ExpirationMonth to say 3 in the GET method and pass the model to the view, then the 3rd option ("Mar") will be selected.
It is not clear what your ModelPropertyPrefix and ModelPropertyPrefixName properties return, however they are not required, and should not be used when in the view (the HtmlHelper methods will always generate the correct name attributes necessary for 2-way model binding).
Note also that it is a common misconception to believe that the Selected property of SelectListItem needs to be set when generating the SelectList. When binding to a model property, the DropDownList() and DropDownListFor() methods internally generate their own IEnumerable<SelectListItem> and set the Selected property based on the value of the property (any attempt to set it yourself is ignored). The only time the Selected property is respected is when you use DropDownList("xxx", ...) and xxx is not a property or the model.
As a side note, your code in the static method could be just
public static IEnumerable<SelectListItem> PossibleMonths()
{
return Enumerable.Range(1, 12).Select(x => new SelectListItem()
{
Value = x.ToString(),
Text = System.Globalization.CultureInfo.CurrentCulture.DateTimeForm‌at.GetMonthName(x)
});
}
and your view models should not contain properties which are data models when editing data. PaymentFormMV should contain public int ExpirationMonth { get; set; } (and other properties of PaymentCCMV you need), not public PaymentCCMV CreditDetails { get; set; }
Based on your edit showing the image of the html, your generating a name attribute which does not relate to your PaymentFormMV model that is being used in the view (it does not have a property named PaymentInfo) and therefore your not correctly binding to the model, and the correct option will not be selected.
From the comments in chat, you need to change the view to use #model MoveInMV and then the method will be
#Html.DropDownListFor(m => m.PaymentInfo.CreditDetails.ExpirationMonth, Helpers.PossibleMonths, ...)
to give correct 2-way model binding.

Populating a highchart graph from a linq query

I am trying to populate a chart with data from my database. I'm using Entity framework and fairly new to asp.net.
What I'm trying to do is populate the chart from my linq query.
var totals = from s in db.ClassInstanceDetails.Include("ClassInstance")
where s.ClassInstance.ClassID == 2
group s by s.ClassInstance.Date into grouped
select new
{
CIDate = grouped.Key,
TotalStudentsInClass = grouped.Count(s => s.Attendance)
};
The linq query works fine, it counts all the students in a classinstance, groups them and counts them. My problem is how do extract the data and put it into the chart. When I debug I can see that the totals variable is
{System.Data.Objects.ObjectQuery<<>f__AnonymousType0<System.DateTime,int>>}
and I can see the results view held by totals as :
{CIDate = {04/09/2012}, TotalStudentsInClass = 5}
{CIDate = {05/09/2012}, TotalStudentsInClass = 7}
{CIDate = {06/09/2012}, TotalStudentsInClass = 14}
Which is great as the query works, it finds how many pupils attended a particular class instance. I'm trying to put this data into a line graph using highcharts. I've tried to separate the linq results into 2 arrays one array containing the dates and another array containing the TotalStudentsInClass value but having no luck as the types are different??? Is this even the right way to do this?
The example I found online pulls from an array I think
.SetSeries(new[]
{
new Series { Name = "Tokyo", Data = new Data(ChartsData.TokioData) },
new Series { Name = "New York", Data = new Data(ChartsData.NewYorkData) },
new Series { Name = "Berlin", Data = new Data(ChartsData.BerlinData) },
new Series { Name = "London", Data = new Data(ChartsData.LondonData) }
}
with the data coming from an object,
public static object[] TokioData = new object[] { 7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2, 26.5, 23.3, 18.3, 13.9, 9.6 };
I've tried to run my linq query into an object but that throws me more errors than I know what to do with!!
Any help would be GREATLY appreciated!!
Chart
public static Highcharts TimeSeriesZoomable(Series[] Series, Number MinRange, Number PointInterval, DateTime PointStartDate, AxisTypes XAxisType = AxisTypes.Datetime, string Title = "", string SubTitle = "", string XAxisTitle = "", string YAxisTitle = "", string ToolTipFormat = "", string YAxisLabel = "")
{
Highcharts chart = new Highcharts("chart")
.SetOptions(new GlobalOptions { Global = new Global { UseUTC = false } })
.InitChart(new Chart { ZoomType = ZoomTypes.X, SpacingRight = 20, DefaultSeriesType = ChartTypes.Area, Height = 300, BorderRadius = 0 })
.SetTitle(new Title { Text = Title })
.SetSubtitle(new Subtitle { Text = SubTitle })
.SetXAxis(new XAxis
{
Type = XAxisType,
MinRange = MinRange,
Title = new XAxisTitle { Text = XAxisTitle }
})
.SetYAxis(new YAxis
{
Title = new YAxisTitle { Text = YAxisTitle },
Min = 0.6,
StartOnTick = false,
EndOnTick = false,
Labels = new YAxisLabels
{
Formatter = #"function() { return this.value +' " + YAxisLabel + "';}"
}
})
.SetTooltip(new Tooltip { Shared = true/*, Formatter = #"function() { return ''+ this.x +' - '+ this.y +' " + ToolTipFormat + "'; }" */})
.SetLegend(new Legend { Enabled = true, VerticalAlign = VerticalAligns.Top })
.SetPlotOptions(new PlotOptions
{
Line = new PlotOptionsLine
{
LineWidth = 3,
Marker = new PlotOptionsLineMarker
{
Enabled = false,
States = new PlotOptionsLineMarkerStates
{
Hover = new PlotOptionsLineMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsLineStates { Hover = new PlotOptionsLineStatesHover { LineWidth = 3 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
},
Spline = new PlotOptionsSpline
{
LineWidth = 3,
Marker = new PlotOptionsSplineMarker
{
Enabled = false,
States = new PlotOptionsSplineMarkerStates
{
Hover = new PlotOptionsSplineMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsSplineStates { Hover = new PlotOptionsSplineStatesHover { LineWidth = 3 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
},
Area = new PlotOptionsArea
{
//FillColor = new BackColorOrGradient(new Gradient
//{
// LinearGradient = new[] { 0, 0, 0, 300 },
// Stops = new object[,] { { 0, "rgb(116, 116, 116)" }, { 1, Color.Gold } }
//}),
LineWidth = 1,
Marker = new PlotOptionsAreaMarker
{
Enabled = false,
States = new PlotOptionsAreaMarkerStates
{
Hover = new PlotOptionsAreaMarkerStatesHover
{
Enabled = true,
Radius = 5
}
}
},
Shadow = false,
States = new PlotOptionsAreaStates { Hover = new PlotOptionsAreaStatesHover { LineWidth = 1 } },
PointInterval = PointInterval,
PointStart = new PointStart(PointStartDate)
}
})
.SetSeries(Series);
return chart;
}
Chart Data
public static Series GetTimeSeriesData(IQueryable<YourModel> model, ChartTypes ChartType)
{
List<Series> Series = new List<Series>();
var chartSeries = model.GroupBy(x => x.Name)
.Select(g => new
{
Name = g.Key,
Data = g.Select(x => x.Value).ToArray()
}).ToArray();
foreach (var item in chartSeries)
{
object[] data = item.Data.Cast<object>().ToArray();
Series localSeries = new Series { Name = item.Name, Data = new Data(data), Type = ChartType };
Series.Add(localSeries);
}
return Series;
}
Usage
IQueryable<YourModel> model;
ChartData chartData = new ChartData();
Highcharts chart = new HighChart("chart_time_series");
try
{
model = db.ClassInstanceDetails.AsQueryable();
chartData = GetTimeSeriesData(model, ChartTypes.Line);
chart = TimeSeriesZoomable(chartData.ToArray(), another_options);
}
catch (Exception e)
{
}
And full examples of charts : http://dotnethighcharts.codeplex.com/releases/view/85324
Are you using Highcharts.Net? If so, I'm not sure how much help I can be (I have only done it manually, creating my own objects and converting to JSON etc, which I found to give me complete control, albeit with more effort)
Anyway, this usually largely depends on how your X-axis needs to behave... it appears like it would just be discrete datetime values, (not automatically sequential), and so your object array probably needs to be composed of X- and Y-values, rather than just the Y-values, as it appears you have there.
This next part really depends on how your implementation works, so please excuse the pseudo-pseudo-code...
You either need a 2D array:
eg: data = [[1,1], [2,5], [3,4]...]
or something more concrete: I use a class which has an X and a Y property (amongst other things), but you could try anonymous types perhaps?
eg [{x=1, y=1}, {x=2, y=5}, {x=3, y=4}...] etc
Does this help at all?
Note: you will probably want to read up a bit on how to convert your datetime values for your x-axis - I had to calculate ticks from Epoch etc

C# and arrays of anonymous objects

What does such an expression mean?
obj.DataSource = new[]
{
new {Text = "Silverlight", Count = 10, Link="/Tags/Silverlight" },
new {Text = "IIS 7", Count = 11, Link="http://iis.net" },
new {Text = "IE 8", Count = 12, Link="/Tags/IE8" },
new {Text = "C#", Count = 13, Link="/Tags/C#" },
new {Text = "Azure", Count = 13, Link="?Tag=Azure" }
};
Especially these lines: new {Text = "IIS 7"... }
How can I create an array like this manually to suit this DataSource.
First, let's reformat that code a little:
obj.DataSource = new[]
{
new { Text = "Silverlight", Count = 10, Link = "/Tags/Silverlight" },
new { Text = "IIS 7", Count = 11, Link = "http://iis.net" },
new { Text = "IE 8", Count = 12, Link = "/Tags/IE8" },
new { Text = "C#", Count = 13, Link = "/Tags/C#" },
new { Text = "Azure", Count = 13, Link = "?Tag=Azure" }
};
This does not look like a multi-dimensional array, but rather like an array of 5 objects. These objects inside the array are of an anonymous type, created and initialized using new { ... }.
Concerning your question how you can manually create such an array to suit the data source: you seem to be doing exactly that with the above code.
That's not a multidimensional array. That's an array of objects which have been created using object initializers with anonymous types.
Looks like an array of anonymous types.
http://msdn.microsoft.com/en-us/library/bb397696.aspx
Just to add: Anonymous types are converted by the compiler to a real object. So the code will be changed to something equivalent of this (MUCH simplified, only to show that the compiler will create an actual class):
internal sealed class AnonymousClass1
{
internal string Text { get; set; }
internal int Count { get; set; }
internal string Link { get; set; }
}
And your code will then be changed to:
obj.DataSource = new AnonymousClass1[]
{
new AnonymousClass1 {Text = "Silverlight", Count = 10, Link="/Tags/Silverlight" },
new AnonymousClass1 {Text = "IIS 7", Count = 11, Link="http://iis.net" },
new AnonymousClass1 {Text = "IE 8", Count = 12, Link="/Tags/IE8" },
new AnonymousClass1 {Text = "C#", Count = 13, Link="/Tags/C#" },
new AnonymousClass1 {Text = "Azure", Count = 13, Link="?Tag=Azure" }
};
In one of my programs, I have code like this (simplified!):
var myObjects = new []{
new { Id = Guid.NewGuid(), Title = "Some Title", Description = string.Empty },
new { Id = Guid.NewGuid(), Title = "Another Title", Description = string.Empty },
new { Id = Guid.NewGuid(), Title = "Number 3", Description = "Better than No2, but not much" }
}
foreach(var myObject in myObjects) {
DoSomeThingWith(myObject.Title);
}
This works because it is just another class (I even get IntelliSense) behind the scenes. The benefit is obviously that I just saved myself from creating a class for this object. In my example, all objects need to look the same as well. (Obviously, doing this for any public members would be a bad idea as the compiler might change the name of the anonymous class if you add/remove some)
It's making a new object array, containing a group of anonymous objects.
new {Text = "Azure", Count = 13, Link="?Tag=Azure" }
is not creating a hash like similar syntax in say php would, but rather real a real object with the properties Test, Count, and Link set.
A good primer for anonymous objects can be found here
You should be able to use the same syntax to create new structures like this, the property values do not have to be constants:
string text = "Azure";
int count = 13;
string link = "?Tag=Azure";
new {Text = text, Count = count, Link=link }
To return such an array from a function I used:
public object GetAnonymousArray()
{
var tbl = new List<object>();
while (___)
{
//fill array in cycle
tbl.Add(new {Text = someString, Count = someInt, Link = link});
}
return tbl;
}

Categories