I have a model with two-dimensional array in it:
public class Matrix
{
public ValidInt[][] Data;
[Range(0, 8, ErrorMessage = "Введите ширину картины")]
public int Width { get; set; }
[Range(0, 8, ErrorMessage = "Введите ширину картины")]
public int Height { get; set; }
public Matrix(int w, int h)
{
Width = w;
Height = h;
Data = new ValidInt[w][];
for (int i = 0; i < w; i++)
this.Data[i] = new ValidInt[h];
}
public class ValidInt
{
[Range(0, 8, ErrorMessage = "Введите число, соответствующее цвету")]
public int Value { get; set; }
public ValidInt()
{
Value = 0;
}
}
}
Then I would like to have HTML.EditorFor to fill data in each block, so I write something like that:
<table>
#for (int column = 0; column < Model.Data.GetLength(1); column++)
{
<tr>
#for (int row = 0; row < Model.Data.GetLength(0); row++)
{
<td>#Html.EditorFor(x => Model.Data[column, row].Value); </td>
}
</tr>
}
</table>
But turns out you can't have EditorFor for two dimensional arrays. Any ideas on how to bypass that?
You cannot use two-dimensional array. However, you could use Jagged Array.
FYI: In order for ModelBinder to bind values to a model, it must have a parameterless constructor.
Model
public class Matrix
{
public int[][] Data { get; set; }
}
View
#using (Html.BeginForm())
{
<table>
#for (int column = 0; column < Model.Data.Length; column++)
{
<tr>
#for (int row = 0; row < Model.Data[column].Length; row++)
{
<td>#Html.EditorFor(x => Model.Data[column][row])</td>
}
</tr>
}
</table>
<button type="submit">Submit</button>
}
Controller
public IActionResult Index()
{
int w = 3, h = 2;
var matrix = new Matrix();
matrix.Data = new int[w][];
for (int i = 0; i < w; i++)
matrix.Data[i] = new int[h];
return View(matrix);
}
[HttpPost]
public IActionResult Index(Matrix matrix)
{
return View(matrix);
}
Result
Related
The problem I'm having is that I'm not sure how to translate my double array into a Dictionary. I have some LINQ Where statements that are pretty heavy, so I wanted to use the keys of my Dictionary to look up a specific value. I have two classes with getters and setters and then my calculator.
I've fiddled around a bit with trying to make either a lookup or a Dictionary, but didn't have any luck.
public class Product
{
public int EarliestOriginYear { get; set; }
public int NoOfDevelopmentYears { get; set; }
public string ProductName { get; set; }
public IEnumerable<ProductIncrementalValue> ProductIncrementalValues { get; set; }
}
public class ProductIncrementalValue
{
public string ProductName { get; set; }
public int OriginYear { get; set; }
public int DevelopmentYear { get; set; }
public double IncrementalValue { get; set; }
}
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
// This is what I want to change (where statements)
var incrementalValues = product.ProductIncrementalValues
.Where(v => v.OriginYear == product.EarliestOriginYear + i)
.ToList();
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
// This is what I want to change
double incrementalValue = incrementalValues.Where(val =>
val.DevelopmentYear == val.OriginYear + j)
.Select(x => x.IncrementalValue)
.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
You could group the products by OriginYear and DevelopmentYear before the loops. Something like this might help:
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.ToLookup(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
var incrementalValues = lookup[(originYear, developmentYear)];
double incrementalValue = incrementalValues.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
Thanks to the previous answer, I managed to implement a Dictionary based on OriginYear and DevelopmentYear, which returns an IncrementalValue.
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.
ToDictionary(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
double incrementalValue;
lookup.TryGetValue((originYear, developmentYear), out incrementalValue);
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
I'm assigning values to a class by using ObservableCollection.Class contains MainItems and it's SubItems. Now how can I read all SubItems for each input of MainItem?
public class MainItems
{
public string ItemName { get; set; }
public ObservableCollection<SubItems> SubItemsList { get; set; }
}
public class SubItems
{
public string SubItemName { get; set; }
}
ObservableCollection<MainItems> _data = new ObservableCollection<MainItems>();
for (int i = 1; i <= 5; i++)
{
MainItems _mainItems = new MainItems();
_mainItems.ItemName = "Main" + i.ToString();
_mainItems.SubItemsList = new ObservableCollection<SubItems>();
for (int j = 1; j <= 3; j++)
{
SubItems _subItems = new SubItems()
{
SubItemName = "SubItem" + i.ToString()
};
_mainItems.SubItemsList.Add(_subItems);
}
_data.Add(_mainItems);
}
The foreach loop alway honors the collections(List, Array, Dictionary(special), ...) boundaries and iterates over all Elements, so its the shortest way to achieve what you want. It disallows you to add/remove elements from the currently iterated collection. In this case the classic for loop is your friend.
Full description from Microsoft:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in
Based on Fangs comment:
// foreach Version
foreach (MainItems mainItem in _data)
{
foreach (SubItems subItems in mainItem.SubItemsList)
{
Console.WriteLine($"{mainItem.ItemName} has a child {subItems.SubItemName}!");
}
}
// for Version
for (int i = 0; i < _data.Count; i++)
{
MainItems mainItem = _data[i];
for (int k = 0; k < mainItem.SubItemsList.Count; k++)
{
SubItems subItem = mainItem.SubItemsList[k];
Console.WriteLine($"{mainItem.ItemName} has a child {subItem.SubItemName}!");
}
}
// For Enumerator version
// get the input main item
string input = "Main1";
IEnumerable<ObservableCollection<SubItems>> ItemsforSelectedMainIem = _data.Where(x => x.ItemName == input).Select(x => x.SubItemsList);
var e = ItemsforSelectedMainIem.GetEnumerator();
while (e.MoveNext())
{
var v = e.Current.Select(x=>x.SubItemName).ToList();
foreach (var item in v)
{
Console.WriteLine(item);
}
}
In my Unit Test I need to change the value of the object that was mocked before. For example:
public class Cell
{
public int X { get; set; }
public int Y { get; set; }
public string Value { get; set; }
}
public class Table
{
private Cell[,] Cells { get; }
public Table(Cell[,] cells)
{
Cells = cells;
}
public void SetCell(int x, int y, string value)
{
Cells[x, y].Value = value;
}
}
I want to test SetCell method in Table.
So, first I mock Cell, then I create a Cell[,] array of cells, create a Table passing array of cells as a parameter.
SetCell doesn't work, because (I think) I can't change the object that was mocked before. How can I change it?
Here's my test:
ICell[,] cells = new ICell[3, 4];
for (int i = 0; i < cells.GetLength(0); i++)
{
for (int j = 0; j < cells.GetLength(1); j++)
{
var mock = new Mock<ICell>();
mock.Setup(m => m.X).Returns(i);
mock.Setup(m => m.Y).Returns(j);
mock.Setup(m => m.Value).Returns("");
cells[i, j] = mock.Object;
}
}
ITable table = new Table(cells);
table.SetCell(0, 0, "TEST"); // Cannot change it here :/
Setup all the properties so that they can be updated
ICell[,] cells = new ICell[3, 4];
for (int i = 0; i < cells.GetLength(0); i++)
{
for (int j = 0; j < cells.GetLength(1); j++)
{
var mock = new Mock<ICell>();
mock.SetupAllProperties();
mock.Object.X = i;
mock.Object.Y = j;
mock.Object.Value = "";
cells[i, j] = mock.Object;
}
}
//...other code removed for brevity
How can I convert a string to be used as a variable? I have a List of strings that I want to be able to loop through and call data from my Model.
My code:
Controller:
List<string> reportContentsCharts = new List<string>();
//Pseudo Code
//If chart is selected added it to reportContents. So in the view instead of calling #Model.getChart1 from the view I can reference this reportContents.
//For Example:If chart1 and chart2 are selected
reportContentsCharts.Add("getChart1");
reportContentsCharts.Add("getChart2");
IndexViewModel viewModel = new IndexViewModel()
{
chart1 = makeChart1(DictionaryofData), //Returns an image and sends to IndexViewModel
chart2 = makeChart2(DictionaryofData),
chart3 = makeChart2(DictionaryofData),
chart4 = makeChart2(DictionaryofData),
chart5 = makeChart2(DictionaryofData),
chart6 = makeChart2(DictionaryofData),
reportContentsCharts = reportContentsCharts
}
private byte[] makeChart1(Dictionary<string, Double> DictionaryofData)
{
//code to construct chart and return as an image.
}
IndexViewModel:
public Byte[] chart1 { get; set; }
public Byte[] chart2 { get; set; }
public Byte[] chart3 { get; set; }
public Byte[] chart4 { get; set; }
public Byte[] chart5 { get; set; }
public Byte[] chart6 { get; set; }
//This code is repeated for all 6 charts
public string getChart1
{
get
{
string mimeType = "image/png";
string base64 = Convert.ToBase64String(chart1);
return string.Format("data: {0}; base64, {1}", mimeType, base64);
}
}
View:
<table>
for(int z = 0; z< Model.reportContentsCharts.Count / 2 ;z++) //At most 2 charts can be selected
{
<tr>
<td ="center">
<img src=#Model.reportContentsCharts[z]/>
</td>
<td ="center">
<img src=#Model.reportContentsCharts[z+1] />
</td>
</tr>
}
</table>
Under lying issue:
Currently when I run this code it returns me a broken image. I am thinking this might be a syntax issue? I have a handful of graphs that can be displayed on my webpage. Based on input from the user only a select few of the graphs will be displayed. The first thing I did was hard coded a position in the html for each graph and then use if() statements to determine whether to display the graph. The problem with this is that, based on the user input, the selected graphs can appear on separate lines. This creates bad alignment and spacing issues.
I understand that this might not be the best way to do this, but I felt like that it was the simplest solution.
Thanks for any suggestions or help.
It looks to me like the root of the problem is your poorly designed ViewModel. You need to normalize it:
private Dictionary<string, byte[]> Charts = new Dictionary<string, byte[]>();
public string GetChart(string name)
{
get
{
string mimeType = "image/png";
string base64 = Convert.ToBase64String(Charts[name]);
return string.Format("data: {0}; base64, {1}", mimeType, base64);
}
}
public string AddChart(string name, byte[] data)
{
Charts[name] = data;
}
Then you can write your controller something like this:
IndexViewModel viewModel = new IndexViewModel()
{
reportContentsCharts = reportContentsCharts
}
for (int i = 0; i < 6; i++)
{
viewModel.AddChart("chart" + i, makeChart("chart" + i, DictionaryOfData));
}
And finally, you can write your view like this:
<table>
for (int z = 0; z < Model.reportContentsCharts.Count; z += 2)
{
<tr>
for (int c = z; c < z + 2; c++)
{
<td align="center">
if (c < Model.reportContentsCharts.Count)
{
<img src="#Model.GetChart(Model.reportContentsCharts[c])"/>
}
</td>
}
</tr>
}
</table>
I have my ViewModel that look like this:
public class ParentViewModel
{
public IList<ChildViewModel> Children { get; set; }
public IList<AttributeViewModel> Attributes { get; set; }
}
public class ChildViewModel
{
public IDictionary<int, bool> Attributes { get; set; }
}
public class AttributeViewModel
{
public int AttributeId { get; set; }
public string Description { get; set; }
}
And the corresponding view looks like this:
#for (var i = 0; i < Model.Children.Count; i++)
{
for (var j = 0; j < Model.Attributes.Count; j++)
{
var attribute = Model.Attributes[j];
#Html.DisplayFor(model => model.Attributes[j].Description)
#Html.CheckBoxFor(model => model.Children[i].Attributes[attribute.AttributeId])
}
}
When I try to do a post back of this view against my controller action, I get an InvalidCastException. Is what I'm doing possible in MVC?
For reference, here's my corresponding Controller:
public class TestController : Controller
{
public ActionResult Index()
{
return View("Index", new ParentViewModel
{
Attributes =
new List<AttributeViewModel>
{
new AttributeViewModel { AttributeId = 1, Description = "green" },
new AttributeViewModel { AttributeId = 2, Description = "spicy" }
},
Children = new[] { new ChildViewModel() }
});
}
public ActionResult PostBack(ParentViewModel model)
{
// Does not work. Fails immediately
return RedirectToAction("Index");
}
}
And my View:
#model ParentViewModel
#{
Layout = null;
}
#using (Html.BeginForm("PostBack", "Test", FormMethod.Post))
{
for (var i = 0; i < Model.Children.Count; i++)
{
for (var j = 0; j < Model.Attributes.Count; j++)
{
var attribute = Model.Attributes[j];
#Html.DisplayFor(model => model.Attributes[j].Description)
#Html.CheckBoxFor(model => model.Children[i].Attributes[attribute.AttributeId])
}
}
<input type="submit"/>
}
If these were regular classes not a dictionary, I know the For loop and Razor syntax would look differently. Maybe if you try modeling your loops and stuff from this syntax it will work.
for (int i = 0; i < Model.listModel.Count; i++)
{
#Html.TextBoxFor(modelItem => Model.listModel[i].LastName)
for (int m = 0; m < Model.listModel[i].anotherListObj.Count; m++)
{
#Html.TextBoxFor(modelItem => Model.listModel[i].anotherListObj[m].FirstName)
}
}
Edit:
#Html.CheckBoxFor(model => model.Children[i].Attributes[attribute.AttributeId])
Above it looks like it's trying to convert a List of Attributes to a Dictionary of attributes
Also my loop Syntax works over a View that expects 'SomeModel' and the definitions are like this
SomeModel
{
List<ListClass> listModel {get;set;}
}
ListCLass
{
List<yetAnotherList> anotherListObj {get; set};
}