Zufar
Zufar

Reputation: 3

What's wrong with Blazor's InputCheckBox

I'm trying to realize Blazor's InputCheckBox with Dictionary<string, bool> model. In fact I want to realize a set of CheckBoxes in foreach cycle. And while I have at the first line of the cycle property with {get; set;} in InputCheckBox (as shown in my Visual Studio's screenchort) it turns to readonly property with only {get;}

@page "/select-props"
@{
    CsvContent headers = new();
    
}
<h3>Properties picker</h3>

<div class="container container-fluid overflow-auto">
    <table class="table overflow-auto">
        <EditForm Model="headers">
            @foreach (var item in headers.Headers)
            {
            <tr class="row ">
                @if (item.Key is not null)
                    {
                        <td scope="col" class="col border border-1 border-secondary">
                            <InputCheckbox class="btn-check" @bind-Value="item.Value">
                                @item.Key
                            </InputCheckbox>
                        </td>
                    }
            </tr>
            }
            <button class="btn btn-info" type="submit">
                Submit
            </button>
        </EditForm>
    </table>
</div>

@code {
}

That is the model class:

    public class CsvContent
    {
        static Dictionary<int, char> divider = new() { { 0, ',' }, { 1, ';' }, { 2, '\t' } };
        public static List<List<string>?> FileContent { get; set; }
        public Dictionary<string, bool>? Headers { get; set; }

        public static async Task/*<List<List<string>>*/  ReadCsvAsync(string path, int divederCode = 2)
        {
            FileContent = new List<List<string>?>();
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            using (StreamReader reader = new StreamReader(path, System.Text.Encoding.GetEncoding(1251)))
            {
                string? line;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    List<string> cells = new();
                    cells.AddRange(line.Split(divider[divederCode]));
                    FileContent.Add(cells);
                }
            }
        }

        public CsvContent()
        {
            if (FileContent is not null)
            {
                foreach (var item in FileContent)
                    Headers.TryAdd(item.ElementAt(0), false);
            }
        }
    }

I can't relize why my dictionary value turn to readonly, please help...

Upvotes: 0

Views: 1046

Answers (3)

Dmitry
Dmitry

Reputation: 21

Workaround for binding to Dictionary<string, bool>:

@foreach (var item in headers.Headers)
{
    bool boolVal = headers.Headers[item.Key];
    ...
    <InputCheckbox Value="boolVal" ValueExpression="@(() => boolVal)" ValueChanged="@(b => headers.Headers[item.Key] = b)">
                        @item.Key
    </InputCheckbox>
    ...
}

It work in ASP.Net 6.0.

Upvotes: 2

MrC aka Shaun Curtis
MrC aka Shaun Curtis

Reputation: 30410

As eluded to in the other answer and comments, KeyValuePair objects are readonly .

You can move away from the Dictionary, and use a List of a header state object. You are almost certainly doing this in the context of some sort of Grid control, so will be using the Header State elsewhere.

Here's a Demo page with the object classes included in the page.

Note the null reference warning goes away by removing the nullable ? in the declaration and declaring a default value for Headers.

@page "/"

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

<h3>Properties picker</h3>

<EditForm Model="headers">
    @foreach (var item in headers.Headers)
    {
        <div class="form-check mb-2">
            <InputCheckbox class="form-check-input" @bind-Value=item.State />
            <label class="form-check-label">@item.Name</label>
        </div>
    }
</EditForm>

<div class="bg-dark text-white m-2 p-2">
    @foreach (var item in headers.Headers)
    {
        <pre>@item.Name : @item.State</pre>
    }
</div>

@code {
    private Model headers = new();

    protected override Task OnInitializedAsync()
    {
        headers.Headers.Add(new("UK", false));
        headers.Headers.Add(new("France", false));
        headers.Headers.Add(new("Spain", false));

        return Task.CompletedTask;
    }

    public class Model
    {
        // The null reference error goes away by declaring a default value for Headers
        public List<HeaderData> Headers { get; set; } = new();
    }

    public class HeaderData
    {
        public string Name { get; init; }
        public bool State { get; set; }

        public HeaderData(string name, bool state = false)
        {
            this.Name = name;
            this.State = state;
        }
    }
}

enter image description here

Upvotes: 0

T.Trassoudaine
T.Trassoudaine

Reputation: 1375

KeyValuePair.Value is readonly, there is not much you can do about it. However, I believe you can bind it going through the dictionary directly:

@bind-Value="headers.Headers[item.Key]".

Upvotes: 0

Related Questions