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:
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}";
}
}