|
@typeparam TValue
<SfGrid TValue="TValue" DataSource="@Lists" AllowPaging="true">
<GridPageSettings PageSize="5"></GridPageSettings>
<GridColumns>
@foreach (var prop in typeof(TValue).GetProperties())
{
<GridColumn Field="@prop.Name" IsPrimaryKey="@(prop.Name == "OrderID")" AutoFit="true" AllowEditing="@prop.CanWrite"></GridColumn>
}
</GridColumns>
</SfGrid>
@code{
[Parameter]
public List<TValue> Lists { get; set; }
}
[Index.Razor]
|
The answer is great and I got it working, but how do I get the RowSelected from the @typeparam SearchClass?
You can see in the code at the bottom where I cannot get the id
@typeparam SearchClass
<SfGrid DataSource="@Data" TValue="SearchClass" AllowPaging="true" AllowFiltering="true" AllowSorting="true">
<GridEvents RowSelected="@GetSelectedRecord" TValue="SearchClass"></GridEvents>
<GridPageSettings PageCount="5"></GridPageSettings>
<GridColumns>
@foreach (var prop in typeof(SearchClass).GetProperties())
{
if (prop.Name == "id")
{
<GridColumn Field="@prop.Name" IsPrimaryKey="true" ></GridColumn>
}
else
{
<GridColumn Field="@prop.Name" ></GridColumn>
}
}
</GridColumns>
</SfGrid>
@code{
[Parameter] public List<SearchClass> Data { get; set; }
private void GetSelectedRecord(RowSelectEventArgs<SearchClass> args)
{
// here I need the ID of the row clicked
// If I put args.Data.id it doesnt know what .id is
}
}
Page.razor
<HTMLSearchResults DataSourceTable="SearchResults_DataSourceTable" DataSourceList="SearchResults_DataSourceList" />
Page.razor.cs
[Parameter] public DataTable SearchResults_DataSourceTable { get; set; }
[Parameter] public List<System.Dynamic.ExpandoObject> SearchResults_DataSourceList { get; set; }
public void SetupControlForSearchResults()
{
DataTable dtSearchResults = MyClass.GetAll();
List<System.Dynamic.ExpandoObject> lstObj = new List<System.Dynamic.ExpandoObject>();
foreach (DataRow row in dtSearchResults.Rows) {
System.Dynamic.ExpandoObject e = new System.Dynamic.ExpandoObject();
foreach (DataColumn col in dtSearchResults.Columns)
{
e.TryAdd(col.ColumnName, row.ItemArray[col.Ordinal]);
}
lstObj.Add(e);
}
SearchResults_DataSourceTable = dtSearchResults;
SearchResults_DataSourceList = lstObj;
}
HTMLSearchResults.razor
@using System.Data
@using Syncfusion.Blazor.Grids
@inject NavigationManager NavManager
@try
{
<SfGrid DataSource="@DataSourceList" TValue="System.Dynamic.ExpandoObject" AllowPaging="true" AllowFiltering="true" AllowSorting="true">
<GridEvents RowSelected="@GetSelectedRecord" TValue="System.Dynamic.ExpandoObject" ></GridEvents>
<GridPageSettings PageCount="5"></GridPageSettings>
<GridColumns>
@foreach (DataColumn col in DataSourceTable.Columns)
{
<GridColumn [email protected] />
}
</GridColumns>
</SfGrid>
}
catch (Exception ex)
{
throw ex;
}
@code {
[Parameter] public DataTable DataSourceTable { get; set; }
[Parameter] public List<System.Dynamic.ExpandoObject> DataSourceList { get; set; }
public void GetSelectedRecord(RowSelectEventArgs<System.Dynamic.ExpandoObject> args)
{
// This is where I cannot get the ID for the RowSelected
}
}
I am sorry, I am having trouble pasting the code into this comment box, it keeps losing the code so I will add this extra reply to explain.
The code I just posted is a HTMLSearchResults.razor component which I want to reuse for every page in my website. I want to dynamically load the DataTable from the parent Page.razor and send in the
DataTable DataSourceTable
List<System.Dynamic.ExpandoObject> DataSourceList
Everything works ok to build the grid dynamically with headers and data but the issue I am having is because I am using System.Dynamic.ExpandoObject for the RowSelected, it will never find the id of the row clicked.
As the code above states I have:
<GridEvents RowSelected="@GetSelectedRecord" TValue="System.Dynamic.ExpandoObject" ></GridEvents>
public void GetSelectedRecord(RowSelectEventArgs<System.Dynamic.ExpandoObject> args){
// here is where I cannot find the id of the row clicked
}
|
@try
{
<SfGrid DataSource="@DataSourceList" TValue="System.Dynamic.ExpandoObject" AllowPaging="true" AllowFiltering="true" AllowSorting="true">
<GridEvents RowSelected="@GetSelectedRecord" TValue="System.Dynamic.ExpandoObject"></GridEvents>
<GridPageSettings PageCount="5"></GridPageSettings>
<GridColumns>
@foreach (DataColumn col in DataSourceTable.Columns)
{
}
</GridColumns>
</SfGrid>
}
catch (Exception ex)
{
throw ex;
}
@code {
[Parameter] public DataTable DataSourceTable { get; set; }
[Parameter] public List<System.Dynamic.ExpandoObject> DataSourceList { get; set; }
public void GetSelectedRecord(RowSelectEventArgs<System.Dynamic.ExpandoObject> args)
{
var data = args.Data as dynamic;
Console.WriteLine(data.CustomerID);
}
}
|
You are a star, thank you very much.
Support from syncfusion is amazing, not only do you answer the question, you also give a working example... very much appreciated.
Thank you for your time in replying
Regards
James
Hi Vignesh,
This example is already helpful and I got it to work.
However, my problem has one complexity more as I don't need a "template" grid working for multiple tables, but the columns of the table being dynamic itself.
My data object looks like this:
public class Effort
{
public int Id { get; set; } //just for db index
public int Task { get; set; } //id of items in task table
public int Role { get; set; } //id of items in roles table
public string tName { get; set; } //name of tasks in task table (show as row title)
public string rName { get; set; } //name of roles in role table (show as column title)
public double eHrs { get; set; } //the value for every task/role table cell
}
....
<DynTable TValue="Effort" Lists="efforts"></DynTable>
....
List<Effort> efforts = new List<Effort>();
Currently the table shows one row for each tasks/role combination instead of the matrix between tasks and roles.
Would that also be possible? I know that needs a second generic loop for the columns, but am not sure whether the mapping to the effort values would be correct?
Thanks a lot,
Klaus.
Hi Rahul,
Thanks for your reply.
Yes, I do have the column headers in one table and row headers in another table. I have no issue with showing those headers, but how to dynamically create the table structure.
I already succeeded now to use ExpandoObject to create the structure, but am stuck now at update features due to th e read/only definition of the dictionary structure.
Would it help to convert the ExpandoObject to a list first or use a json string instead as table data?
Thanks
Klaus.
Hi,
thanks for your reply.
I think most of your questions are answered when looking at the code of the razor page:
@if (dynList == null)
{
<p><em>Loading...</em></p>
}
else
{
<SfGrid TValue="ExpandoObject" DataSource="@dynList" AllowPaging="false" Toolbar="@ToolbarItems" AllowSorting="true" EnableAltRow="true" AllowGrouping="false" AllowTextWrap="true" Height="500">
<GridEvents OnActionComplete="EffortsActionHandler" OnActionFailure="ActionFailureHandler" TValue="ExpandoObject"></GridEvents>
<GridEditSettings AllowAdding="false" AllowDeleting="false" AllowEditing="true" AllowEditOnDblClick="true" ></GridEditSettings>
<GridColumns>
@foreach (var prop in cols)
{
@if (prop.Name == "Task")
{
<GridColumn Field="@prop.Name" Width="250" IsPrimaryKey="true" AllowEditing="@prop.CanWrite" TextAlign="TextAlign.Left"></GridColumn>
}
else
{
<GridColumn Field="@prop.Name" Width="120" Format="N2" AllowEditing="@prop.CanWrite" TextAlign="TextAlign.Right">
<EditTemplate>
<SfNumericTextBox TValue="Object" @bind-Value="@((context as Object).FirstOrDefault(x => x.Key == prop.Name).Value)" Min=0 ShowSpinButton=false></SfNumericTextBox>
</EditTemplate>
</GridColumn>
}
}
</GridColumns>
</SfGrid>
<p></p>
}
@code {
List dynList = null;
dynamic propEff = new ExpandoObject();
List<PTask> tasks;
List<Role> roles;
List<colProp> cols = new List<colProp>();
private List<string> ToolbarItems = new List<string>() { "Edit", "Update", "Cancel" };
public class colProp
{
public string Name { get; set; }
public bool CanWrite { get; set; }
}
protected override async Task OnInitializedAsync()
{
tasks = await db.PTask.ToListAsync();
roles = await db.Roles.ToListAsync();
int ix = 1; int iy = 1;
string tName = ""; string rName = "";
//### Create dynamic template for table headers
dynamic propEff = new ExpandoObject();
((IDictionary<String, Object>)propEff).Add("tID", 0);
((IDictionary<String, Object>)propEff).Add("Task", "");
cols.Add(new colProp { Name = "Task", CanWrite = false });
foreach (Role pRole in roles)
{
((IDictionary<String, Object>)propEff).Add(pRole.NAME, 0);
cols.Add(new colProp { Name = pRole.NAME, CanWrite = true });
}
//### Get DB values into table object
dynList = db.GetDynEfforts().Result.ToList();
//### Create dummy entries if db is empty
if (dynList == null)
{
//### Create new dynamic object
dynList = new List<ExpandoObject>();
foreach (PTask pTask in tasks)
{
ix = 1;
tName = tasks.Where(t => t.Id == iy).FirstOrDefault().NAME;
dynamic dynEff = new ExpandoObject();
((IDictionary<String, Object>)dynEff).Add("tID", iy);
((IDictionary<String, Object>)dynEff).Add("Task", tName);
foreach (Role pRole in roles)
{
rName = roles.Where(r => r.Id == ix).FirstOrDefault().NAME;
((IDictionary<String, object>)dynEff).Add(pRole.NAME, 0);
ix++;
}
dynList.Add(dynEff);
await db.SetDynEfforts(dynEff);
dynEff = null;
iy++;
}
}
}
public async void EffortsActionHandler(ActionEventArgs<ExpandoObject> args)
{
if (args.RequestType != Syncfusion.Blazor.Grids.Action.BeginEdit)
{
if (!db.SetDynEfforts(args.Data).Result) throw new InvalidOperationException("Error during DB save operation");
}
}
The input and output db data looks like this:
Id PTask Role eHrs
1 1 1 0
2 1 2 3
3 1 3 0
4 1 4 4
5 1 5 0
6 1 6 0
The db Interface is realised through the db.GetDynEfforts and db.SetDynEfforts and works correctly.
Problematic is especially the "@bind-Value" param in the EditTemplate for the numeric editor (which is necessary as it needs to edit decimal values, which I understand is not possible with the regular number format cell).
Thanks
Klaus.
Dear SyncFusion Team,
I found now an (already existing) suitable description in SF documentation regarding using binding to a ExpandoObject (Incl. this video: https://www.youtube.com/watch?v=Xhaw3DdHmJk&t=448s)
That actually helped a lot and I got the hole solution working now with the following components:
1) a dynamic object with respective methods to use for read & update:
public class EffortItem : DynamicObject
{
public DictionaryEffortDict = new Dictionary ();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
return EffortDict.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
EffortDict[binder.Name] = value;
return true;
}
public override IEnumerableGetDynamicMemberNames()
{
return this.EffortDict?.Keys;
}
public void Add(string Key, object Value)
{
EffortDict.Add(Key, Value);
}
public object Get(string Key)
{
var ret = (EffortDict.TryGetValue(Key, out object result)) ? result : null;
return ret;
}
}
2) Database interface methods to load and store the dynamic object data from/into database:
//##################################
//### Converter for Efforts data
public async Task> GetDynEfforts()
{
ListdynList = new List ();
try
{
//### Get DB data
var efforts = await this.Efforts.ToListAsync();
var tasks = await this.PTask.ToListAsync();
var roles = await this.Roles.ToListAsync();
int ix = 1; double myEff = 0.0;
string tName = "";
if (efforts.Count == 0) return null;
//### Create dynamic object
dynList = Enumerable.Range(1, tasks.Count).Select((iy) =>
{
ix = 1;
tName = tasks.Where(t => t.Id == iy).FirstOrDefault().NAME;
EffortItem dynEff = new EffortItem();
dynEff.Add("tID", iy);
dynEff.Add("Task", tName);
foreach (Role pRole in roles)
{
myEff = efforts.First(e => e.PTask == iy && e.Role == ix).eHrs;
dynEff.Add(pRole.NAME, (double) myEff);
ix++;
}
return dynEff;
}).Cast().ToList ();
return dynList;
} catch (Exception args)
{
MessagingCenter.Send(new AlertContent("Get Data Error", args.Message + "/" + args.InnerException, "fas fa-exclamation-triangle"), "AlertNotification");
return null;
}
}
public async TaskSetDynEfforts(EffortItem updEfforts)
{
//### Get DB data
Efforts myEffort = null;
var roles = Roles.ToList();
double myEff = 0; int tID;
//### Update db data from dynamic object
try
{
tID = (int)updEfforts.Get("tID");
foreach (Role pRole in roles)
{
var ok = double.TryParse(updEfforts.Get(pRole.NAME).ToString(), out myEff);
if (!ok) myEff = 0;
myEffort = Efforts.Where(e => (e.PTask == tID && e.Role == pRole.Id)).FirstOrDefault();
if (myEffort != null)
{
myEffort.eHrs = myEff;
this.Efforts.Update(myEffort);
} else
{
myEffort = new Efforts
{
PTask = tID,
Role = pRole.Id,
eHrs = myEff
};
this.Efforts.Add(myEffort);
}
}
await this.SaveChangesAsync();
return true;
} catch
{
return false;
}
}
3) the Razor page to dynamically show the table in 2 dynamic dimensions and CRUD handling events:
@if (dynList == null)
{Loading...
}
else
{
<SfGrid DataSource="@dynList" AllowPaging="false" Toolbar="@ToolbarItems" AllowSorting="true" EnableAltRow="true" AllowGrouping="false" AllowTextWrap="true" Height="500">
<GridEvents OnActionComplete="EffortsActionHandler" OnActionFailure="ActionFailureHandler" TValue="EffortItem"></GridEvents>
<GridEditSettings AllowAdding="false" AllowDeleting="false" AllowEditing="true" AllowEditOnDblClick="true"></GridEditSettings>
<GridColumns>
@foreach (var colName in dynList[0].GetDynamicMemberNames())
{
@switch (colName) {
case "Task":
<GridColumn Field="@colName" Width="250" IsPrimaryKey="true" AllowEditing="false" TextAlign="TextAlign.Left"></GridColumn>
break;
case "tID":
break;
default:
<GridColumn Field="@colName" Width="120" Format="N2" AllowEditing="true" TextAlign="TextAlign.Right">
</GridColumn>
break;
}
}
</GridColumns>
</SfGrid>
<p></p>
}
@code {
ListdynList { get; set; } = null;
dynamic propEff = new ExpandoObject();
Listtasks;
Listroles;
private ListToolbarItems = new List () { "Edit", "Update", "Cancel" };
protected override async Task OnInitializedAsync()
{
tasks = await db.PTask.ToListAsync();
roles = await db.Roles.ToListAsync();
int ix = 1;
string tName = ""; string rName = "";
//### Get DB values into table object
dynList = await db.GetDynEfforts();
//### Create dummy entries if db is empty
if (dynList == null)
{
//### Create new dynamic object
dynList = new List();
dynList = Enumerable.Range(1, tasks.Count).Select((iy) =>
{
ix = 1;
tName = tasks.Where(t => t.Id == iy).FirstOrDefault().NAME;
EffortItem dynEff = new EffortItem();
dynEff.Add("tID", iy);
dynEff.Add("Task", tName);
foreach (Role pRole in roles)
{
dynEff.Add(pRole.NAME, (double)0.0);
ix++;
}
return dynEff;
}).Cast().ToList ();
//### Save to db as initial setup
foreach (EffortItem dynEff in dynList) db.SetDynEfforts(dynEff);
}
}
public async void EffortsActionHandler(ActionEventArgsargs)
{
if (args.RequestType == Syncfusion.Blazor.Grids.Action.Save)
{
//if (!db.SetDynEfforts(args.Data).Result) throw new InvalidOperationException("Error during DB save operation");
var ret = db.SetDynEfforts(args.Data);
}
}