Home > Back-end >  Blazor - Binding Blazor Component or workaround [ask from WPF guy]
Blazor - Binding Blazor Component or workaround [ask from WPF guy]

Time:11-06

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.

CodePudding user response:

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 >
    <h3>Counter Display @Service.CounterState.Name</h3>
    <div>Counter : @Service.CounterState.Counter</div>
    <div >
        <button  @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 >
    <button  @onclick=this.AddCounter>AddCounter</button>
    @foreach (var counter in Service.Items)
    {
        <button  @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:

enter image description here

CodePudding user response:

I think you overcomplicate things.
Basic one-way binding only needs a Parameter:

SaySomething.razor

<h3>@Label</h3>

@code {
    [Parameter]
    public string Label { get; set; } 
}

Index.razor

<SaySometing Label="@sayWhat" />

@code
{
    string sayWhat = "";

    void OnClickSomething()
    {
       sayWhat = $"It is now {DateTime.Now}";
    }
}
  • Related