Unable to load local html file in WebView2 - winforms

Trying loading HTML from Bin>Debug>html folder.
Want to achieve loading HTML from any location from the project package.
Code -
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
namespace WebViewPOC
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
initWebView();
}
async void initWebView()
{
await webView21.EnsureCoreWebView2Async(null);
webView21.CoreWebView2.Navigate(Path.Combine("file:", Application.StartupPath, "html", "index.html"));
}
}
}
Output and location of HTML

Related

ABPFramwork - Remove api from layer application in swagger

I have created a project using abpframwork. When running swagger, swagger receives the function in the application layer is a api. I don't want that. Can you guys tell me how to remove it in swagger
Code in Application Layer
public class UserService : AdminSSOAppService, ITransientDependency, IValidationEnabled, IUserService
{
IUserRepository _userRepository;
private readonly ILogger<UserService> _log;
public UserService(IUserRepository userRepository,
ILogger<UserService> log
)
{
_userRepository = userRepository;
_log = log;
}
public async Task<List<UserDto>> GetList()
{
var list = await _userRepository.GetListAsync();
return ObjectMapper.Map<List<User>, List<UserDto>>(list);
}
public async Task<UserDto> GetUserById(int Id)
{
var user = await _userRepository.GetAsync(c=>c.Id == Id);
return ObjectMapper.Map<User, UserDto>(user);
}
}
Code in HttpApi Layer
[Area(AdminSSORemoteServiceConsts.ModuleName)]
[RemoteService(Name = AdminSSORemoteServiceConsts.RemoteServiceName)]
[Route("api/user/user-profile")]
public class UserController : ControllerBase, IUserService
{
private readonly IUserService _userAppService;
public UserController(IUserService userAppService)
{
_userAppService = userAppService;
}
[HttpGet]
[Route("get-list-httpapi")]
public Task<List<UserDto>> GetList()
{
return _userAppService.GetList();
}
[HttpGet]
[Route("get-by-id-httpapi")]
public Task<UserDto> GetUserById(int Id)
{
return _userAppService.GetUserById(Id);
}
}
I can suggest a workaround as to enable only the APIs you need to appear on swagger (though the ones that don't appear anymore will still be available for consumption).
I would suggest you add a configuration part in your *.Http.Api project module inside your ConfigureSwaggerServices, like so:
context.Services.AddSwaggerGen(options =>
{
options.DocInclusionPredicate(
(_, apiDesc) =>
apiDesc
.CustomAttributes()
.OfType<IncludeInSwaggerDocAttribute>()
.Any());
});
And for the attribute, it would be very simple, like so:
[AttributeUsage(AttributeTargets.Class)]
public class IncludeInSwaggerDocAttribute : Attribute
{
}
This will let you achieve what you want, however I still recommend reading the doc carefully to be able to implement DDD.

.net Blazor Desktop folder picker?

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.

How to read appsettings.json in WPF Prism application

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;

can't open pdf with wpf webbrowser

I'm trying to create an app to open a local PDF file using web browser in WPF. However the file doesn't open properly, instead displays a grey blank screen. The code works perfectly fine when used to open a HTML file. Please help!
Code: webBrowser1.Navigate(#"file:///C:/Working/sample.pdf");
Note: I have adobe reader installed in my PC, if that is necessary. Is it?
WPF by default uses IE-based WebBrowser. In order to be able to view PDF-files, you must have a plugin installed into IE which can display PDF-files.
In addition to grey background, this is what can happen with a PC where IE doesn't have a PDF-plugin (Acrobat Reader etc) installed:
If you don't want to install plugins, one option to get around this issue is to use Windows 10 APIs to draw the PDF.
Other option is a 3rd party library, like CefSharp. Here's steps for using CefSharp:
First install Nuget CefSharp.WPF
Second, change XAML from the default WebBrowser to:
<wpf:ChromiumWebBrowser Loaded="ChromiumWebBrowser_Loaded" x:Name="Browser"></wpf:ChromiumWebBrowser>
Then create custom resolvers for CefSharp:
public class CustomProtocolSchemeHandler : ResourceHandler
{
public CustomProtocolSchemeHandler()
{
}
public override bool ProcessRequestAsync(IRequest request, ICallback callback)
{
return true;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}
Almost lastly, register the resolvers in App.xaml.cs:
public partial class App : Application
{
protected override void OnLoadCompleted(NavigationEventArgs e)
{
var settings = new CefSettings();
settings.RegisterScheme(new CefCustomScheme
{
SchemeName = CustomProtocolSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomProtocolSchemeHandlerFactory(),
IsCSPBypassing = true
});
settings.LogSeverity = LogSeverity.Error;
Cef.Initialize(settings);
}
}
Now everything should work:
More information about using CefSharp: https://www.codeproject.com/Articles/881315/Display-HTML-in-WPF-and-CefSharp-Tutorial-Part
I'll probably add a few changes to #Mikael's code (In case something didn't work out for you)
public class CustomProtocolSchemeHandler : ResourceHandler
{
public CustomProtocolSchemeHandler()
{
}
public override CefSharp.CefReturnValue ProcessRequestAsync(IRequest request, ICallback callback)
{
return CefSharp.CefReturnValue.Continue;
}
}
public class CustomProtocolSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "customFileProtocol";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
return new CustomProtocolSchemeHandler();
}
}

Selenium Webdriver C# - Running more than one test case in VS 2017 from Test Suite

I have been working on Page Object Framework which will have categorized test suites depending on the page. I have followed all steps in order to build a decent framework.
My each Unit Test, contains one method which follows simple steps. So far I have been able to create a few automated test cases. The issue began when I wanted to run more than one test case from the test suite. I have one chrome web driver instance which is in a separate class. Below is an example:
using System;
using System.Dynamic;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
namespace SystemUiAutomationTestFramework
{
public static class Browser
{
private static readonly IWebDriver _webDriver = new ChromeDriver(Properties.Settings.Default.ChromePathDriver);
public static IWebDriver WebDriver {
get { return _webDriver; }
}
public static ISearchContext Driver
{
get { return _webDriver; }
}
public static string Url
{
get { return _webDriver.Url; }
}
public static string Title
{
get { return _webDriver.Title; }
}
public static void Goto(string url)
{
_webDriver.Manage().Window.Maximize();
_webDriver.Url = url;
}
public static void Close()
{
_webDriver.Quit();
}
}
}
Each test case when it is run is independent and as good practice shows, there should be no test order implementation because it generates flows. I will place two examples which check simple login operation and login validation.
Below you can find a class for the login page:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.PageObjects;
using OpenQA.Selenium.Support.UI;
namespace SystemUiAutomationTestFramework
{
public class LoginPage
{
static string Url = "http://..";
private static string LoginUrl = "http://...";
private static string PageTitle = "System title page";
private static string LoginPlaceholder = "Login";
private static string PswdPlaceholder = "Password";
private static string ButtonText = "Sign in";
private static string ErrorMessage = "Wrong login or password";
/* LoginPage Elements */
[FindsBy(How = How.Id, Using = "inputLogin")]
private IWebElement inputLogin;
[FindsBy(How = How.Id, Using = "inputPassword")]
private IWebElement inputPassword;
[FindsBy(How = How.TagName, Using = "button")]
private IWebElement loginButton;
[FindsBy(How = How.ClassName, Using = "errorMsg")]
private IWebElement errorMessage;
/*----------------------------------------*/
public void Goto()
{
Browser.Goto(Url);
}
public bool IsAtLoginPage()
{
return Browser.Url == LoginUrl;
}
public bool IsAtLoginPageTitle()
{
return Browser.Title == PageTitle;
}
public bool IsAtLoginField()
{
return inputLogin.GetAttribute("placeholder") == LoginPlaceholder;
}
public bool IsAtPswdField()
{
return inputPassword.GetAttribute("placeholder") == PswdPlaceholder;
}
public bool IsAtLoginButton()
{
return loginButton.Text == ButtonText;
}
public void InputCredentials(string userName, string userPassword)
{
inputLogin.SendKeys(userName);
inputPassword.SendKeys(userPassword);
loginButton.Click();
}
public void WaitErrorMessage()
{
WebDriverWait wait = new WebDriverWait(Browser.WebDriver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(By.ClassName("errorMsg")));
}
public bool IsAtErrorMessage()
{
return errorMessage.Text == ErrorMessage;
}
public void ReportPageSuccessLogin()
{
Console.WriteLine("Application Url checked");
Console.WriteLine("Application Title checked");
Console.WriteLine("Login Field present");
Console.WriteLine("Password Field checked");
Console.WriteLine("Sign in button checked");
Console.WriteLine("Login ssuccess");
}
public void ReportPageValidationTest()
{
ReportPageSuccessLogin();
Console.WriteLine("Fake Credentials entered");
Console.WriteLine("Login Button Pressed");
Console.WriteLine("Login or Password validation message displayed: " + errorMessage.Displayed);
}
}
}
I also have an API class for Pagefactoring. As an example:
public static class Pages
{
public static LoginPage LoginPage
{
get
{
var loginPage = new LoginPage();
PageFactory.InitElements(Browser.Driver, loginPage);
return loginPage;
}
}
Now for the test cases, below an example when a user is on the login page, all elements are displaed, logs into the system, system checks if the user has logged into and closes the instance.
using System;
using System.Runtime.Remoting.Channels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SystemUiAutomationTestFramework;
namespace TestSuite.LoginPage
{
[TestClass]
public class LoginPageTest
{
[TestMethod]
public void Can_Go_LoginPage()
{
Pages.LoginPage.Goto();
Assert.IsTrue(Pages.LoginPage.IsAtLoginPage());
Assert.IsTrue(Pages.LoginPage.IsAtLoginPageTitle());
Assert.IsTrue(Pages.LoginPage.IsAtLoginField());
Assert.IsTrue(Pages.LoginPage.IsAtPswdField());
Assert.IsTrue(Pages.LoginPage.IsAtLoginButton());
Pages.LoginPage.InputCredentials(SettingsService.Username, SettingsService.Userpassword);
Pages.HomePage.IsAtHomePage();
Pages.LoginPage.ReportPageSuccessLogin();
}
[TestCleanup]
public void CleanUp()
{
Browser.Close();
}
}
}
The other one just validates the login page by providing fake login and password and if the error message is correctly displayed the test case finishes also by closing the instance.
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SystemUiAutomationTestFramework;
namespace TestSuite.LoginPage
{
[TestClass]
public class LoginPageValidationTest
{
[TestMethod]
public void Can_Validate_LoginPage()
{
Pages.LoginPage.Goto();
Assert.IsTrue(Pages.LoginPage.IsAtLoginPage());
Assert.IsTrue(Pages.LoginPage.IsAtLoginPageTitle());
Assert.IsTrue(Pages.LoginPage.IsAtLoginField());
Assert.IsTrue(Pages.LoginPage.IsAtPswdField());
Assert.IsTrue(Pages.LoginPage.IsAtLoginButton());
Pages.LoginPage.InputCredentials("abcd.efgh", "test123");
Pages.LoginPage.WaitErrorMessage();
Assert.IsTrue(Pages.LoginPage.IsAtErrorMessage());
Pages.LoginPage.ReportPageValidationTest();
}
[TestCleanup]
public void CleanUp()
{
Browser.Close();
}
}
}
Now the issue happens when I try those two test cases from the Login Page Test Suite, one finishes as passed the other one as failed. From the error message, I understand that it is because the other test case is trying to use an instance of the Webdriver which is already running.
I would like to know your opinion/guides/solution how can I solve this problem. I apologize for the long post but I thought that if I place my code it will be easy for you to understand my issue.
Best regards and thank you for your answers or linking me to the topic which either a duplicate of my issue or there is a solution already for it.
I was able to solve the issue on my own. I have created an
property for my driver class and an Initialize method which is called each time a test class is called. Also refactored my code to be more flexible.

Resources