I try to build a WPF Blazor App following this MS documentation. I try to add mudblazor along this guide.
MainWindow Code Behind:
public MainWindow()
{
InitializeComponent();
var serviceCollection = new ServiceCollection();
serviceCollection.AddWpfBlazorWebView();
serviceCollection.AddMudServices();
Resources.Add("services", serviceCollection.BuildServiceProvider());
}
Counter Razor Component:
<MudText Typo="Typo.h1">Counter</MudText>
<MudText Typo="Typo.h5">Current state: #currentCount</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Secondary"
#onclick="IncrementCount">Click me</MudButton>
#code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
The MudBlazor magic is sadly not visible, the result is :
Where I`m wrong?
Related
After a successfull experience with Blazor web server and Maui hybrid with Blazor, I'm now taking my first steps integrating my Blazor components in Winforms.
But I found no tutorials with samples of how to navigate between different pages/windows/forms.
Html links do not seem to work, and neither do NavigationManager:
Default page, app successfully loads it:
#inject NavigationManager NavigationManager
<div>
<button #onclick="OnClick">Go to page 2</button>
<a href="#" #onclick="#OnClick" #onclick:preventDefault>go to page 2</a>
<a href="page2" >go to page 2</a>
</div>
#code {
private void OnClick()
{
NavigationManager.NavigateTo("Page2");
}
}
Second page, no way to open it from previous one:
#page "/Page2"
<div>Wellcome to Page2</div>
Should I better navigate between Forms?
Update 1
I finally got it working having the component trigger an event on a form state class which instantiates a new form and shows it.
I guess I should better declare a singleton service with global appstate class in program.cs and hold all form instantiation from there, but I can't find the right way to declare it on a Blazor Winforms project.
My last version as follows, but appState is null when initializing Form1:
The singleton that communicates between Blazor compolnents and Winforms forms:
public class AppState
{
public event EventHandler<EventArgs>? GotoForm;
public void NavigateNext()
{
GotoForm?.Invoke(this,new EventArgs());
}
}
The component which feeds content to each form:
#inject AppState appState
<h3>Welcome to Form #FormNumber</h3>
<button #onclick="appState.NavigateNext">Goto page #AltFormNumber()</button>
#code {
[Parameter] public int FormNumber { get; set; }
int? AltFormNumber()=>FormNumber == null ? null : 3-FormNumber;
}
The default form:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
public partial class Form1 : Form
{
[Inject] AppState appState { get; }
public Form1()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 1 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form2();
frm.Show();
}
}
The alternative form:
public partial class Form2 : Form
{
[Inject] AppState appState { get; }
public Form2()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
//services.AddSingleton<FormState>();
blazorWebView1.HostPage = "wwwroot\\index.html";
blazorWebView1.Services = services.BuildServiceProvider();
//var appState = blazorWebView1.Services.GetRequiredService<AppState>();
appState.GotoForm += NavigateNext;
blazorWebView1.RootComponents.Add<Pages.FormComponent>("#app",
new Dictionary<string, object?> { { "FormNumber", 2 } });
}
private void NavigateNext(object? sender, EventArgs args)
{
var frm = new Form1();
frm.Show();
}
}
The Program.cs:
internal static class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
var host = CreateHostBuilder().Build();
ServiceProvider = host.Services;
Application.Run(new Form1());
}
public static IServiceProvider ServiceProvider { get; private set; }
static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((context, services) => {
services.AddSingleton<AppState>();
});
}
}
I am using a WPF Desktop app with BlazorWebView. I would like to open up file explorer and have the user select a folder to get the path selected. I can use the browser input to select files but as I understand it is a limitation of the browser to allow me to select a folder path. Is there a Folder Picker for native access?
The Process.Start only seem to open the file explorer and won't let me choose the folder.
<blazor:BlazorWebView HostPage="wwwroot\index.html" Services="{DynamicResource services}">
<blazor:BlazorWebView.RootComponents>
<blazor:RootComponent Selector="#app" ComponentType="{x:Type shared:App}" />
</blazor:BlazorWebView.RootComponents>
</blazor:BlazorWebView>
#using System.Diagnostics
<button #onclick="OnClickOpenNativeFileExplorer">Open</button>
#code {
private void OnClickOpenNativeFileExplorer(MouseEventArgs e)
{
Process.Start("explorer.exe");
}
}
For anyone wondering, I was able to solve it by doing the following.
I added the IFolderPicker interface to my razor class library. Then implement the FolderPicker in the WPF project using a NuGet package.
Install-Package WindowsAPICodePack-Shell -Version 1.1.1
public interface IFolderPicker
{
public string DisplayFolderPicker();
}
public class FolderPicker : IFolderPicker
{
public string DisplayFolderPicker()
{
var dialog = new CommonOpenFileDialog();
dialog.IsFolderPicker = true;
CommonFileDialogResult result = dialog.ShowDialog();
if (result == CommonFileDialogResult.Ok)
return dialog.FileName;
return "";
}
}
I then register the dependency using the DI container within the MainWindow.xaml.cs file.
public MainWindow()
{
InitializeComponent();
Application.Current.MainWindow.WindowState = WindowState.Maximized;
var serviceCollection = new ServiceCollection();
serviceCollection.AddWpfBlazorWebView();
serviceCollection.AddTransient<IFolderPicker, FolderPicker>();
Resources.Add("services", serviceCollection.BuildServiceProvider());
}
Then within the razor component, I have a button that calls the DisplayFolderPicker method.
#inject IFolderPicker _folderPicker
<button #onclick="OnClickOpenNativeFileExplorer">Open</button>
<p>#path</p>
#code {
private string path = "";
private void OnClickOpenNativeFileExplorer(MouseEventArgs e)
{
path = _folderPicker.DisplayFolderPicker();
}
}
Take-away: I suppose not only will this work for FolderPicker but for calling any native component.
In a pure WPF application I can use HostBuilder, like below. But how could I do the same in a Prism app? What I read, HostBuild is not possible in Prism (according to Brian Lagunas it would not make sense - cannot find the link).
Could someone point me to the right direction or share a code?
public App()
{
_host = new HostBuilder()
.ConfigureAppConfiguration((context, configurationBuilder) =>
{
configurationBuilder.SetBasePath(context.HostingEnvironment.ContentRootPath);
configurationBuilder.AddJsonFile("appsettings.json", optional: false);
})
.ConfigureServices((context, services) =>
{
services.Configure<AppSettings>(context.Configuration);
services.AddDbContext<VisitorDbContext>(options =>
options.UseSqlServer(context.Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ISampleService, SampleService>();
services.AddScoped<IImportService, ImportService>();
services.AddSingleton<MainWindow>();
})
.ConfigureLogging(logging =>
{
//logging.AddConsole();
})
.Build();
}
If you use the .NET host to parse the app settings, you could override the RegisterTypes method of your Prism App.xaml.cs class to register the IOptions<T> with the app:
public partial class App : PrismApplication
{
private readonly IHost _host;
public App()
{
_host = ...;
}
...
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance(
_host.Services.GetService<IOptions<AppSettings>>());
}
}
Without using the host, this could be done like the following
public sealed partial class App
{
private readonly AppSettings? _appSettings;
public App()
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile("appsettings.json", optional: false);
IConfigurationRoot configuration = configurationBuilder.Build();
IServiceCollection services = new ServiceCollection();
services.Configure<AppSettings>(configuration.GetSection(typeof(AppSettings).Name));
IServiceProvider provider = services.BuildServiceProvider();
IOptions<AppSettings>? options = provider.GetService<IOptions<AppSettings>>();
if (options is null)
throw new ApplicationException("App settings is not registered");
_appSettings = options.Value;
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance(_appSettings);
}
}
you would need to add the following package
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
and the following namespaces
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
My project in using cefsharp winform browser
There are not properly working OnLoadingStateChanged event
after opening some website this event 2 or 3 times trigger
I have no idea how to use properly code for hole website properly loaded then i get event
i am using VS 2015 Community
package version of cefsharp
all packages installed screenshot image
project dot net framework is 4.6.2
This is code i am using
public partial class BrowserWin : UserControl
{
public ChromiumWebBrowser browser;
public bool IsLoading;
public BrowserWin() {
InitializeComponent();
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
browser = new ChromiumWebBrowser();
browser.Dock = DockStyle.Fill;
this.Controls.Add(browser);
//Wait for the page to finish loading
browser.LoadingStateChanged += OnLoadingStateChanged;
browser.FrameLoadStart += Browser_FrameLoadStart;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
}
private void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
if (args.IsLoading == false)
{
IsLoading = false;
}
}
private void Browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e)
{
IsLoading = true;
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
{
}
}
I am trying to update a label control, on a "WPF" page, from my own .cs file but not updating .Please help me.I have a page "Measurements.xaml" ,after page load,in button click create object for my own cs file(sample.cs) and called a method in the cs file.In that method i am trying to update lable control on page (Measurements.xaml),as below
((Measurements)System.Windows.Application.Current.MainWindow).label1.Content = "153";
Measurements.xaml is not a window.
Not sure what you have done or how, but this should work:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void Button1_OnClick(object sender, RoutedEventArgs e)
{
var sample = new Sample();
sample.UpdateLabel();
}
}
class Sample
{
public void UpdateLabel()
{
var main = (MainWindow) Application.Current.MainWindow;
main.Label1.Content = "153";
}
}