Data Grid View not showing data from related models? - c#

I have two related classes:
public class Dealer {
public int Id { get; set; }
public string BaseUrl { get; set; }
public virtual ICollection<DealerAddress> DealerAddress { get; set; }
}
public class DealerAddress {
public int Id { get; set; }
public int DealerId { get; set; }
public string Email { get; set; }
public virtual Dealer Dealer { get; set; }
}
And in my form I want to display data from DealerAddress class:
public SortableBindingList<DealerAddress> Addresses = new SortableBindingList<DealerAddress>();
private void CreateDataGridView() {
dataGridViewPlaceHolderPanel.Visible = false;
dataGridView = new DataGridView();
dataGridView.Name = "dataGridView";
List<string> regularColumns = new List<string>()
{
nameof(DealerAddress.Id),
nameof(DealerAddress.DealerId),
nameof(DealerAddress.Dealer.BaseUrl),
nameof(DealerAddress.Email),
};
var columns = new List<DataGridViewTextBoxColumn>();
foreach (var regularColumnName in regularColumns)
{
var col = new DataGridViewTextBoxColumn
{
HeaderText = regularColumnName,
DataPropertyName = regularColumnName,
Name = "column" + regularColumnName
};
columns.Add(col);
}
}
public void SetAddresses(SortableBindingList<DealerAddress> addresses, int totalCount)
{
try
{
dataGridView.RowStateChanged -= DataGridView_RowStateChanged;
Addresses = addresses;
RefreshDataGridView();
}
finally
{
dataGridView.RowStateChanged += DataGridView_RowStateChanged;
}
}
private void RefreshDataGridView(){
if (Addresses == null || Addresses.Count == 0)
return;
dataGridView.DataSource = Addresses;
}
And the data displayed in my table is:
When I hit "SetAddresses", the data is populated from DealerAddress model, but it doesn't display column values from "DealerAddress.Dealer".

TL;DR
Quick fix it with the following. In your DealerAddres, add
public string BaseUrl
{
get => Dealer.BaseUrl;
set => Dealer.BaseUrl = value;
}
Explaining
You have two class DealerAddress and Dealer. You set a list of DealerAddress to the DataSource.
So when the DataGridView starts to render, it will search the properties in the first class.
When you do nameof(DealerAddress.Dealer.BaseUrl) you are actually telling, to the DataGridView, that the class DealerAddress contains that property --- which it does not.
See this for more information.

Related

Allowing a user to select column headers to import

I'm using LINQtoCSV within a program that allows the user to import an order from a CSV file. I have all the code working however, if the CSV file doesn't have the exact column headers then it doesn't work.
Below is my class that LINQtoCSV reads into -
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
If the CSV file doesn't have the exact headers it won't work. The data I actually only need is the first 4 strings.
Below is my function that actually reads in the data.
private void csvParse()
{
// order.Clear();
string fileName = txt_filePath.Text.ToString().Trim();
try
{
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
MessageBox.Show($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
orderReturn.Add(d);
}
this.DialogResult = DialogResult.Yes;
this.Close();
}
catch (Exception ex)
{
if (ex.ToString().Contains("being used by another process"))
{
MessageBox.Show("Error: Please close the file in Excel and try again");
}
else
{
MessageBox.Show(ex.ToString());
}
}
}
I want the user to be able to just pass in a file and then select the relevant columns which relate to the corresponding values and then read in the data ignoring any columns that haven't been selected.
Hope this all makes sense, is something like this possible within LINQtoCSV
You have to add IgnoreUnknownColumns = true to your CsvFileDescription
CSV:
product,price,someColumn,orderQty,value,otherColumn
my product,$123,xx,2,$246,aa
my other product,$10,yy,3,$30,bb
Working code (I modified your code a little bit, to run it in a console)
using System;
using System.Collections.Generic;
using LINQtoCSV;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
csvParse();
Console.ReadLine();
}
private static void csvParse()
{
string fileName = "../../../test.csv"; // provide a valid path to the file
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true,
IgnoreUnknownColumns = true // add this line
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
Console.WriteLine($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
}
}
}
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
}
Output:
Product:my product,Quantity:"2",Price:"$123"
Product:my other product,Quantity:"3",Price:"$10"
If your properties have different names than CSV columns, you should use CsvColumn attribute:
public class OrderProduct
{
[CsvColumn(Name = "product")]
public string Product { get; set; }
[CsvColumn(Name = "price")]
public string Price { get; set; }
[CsvColumn(Name = "orderQty")]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
Or if you prefer mapping columns by their indices:
public class OrderProduct
{
[CsvColumn(FieldIndex = 0)]
public string Product { get; set; }
[CsvColumn(FieldIndex = 1)]
public string Price { get; set; }
[CsvColumn(FieldIndex = 2)]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
If you have to specify the columns on the fly, the only way seems to be to read raw data and process it yourself (the solution is based on this article):
internal class DataRow : List<DataRowItem>, IDataRow
{
}
...
int productColumnIndex = 0; // your users will provide it
var fromCSV = cc.Read<DataRow>(fileName);
foreach (var row in fromCSV)
{
var orderProduct = new OrderProduct
{
Product = row[productColumnIndex].Value,
};
Console.WriteLine(orderProduct.Product);
}

Populating buttons with name based on login type in Xamarin.forms

I have models as follows:
public class UserDetails
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool Truck { get; set; }
public bool Car { get; set; }
public bool Bus { get; set; }
}
public class TransportDetails
{
public int Id { get; set; }
public string Name { get; set; }
public string Url{ get; set; }
}
I also defined a list that contains user details as follows:
public List<UserDetails> GetAllUserDetails()
{
List<UserDetails> userDetails = new List<UserDetails>
{
new UserDetails
{
Id = 1,
Username = "admin",
Password = "123",
Truck = false,
Car = true,
Bus = true
},
new UserDetails
{
Id = 2,
Username = "superadmin",
Password = "123",
Truck = true,
Car = false,
Bus = true
}
};
return userDetails;
}
The app works as follows :
The user logins with a credentials.
Based on his credentials, it is determined which property he has access to, either truck, car or bus. And he is directed to the MainPage.
When redirected to the MainPage, buttons should appear with the properties' values that the user has access to.
In other words, the buttons should have name as defined in the Model TransportDetails
So far, this has been done:
public void CheckLogin()
{
UserData userData = new UserData();
allUsers = new List<UserDetails>();
allUsers = userData.GetAllUserDetails();
if (allUsers.Any(x => x.Username.Equals(Username) && x.Password.Equals(Password)))
{
var user = allUsers.Where(x => x.Username.Equals(Username) && x.Password.Equals(Password)).First();
Application.Current.MainPage.Navigation.PushAsync(new PortalPage(user));
}
else
{
Application.Current.MainPage.DisplayAlert("Error", "Invalid credentials", "OK");
}
}
I want to get buttons to appeared dynamically based on what property is true in the model UserDetails once a user has logged in. Then, when the buttons appeared based on what is true for this user, I want the button name and button value (url) from the TransportDetails model.
Could someone advise me on how to achieve this please ?
StackLayout stack = new StackLayout();
if (user.Truck) {
Button truck = new Button();
var transport = transports.Where(t => t.Name == "truck").First();
truck.Text = transport.Name;
truck.Clicked += (sender, e) => { Device.OpenUrl(transport.Url); };
stack.Children.Add(truck);
}
// repeat for other types
if you modify your UserDetails class
public class UserDetails
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<TransportDetail> Transport { get; set; }
}
then you could just do this
StackLayout stack = new StackLayout();
foreach (var t in user.Transport) {
Button btn = new Button();
btn.Text = t.Name;
btn.Clicked += (sender, e) => { Device.OpenUrl(t.Url); };
stack.Children.Add(btn);
}
I think you should modify your Model.
For every User you should have a ObservableCollection<TransportDetail> transportDetail
public class UserDetails
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
ObservableCollection<TransportDetails> TransportDetails{get;set;}
}
then you should add to TransportDetails only transport enabled to the user.
At this point, I suggest to use a ListView. On every ViewCell you should have a Button. Bind your properties to this Button. Fill the ViewCell with trasportDetail List

How do I create and insert one-to-many object with entity framework c#

I'm trying to create an object and insert to the database but keep getting the same error no matter what I try.
The row that I get the error on is ColumnGroupTest.ValidValues.Add(memberComment1); the error is
error message
NullReferenceException was unhandled by user code
my models
public class StoreColumnName
{
public int Id { get; set; }
public string StoreColumnGroupName { get; set; }
public string ColumnName { get; set; }
public string ColumnType { get; set; }
public List<StoreValidValue> ValidValues { get; set; }
}
public class StoreValidValue
{
public int Id { get; set; }
public string ValidValue { get; set; }
public StoreColumnName StoreColumnName { get; set; }
}
my controller
public ActionResult Index()
{
XDocument document = XDocument.Load(#"C:\Users\Physical.xml");
var result = document.Descendants("ColumnGroup");
foreach(var item in result){
var ColumnGroupName = item.Attribute("name").Value;
var Columns = item.Descendants("Column");
foreach (var itemColumn in Columns)
{
StoreColumnName ColumnGroup = new StoreColumnName();
var ColumnGroupTest = new StoreColumnName
{
StoreColumnGroupName = ColumnGroupName,
ColumnName = itemColumn.Attribute("name").Value,
ColumnType = itemColumn.Attribute("type").Value,
Id = 11
};
var ValidValues = itemColumn.Descendants("ValidValues");
var Values = ValidValues.Descendants("Value");
foreach (var Value in Values)
{
var memberComment1 = new StoreValidValue
{
StoreColumnName = ColumnGroupTest,
ValidValue = Value.Value,
Id = 101
};
ColumnGroupTest.ValidValues.Add(memberComment1);
}
}
}
return View();
}
(I gladly take tips on what I can improve when asking for help/guiding here).
Can anyone help ?
The issue that you're having is that you don't initialize your ValidValues property to a list. By default, those types of properties initialize to null unless you specify differently.
The best approach is to add that initialization to your constructor of that object.
public StoreColumnName() {
this.ValidValues = new List<StoreValidValue>();
}

Set Width when populating automatically a ListView from object in Windows Forms

I have a entity-Dto, for example:
public int Id { get; set; }
public byte[] Datos { get; set; }
public System.DateTime Fecha { get; set; }
public DateTime? FechaEmpaquetado { get; set; }
public Guid? ProjectGuid { get; set; }
public String ProjectName { get; set; }
public bool EsAutomatico { get; set; }
public short Timeout { get; set; }
I want populate ListView automatically for show details data of a single entity.
I DON'T want use AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent)
In GridView for example, has AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill.
var myProperties = GetMyAllProperties(typeof(MyDto));
myProperties.Foreach( prop => {
var witdh = prop.Value.ToString().Length; // ===> not working find , a char like 'c' which width value has ?
lvData.Columns.Add(prop.Name, witdh);
}
var firstProp = false;
ListViewItem item1 = null;
myProperties.Foreach( prop => {
if (!firstProp) item1 = new ListViewItem(prop.Name);
if (!firstProp) item1.SubItems.Add(prop.Name);
}
lvData.Items.Add(item);
How can I set Width property for ListView Ítem according Value Property ?
According #sinatr and #plutonix, I think now better is avoid Measuring width using AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent). More easy.
Configure(lvData);
lvData.Clear();
var myProperties = GetMyAllProperties(typeof(MyDto));
myProperties.Foreach( prop => {
lvData.Columns.Add(prop.Name);
}
var firstProp = false;
ListViewItem item1 = null;
myProperties.Foreach( prop => {
if (!firstProp) item1 = new ListViewItem(prop.Name);
if (!firstProp) item1.SubItems.Add(prop.Name);
}
lvData.Items.Add(item);
lvData.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);

How to display property from FK obj but edit the ID when editting the datafield?

In my Grid viewmodel I have an whole obj that has two foreign keys in it and I need to have them display a property they have ( a string obj) as the text and the value that will be editted would be the foreign key id. Right now all I have is it displaying parts of the obj in the grid on my webpage but where the FK fields are is just displays a path not the data. I am using MVC with EF
public class Bug
{
public int BugId { get; set; }
[ForeignKey("BugType")]
public int BugTypeId { get; set; }
public virtual BugType BugType { get; set; }
[ForeignKey("BugStatus")]
public int BugStatusId { get; set; }
public virtual BugStatus BugStatus { get; set; }
public string Description { get; set; }
public DateTime DateReported { get; set; }
}
public class BugType
{
public int BugTypeId { get; set; }
public string BugTypeName { get; set; }
public string Description { get; set; }
}
public class BugStatus
{
public int BugStatusId { get; set; }
public string BugStatusName { get; set; }
}
public class BugJqGridViewModel
{
public JQGrid Grid { get; set; }
public BugJqGridViewModel()
{
Grid = new JQGrid
{
Columns = new List<JQGridColumn>()
{
new JQGridColumn{ DataField="BugId",
PrimaryKey = true,
Editable = false,
Width = 100 },
new JQGridColumn{ DataField="BugType",
Editable = true,
Width = 175, },
new JQGridColumn{ DataField="BugStatus",
Editable = true,
Width = 175, },
new JQGridColumn{ DataField="DateReported",
Editable = false,
Width = 175,
DataFormatString = "{0:yyyy/MM/dd}" },
new JQGridColumn{ DataField="Description",
Editable = false,
Width = 800 }
},
Width = Unit.Pixel(1425),
Height = Unit.Percentage(100)
};
Grid.ToolBarSettings.ShowRefreshButton = true;
}
}
SetupEditdropdownlist method
I was trying to recreate this method to suit my needs but I ran into a problem with the FK id cant be converted into a string at this point. I am not sure if I am going the right direction with this or not because of the FK
private void FunctionalityContextMenu_SetUpBugIDEditDropDown(JQGrid grid)
{
// setup the grid search criteria for the columns
JQGridColumn bugTypeColumn = grid.Columns.Find(c => c.DataField == "BugType");
bugTypeColumn.Editable = true;
bugTypeColumn.EditType = EditType.DropDown;
JQGridColumn bugStatusColumn = grid.Columns.Find(c => c.DataField == "BugStatus");
bugStatusColumn.Editable = true;
bugStatusColumn.EditType = EditType.DropDown;
// Populate the search dropdown only on initial request, in order to optimize performance
if (grid.AjaxCallBackMode == AjaxCallBackMode.RequestData)
{
var db = new GameDevCupidContext();
var editList = from bug in db.Bugs
select new SelectListItem
{
Text = bug.BugType.BugTypeName,
Value = bug.BugTypeId // accepts a only a string
};
bugTypeColumn.EditList = editList.ToList();
var editList1 = from bug in db.Bugs
select new SelectListItem
{
Text = bug.BugStatus.BugStatusName,
Value = bug.BugStatusId // Accepts only a string
};
bugStatusColumn.EditList = editList1.ToList();
}
}
I found a situation to my own problem. Apparently, you have to join the tables together then bind them to the grid.
public JsonResult EmployeeJqGridDataRequested()
{
var bugGridModel = new BugJqGridViewModel();
var db = new bugContext();
var bugs = from b in db.Bugs
join bt in db.BugTypes on b.BugTypeId equals bt.BugTypeId
join bs in db.BugStati on b.BugStatusId equals bs.BugStatusId
select new { b.BugId, bt.BugTypeName, bs.BugStatusName,b.DateReported,b.Description };
return bugGridModel.Grid.DataBind(bugs);
}

Categories