Blazor table component not updating dynamically - c#

I ran into an issue with server-side Blazor trying to create a custom table component. The data in the table rows updates and changes dynamically so that is not the issue but if I bind the header on a property, the header will take the previous value of that property.
From table.razor, I am setting up a simple dropdown <select> tag with default values. When that value is changed, it should update the value on the table header.
I have added a <code> tag and a classic HTML table as a test and they both reflect the new <select> value properly. Any idea why it's not the same for a custom component?
Table.razor
#page "/table"
#using BlazorApp1.Data
#inject WeatherForecastService ForecastService
<select #bind="#SelectedListItem">
<option value="test">Test</option>
<option value="test2">Test2</option>
</select>
<code>#SelectedListItem</code>
<table>
<thead>
<tr>
<th>#SelectedListItem</th>
</tr>
<tbody>
<tr>
<td>1</td>
</tr>
<tr>
<td>2</td>
</tr>
<tr>
<td>3</td>
</tr>
<tr>
<td>4</td>
</tr>
<tr>
<td>5</td>
</tr>
</tbody>
</thead>
</table>
#if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<BlazorApp1.Components.DataTable Items=#forecasts TRowItem="WeatherForecast">
<BlazorApp1.Components.Column CustomTitle="#SelectedListItem" TRowItem="WeatherForecast"></BlazorApp1.Components.Column>
</BlazorApp1.Components.DataTable>
}
#code{
public string SelectedListItem { get; set; }
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}
DataTable.cs
using Microsoft.AspNetCore.Components;
namespace BlazorApp1.Components
{
public partial class DataTable<TRowItem> : ComponentBase
{
[Parameter]
public IList<TRowItem> Items { get; set; } = new List<TRowItem>();
[Parameter]
public RenderFragment? ChildContent { get; set; }
private IList<Column<TRowItem>> Columns { get; set; } = new List<Column<TRowItem>>();
protected override void OnInitialized()
{
if (Items == null) Items = new List<TRowItem>();
}
protected override async Task OnParametersSetAsync()
{
await UpdateAsync().ConfigureAwait(false);
}
public async Task UpdateAsync()
{
Refresh();
}
public void Refresh()
{
InvokeAsync(StateHasChanged);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
foreach (var column in Columns)
{
column.StateChanged += ColumnStateChanged;
}
StateHasChanged();
}
}
public void Dispose()
{
foreach (var column in Columns)
{
column.StateChanged -= ColumnStateChanged;
}
Items.Clear();
}
public void AddColumn(Column<TRowItem> column)
{
Columns.Add(column);
StateHasChanged();
}
private void ColumnStateChanged(Object? sender, EventArgs args) => StateHasChanged();
}
}
DataTable.razor
#typeparam TRowItem
<h3>DataTable</h3>
<CascadingValue Value="this">
<div>
<table>
<thead>
<tr>
#foreach(var column in Columns)
{
<th nowrap>#column.CustomTitle</th>
}
</tr>
</thead>
<tbody>
#foreach(var item in Items)
{
foreach(var column in Columns)
{
<td></td>
}
}
</tbody>
<tfoot>
</tfoot>
</table>
</div>
#ChildContent
</CascadingValue>
Column.cs
using Microsoft.AspNetCore.Components;
using System.Linq.Expressions;
namespace BlazorApp1.Components
{
public partial class Column<TRowItem> : ComponentBase
{
[CascadingParameter]
private DataTable<TRowItem>? DataTable { get; set; }
[Parameter]
public string? CustomTitle { get; set; }
[Parameter]
public Expression<Func<TRowItem, object>>? Property { get; set; }
protected override Task OnInitializedAsync()
{
if (DataTable == null) throw new ArgumentNullException($"A 'DataTableColumn' must be a child of a 'DataTable' component");
DataTable.AddColumn(this);
return Task.CompletedTask;
}
public event EventHandler? StateChanged;
private void RaiseStateChanged()
{
EventHandler? handler = StateChanged;
handler?.Invoke(this, new EventArgs());
}
}
}
Column.razor
#typeparam TRowItem

Any idea why it's not the same for a custom component?
Any change in your select causes a Blazor UI event in the page which triggers a re-render event. The Renderer does this by triggering SetParametersAsync on the component The component updates its parameters, runs OnParametersSet{Async} and re-renders.
DataTable contains Column so it re-renders first. At this point CustomTitle in Column hasn't run SetParametersAsync, so DataTable renders the current "old" value. Column then re-renders setting CustomTitle to the new value.
Based on the code you've shown the answer is to render the label in the Column component. But I'm guessing that's a bit more complex in reality, so that's probably not the answer. You probably want it to show the title in the header section and the value in the row template. If so I can point you to some code that shows how to do that.
The solution is not to do some more plumbing or put in some more StateHasChanged, but to rethink what your doing. Get the data i.e. your table configuration into a data class/classes and cascade that down to your individual components. Or use a DI service to hold the data and drive the component updates by events.

Related

Blazor - Binding Blazor Component or workaround [ask from WPF guy]

I am looking for something like this in the WPF for Blazor, if is it possible:
WPF:
<ContentPresenter Content="{Binding LicensesText}" />
public LicensesText LicensesText { get; set; }
ctor()
{
LicensesText licensesText = new LicensesText();
licensesText.ClickedShowComparision += () => { Licenses = new Licences(); };
LicensesText = licensesText;
//Sleep for 5 seconds
List<LicensesText> list = new ();
list.Add(licensesText);
LicensesText licensesText1 = new LicensesText();
licensesText1.ClickedShowComparision += () => { Licenses = new Licences(); };
LicensesText = licensesText1;
//Sleep for 5 seconds
LicensesText = list.First();}
}
The Best would be something like this:
BLAZOR:
SaySomething.razor
<h3>#Label</h3>
#code {
public string Label { get; set; } = "Default Label";
public void SetLabel(string label)
{
Label = label;
StateHasChanged();
}
}
Index.razor
#SaySometing
#code
{
public SaySometing SaySometing { get; set; } = new SaySometing();
}
But for sure it doesn't work like it.
I come to some solution but all of them are imperfect. And I dont know how to visualize for example already created ComponentBase element, if is it possible.
#page "/"
#DynamicRender
<SaySometing #ref="SaySometing" />
<DynamicComponent Type="typeof(SaySometing)" #ref="DynamicComponent"></DynamicComponent>
<button #onclick="args => ChangeText(args)">Click Me!</button>
#code
{
public SaySometing SaySometing { get; set; } = new SaySometing();
public SaySometing SaySometing1 { get; set; } = new SaySometing();
public DynamicComponent DynamicComponent { get; set; } = new DynamicComponent();
private RenderFragment DynamicRender { get; set; }
protected override Task OnInitializedAsync()
{
DynamicRender = CreateComponent();
return base.OnInitializedAsync();
}
private RenderFragment CreateComponent() => builder =>
{
builder.OpenComponent(0, typeof(SaySometing));
builder.AddComponentReferenceCapture(1, obj =>
{
SaySometing1 = (SaySometing) obj;
});
builder.CloseComponent();
};
private void ChangeText(MouseEventArgs args)
{
SaySometing.SetLabel("Hello From SaySomething!");
SaySometing1.SetLabel("Gutten Tag From SaySomething1!");
((SaySometing?)DynamicComponent?.Instance)?.SetLabel("DobrĂ½ den from DynamicComponent");
StateHasChanged();
}
}
All three works properly and switch text to the right SaySomething.Label. But what if i want to change the SaySometing1 to new instance and keep the old instance for later and then again render it? Or how is it solved in the Blazor. As i told, I was the WPF guy and it is a new jungle for me. Or the best workaround in this technology.
You need to separate out the data and it's state from it's presentation. Many components demonstrate no separation of concerns: data access, data management and state, and data display are all rolled into a page.
This may be totally wide of the mark: I'll remove the answer if it is.
Take the Counter.
We can create a counter state object:
public class CounterState
{
public Guid CounterId { get; } = Guid.NewGuid();
public int Counter { get; private set; }
public string Name => this.CounterId.ToString().Substring(0, 4);
public void Increment()
=> Counter++;
}
A Service to manage the data - in this case a list of counter objects:
public class CounterViewService
{
public CounterState CounterState { get; private set; }
public List<CounterState> Items { get; set; } = new();
public event EventHandler? CounterChanged;
public CounterViewService()
{
this.CounterState = new CounterState();
Items.Add(this.CounterState);
}
public void SetCounter(CounterState item)
{
this.CounterState = item;
this.CounterChanged?.Invoke(item, EventArgs.Empty);
}
public void AddCounterState()
{
var item = new CounterState();
this.Items.Add(item);
this.CounterState = item;
}
public void Increment()
{
this.CounterState.Increment();
this.CounterChanged?.Invoke(this.CounterState, EventArgs.Empty);
}
}
Registered as a service like this:
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<CounterViewService>();
builder.Services.AddSingleton<WeatherForecastService>();
A Counter Component to display counter state:
#inject CounterViewService Service
#implements IDisposable
<div class="bg-dark text-white p-2 m-2">
<h3>Counter Display #Service.CounterState.Name</h3>
<div>Counter : #Service.CounterState.Counter</div>
<div class="m-2">
<button class="btn btn-light" #onclick="IncrementCount">Increment me</button>
</div>
</div>
#code {
protected override void OnInitialized()
=> this.Service.CounterChanged += this.OnChange;
private void IncrementCount()
=> this.Service.Increment();
private void OnChange(object? sender, EventArgs e)
=> this.StateHasChanged();
public void Dispose()
=> this.Service.CounterChanged -= this.OnChange;
}
And finally the display page:
#inject CounterViewService Service
#page "/counter"
<PageTitle>Counter</PageTitle>
<CounterDisplay />
<div class="m-2">
<button class="btn btn-primary me-2" #onclick=this.AddCounter>AddCounter</button>
#foreach (var counter in Service.Items)
{
<button class="btn btn-secondary me-2" #onclick="() => this.SelectCounter(counter)">Select Counter #(counter.CounterId.ToString().Substring(0, 4))</button>
}
</div>
#code {
private void AddCounter()
=> this.Service.AddCounterState();
private void SelectCounter(CounterState counter)
=> this.Service.SetCounter(counter);
}
A screenshot to show it in action:
Here is what exactly I need...
RazorParent.razor:
//Soemthing like ContentPresenter in the WPF
< DoesExistSomethingLikeThis Content="#RazChild" />
<a #OnClick="DisplayConfiguredRazChild">...
<a #OnClick="DisplayEmptyRazChild">...
RazorParent.razor#code:
RazorChildren RazChild {get;set;} = null;
void DisplayConfiguredRazChild()
{
RazorChildren razChild = new RazorChildren();
razChild.Configure();
RazChild = razChild;
StateHasChanged();
}
void DisplayEmptyRazChild()
{
RazorChildren razChild = new RazorChildren();
RazChild = razChild;
StateHasChanged();
}
Or if exist something like you create view and configure it in the code behind and then display it through the binding and still control it over the created instance... Or you must go always through the:
<RazorChildren #ref="_razChildRef" Param1="#RazChild.Param1"/>
Where you unfortunately don't control the original instance. Is it possible to create something like WPF ContentPresenter in the Blazor? Where you just bind the ComponentBase instance? It would be amazing, I know there are RenderFragment, DynamicComponent etc... But nothing can substitude this behavior where you just bind directly the ComponentBased component... Without duplicate reference instances and passing parameters in razor.

refer to components that created by loop

I want to create a components by following this steps:
I have a list of items.
I want to loop in this list and create a component like InputNumber.
Add EventCallback to the generic created InputNumber that accept ref of this Inputtext because I want to use this ref to set the focus on this InputNumber.
I have also onblure method that execute some code for me, and I am using the onfocus to return focus to the input after execute this code by onblure
My question How can I get this ref and send it as parameter of EventCallback? The problem here that this components have been generated by loop, so I don't want to create by hand hundred variables to represent ref's.
My concept code like this:
#code{
private void OnFocus(MyInputNumber<double?> obj)
{
if (obj is not null)
{
obj!.Element.Value.FocusAsync();
}
}
}
#foreach(var MyItem in MyList)
{
<EditForm Model="MyItem">
//Some components ..
<label>
Test
<InputNumber #bind-Value="MyItem.MyVal"
#onfocus="#((InputNumber<double?> obj #*wrong*#) =>
OnFocus(obj))"
#onblur=#(() => OnblureHandler(context))
</label>
</EditForm>
}
If you see up the parameter InputNumber<double?> obj, this way is wrong, usually I use #ref=SomeVariable but becasue I created in generic way, I can not do that.
Note:
I don't to adjust my list to be dictionary<MYItemType,InputNumber<double?>>, or create a new class that has InputNumber<double?> as property. I am searching for different way, like go from editcontext to any input has been modified and reset focus on it, I don't know if that possible !
You can add an InputNumber<double?> InputNumberRef { get; set; } property to your model class. Then is the foreach loop bind it to the component reference #ref="MyItem.InputNumberRef" then you can pass it in your callback method #onblur="() => HandleBlur(MyItem.InputNumberRef)".
Here is the demo code that I used. The following code after input onblur event it waits 2 seconds and returns the focus to the input.
#page "/"
#foreach (var item in _items)
{
<EditForm Model="#item">
<InputNumber class="form-control" #ref="#item.InputNumberRef" #bind-Value="#item.Value" #onblur="() => HandleBlur(item.InputNumberRef)" />
</EditForm>
}
#code {
private List<Item> _items = new List<Item>
{
new Item { Value = 10 },
new Item { Value = 30 },
new Item { Value = 20 },
};
private async Task HandleBlur(InputNumber<int> inputNumberRef)
{
if (inputNumberRef.Element.HasValue)
{
await Task.Delay(2000);
await inputNumberRef.Element.Value.FocusAsync();
}
}
public class Item
{
public int Value { get; set; }
public InputNumber<int> InputNumberRef { get; set; }
}
}
Credits to user #enet for suggesting this solution in a different question on stackoverflow.
If your requirement is that you apply some form of complex validation on the content of the input before the user is allowed to leave it, i.e if the handler attached to onBlur fails validation then you want to return focus to the input, then this is how to do that without resorting to dictionaries, ...
I've defined a custom InputText component to demonstrate the principles. You'll need to apply the same principles to any other InputBase component where you want to apply the functionality. The key is defining a delegate Func (which returns a bool) as a parameter which is called when the user tries to leave the control. As everything is contained within the component (a bit of SOLID as pointed out by #BrianParker), we can call the inbuilt Element property to return focus to the component.
#inherits InputText
<input #ref="Element"
#attributes="AdditionalAttributes"
class="#CssClass"
value="#CurrentValue"
#oninput="OnInput"
#onblur="OnBlur" />
#if (validationMessage != string.Empty)
{
<div class="validation-message">
#validationMessage
</div>
}
#code {
private string validationMessage = string.Empty;
[Parameter] public Func<string?, Task<bool>>? BlurValidation { get; set; }
[Parameter] public string ValidationFailMessage { get; set; } = "Failed Validation";
private void OnInput(ChangeEventArgs e)
=> this.CurrentValueAsString = e.Value?.ToString() ?? null;
private async Task OnBlur(FocusEventArgs e)
{
validationMessage = string.Empty;
if (Element is not null && BlurValidation is not null && !await this.BlurValidation.Invoke(this.CurrentValue))
{
await Element.Value.FocusAsync();
validationMessage = ValidationFailMessage;
}
}
}
And a demo page:
#page "/"
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
#foreach(var item in model)
{
<EditForm Model=item>
<MyInputText class="form-text" #bind-Value=item.MyCountry BlurValidation=CheckBlur />
</EditForm>
}
#code {
private List<MyData> model = new List<MyData>() { new MyData { MyCountry = "UK" }, new MyData { MyCountry = "Australia" } };
private async Task<bool> CheckBlur(string value)
{
// Emulate some async behaviour to do whatever checking is required
await Task.Delay(100);
// simple test here to demonstrate - I know you could use nornal validation to do this!
return value.Length > 5;
}
public class MyData
{
public string? MyCountry { get; set; }
}
}
I'm not sure I'm happy with the UX using this design, but it's your code.

Blazor, MatBlazor - How to catch the value change of MatSelect component

I have used the MatBlazor framework for my project.
In MatSelect, I want to catch its value onchange event to do some other works.
I have tried some solutions but the onchange event has not fired yet.
<MatSelect Label="Customer" Value="#customer" ValueChanged="OnChangeCustomer">
<MatOptionString Value="-1">All</MatOptionString>
#foreach (var item in customers)
{
<MatOption Value="#item.Id">#item.Name</MatOption>
}
</MatSelect>
The below is my onchange event handler. But it did not fired when select another value in drop down list:
public void OnChangeCustomer(ChangeEventArgs args)
{
if (args.Value.ToString() != "-1")
isAccountDropDownListDisabled = false;
}
Can anyone help me?
Thanks
You could refer the following sample to using the MatSelect control:
<MatSelect Outlined="true" Label="Category" ValueChanged="(string i) => OnChangeCategory(i)">
<MatOptionString Value="-1">All</MatOptionString>
#foreach (var cat in GetCategories())
{
<MatOptionString Value="#cat.Id.ToString()">#cat.Name</MatOptionString>
}
</MatSelect>
<span>#selectedValue</span>
#code
{
public string selectedValue;
protected List<Customer> GetCategories()
{
//return new List<string>() { "AA", "BB" };
return new List<Customer>() {
new Customer(){Id=1001, Name="Tom"},
new Customer(){Id=1002, Name="David"},
new Customer(){Id=1003, Name="Lucy"}
};
}
protected void OnChangeCategory(string value)
{
//do something
selectedValue = "Selected Value: " + value;
}
}
The screenshot as below:
More detail information, check the MatSelect document.
The code from #ZhiLv works well but if you want a pre filled dynamic select value it will become harder.
I spent so many hours trying to get this to work with MatSelectValue with no luck.
https://www.matblazor.com/SelectValue
I ended up using a simple MatSelect with a property calling my onchange event method. This is the only way I got the select list to prefill correctly.
Example with nullable int but you can change to string, guid etc as well.
https://www.matblazor.com/Select#MatSelectGuid
#inject StateContainer StateContainer
<MatSelect Label="Choose threat" #bind-Value="#SelectThreatId" Outlined="true" FullWidth="true">
#foreach (var item in selectThreats)
{
<MatOption TValue="int?" Value="#item.Id">#item.Threat</MatOption>
}
</MatSelect>
#code
{
[Parameter]
public ThreatAndCountermeasureDto ThreatAndCountermeasureDto { get; set; }
List<ThreatDto> selectThreats = new List<ThreatDto>();
ThreatDto selectedThreat = null;
private int? threatId = null;
public int? SelectThreatId
{
get { return threatId; }
set
{
threatId = value;
SelectThreatValueChanged(value);
}
}
private void SelectThreatValueChanged(int? id)
{
selectedThreat = StateContainer.Threats.Single(x => x.Id == id);
}
protected override void OnInitialized()
{
base.OnInitialized();
StateContainer.OnChange += StateHasChanged;
SelectThreatId = ThreatAndCountermeasureDto.Threat.Id;
selectThreats = StateContainer.Threats.ToList();
}
...
Source:
https://github.com/SamProf/MatBlazor/issues/498

Pass the name of a component when clicked

How would I go about passing the name of a component that is clicked through the onclick handler in Blazor WebAssembly? For example (In this case we'll use a table cell):
InterestingTable.razor
<table class="table">
<thead>
<tr>
<th scope="col">First</th>
<th scope="col">Second</th>
</tr>
</thead>
<tbody>
<tr>
<td #onclick="#(e => CellClicked(e, ?????))" name1="firstTd"><SomeComponent name2="firstComponent" /></td>
<td #onclick="#(e => CellClicked(e, ?????))" name1="secondTd"><SomeComponent name2="secondComponent" /></td>
</tr>
</tbody>
</table>
InterestingTableBase.cs
protected void CellClicked(MouseEventArgs e, ????) {
}
How would I pass either the string in name1 or name2 into the onclick handler?
Thanks!
In javascript when a dom event handler is invoked, the this keyword inside the handler is set to the DOM element on which the handler is registered. In Blazor this is not the case. You either have to create a component or if you have to use native elements store the values in the code section so that you can access them in the event handler.
Index.razor:
<div name="#myDivName" #onclick="MyHandler2"></div>
<Component Name="Foo" OnClick="MyHandler"></Component>
#code{
private string myDivName = "foo";
public void MyHandler2()
{
}
public void MyHandler(string name)
{
}
}
Component.razor:
<div name="#Name" #onclick="()=>OnClick.InvokeAsync(Name)"></div>
#code {
[Parameter]
public string Name { get; set; }
[Parameter]
public EventCallback<string> OnClick { get; set; }
}

Add checkbox fields to datatable based table MVC 4 Razor

I am using MVC 4 Visual Studio 2012 with Razor.
I am generating a table based off of several tables pulled into a dataset from a remote call to a SQL server.
I want to be able to output these tables onto the webpage and then create two columns of checkboxes beside them in order to assign them to one area or another (it essentially sorts data into accepted and not accepted, while allowing some to continue pending if a decision has not been made).
I currently have all the tables in the dataset being assigned to datatables in the controller and then exported to the razor page. I do not have a model set up for this as of yet and I'm not sure what I would require in one if I did.
This is my current View:
#{
ViewBag.Title = "Requisitions";
}
<table class="table">
<thead>
<tr>
#foreach (System.Data.DataColumn col in Model.Columns)
{
<th class ="td">#col.Caption</th>
}
</tr>
</thead>
<tbody>
#foreach(System.Data.DataRow row in Model.Rows)
{
<tr>
#foreach (var cell in row.ItemArray)
{
<td class="td">#cell.ToString()</td>
}
</tr>
}
</tbody>
</table>
This is my current controller:
DataTable R = new DataTable();
public void GetData()
{
string connString = "Data Source=.;database=dataBase;Integrated Security=SSPI";
DataSet dataset = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter("dbo.procApprovalSelectPending", connString);
using (adapter)
{
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.Fill(dataset);
}
int count = dataset.Tables.Count;
for (int i = 0; i < dataset.Tables.Count; i++)
{
// Do something for each recordset (11 recordsets)
if (i == 0)
{
R = dataset.Tables[i];
}
}
dataset.Dispose();
adapter.Dispose();
}
public ActionResult Rs()
{
GetData();
return View(R);
}
I have more datatables than shown but they were removed to conserve space.
To sum it up, I would like two checkbox columns that are aligned with each row that is created from the datatable that allow me to choose that portion of data to send back to the server what has been changed with it, and creating a dynamic checkbox for each row was giving me an error such that :
#foreach(System.Data.DataRow row in Model.Rows)
{
<tr>
#foreach (var cell in row.ItemArray)
{
<td class="td">#cell.ToString()</td>
}
<td class="td">#Html.CheckBoxFor(m => m.Checkbox)</td>
</tr>
}
"An expression tree may not contain a dynamic operation"
Thanks in advance!
You should create a view model to represent the properties you want to display, including 2 additional boolean properties for 'accepted' and 'not accepted'. Assuming your columns are ID and Name
public class MyModel
{
public int ID { get; set; }
public string Name { get; set; }
public bool Accepted { get; set; }
public bool NotAccepted { get; set; }
}
and create a collection based on each row in your table that you want to display
public ActionResult Rs()
{
List<MyModel> items = new List<MyModel>();
// Populate items from your datatable
return View(items);
}
then in your view
#model List<MyModel>
#using (Html.BeginForm()
{
<table>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m[i].ID)</tr>
<td>#Html.TextBoxFor(m => m[i].Name)</tr>
<td>#Html.CheckBoxFor(m => m[i].Accepted)</tr>
<td>#Html.CheckBoxFor(m => m[i].NotAccepted)</tr>
</tr>
}
</table>
<input type="submit" value="Save" />
}
then in your post method
[HttpPost]
public ActionResult Rs(List<MyModel> model)
{
foreach(MyModel item in model)
{
if(item.Accepted) {.. // do something
else if (item.NotAccepted) { .. // do something else
else {.. // do another thing
}
}
Use Following pattern
I have Created one static Class that is called as CommonUtilities it is giving me FormCollection,Checkboxname and valueofKey
public static class CommonUtilities
{
public static List<string> GetCheckboxSelectedValue(FormCollection frm, string checkboxname, string value)
{
List<string> retls = new List<string>();
var fileIds = frm[value].Split(',');
var selectedIndices = frm[checkboxname].Replace("true,false", "true").Split(',').Select((item, index) =>
new {
item= item,
index= index
}).Where(row=>row.item =="true")
.Select(row => row.index).ToArray();
if (selectedIndices.Count() > 0)
{
retls.AddRange(selectedIndices.Select(index => fileIds[index]));
}
return retls;
}
}
the above Function will be applied as below
Controller
List<string> selectedKeypair = CommonUtilities.GetCheckboxSelectedValue(frm, "m.BoolSelectedVal", "m.Key");
view
#foreach (MVCCareerModule.Models.Requirement m in Model.RequirementSearchResult)
{
<tr>
<td>
#Html.CheckBoxFor(modelitem => m.Apply)
#Html.HiddenFor(modelitem => m.Req_ID, new { Value = m.Req_ID })
</td>
</tr>
}
You will receive all Selected IDs in selectedKeypair

Categories