.net Blazor Desktop folder picker? - wpf

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.

Related

Using MudBlazor in WPF Blazor App (Hybrid Solution)

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?

Can Fontello glyph be used for Winforms button in a similar way as for a Xamarin Forms button?

In Xamarin.Forms using a glyph from a Fontello font is simple:
Download a font e.g. smiley.ttf.
Add to project as Embedded Resource
Export the font:
[assembly: ExportFont("smiley.ttf", Alias = "smiley")]
Use the glyph in xaml for the Text property:
<StackLayout BackgroundColor="#eeeeee">
<!--Uses glyph #E800 from smiley.ttf-->
<Button BorderColor="Aqua"
BackgroundColor="Yellow"
BorderWidth="5"
CornerRadius="10"
FontSize="150"
FontFamily="smiley"
Text=""
TextColor="Black"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
HeightRequest="200"
WidthRequest="200" />
</StackLayout>
And presto:
I would like to do the same thing in Winforms. Here's what I tried:
public MainForm()
{
InitializeComponent();
// For the sake of simplicity, the TTF is copied to output directory...
var path = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Fonts", "smiley.ttf");
// ... and loaded here.
privateFontCollection.AddFontFile(path);
var fontFamily = privateFontCollection.Families[0];
Debug.Assert(fontFamily.Name == "smiley", "Expecting 'smiley' is the font family name");
button1.Font = new Font(fontFamily, 12F);
button1.UseCompatibleTextRendering = true;
// Shows 'A'
// button1.Text = "A";
// Shows nothing.
button1.Text = "\u0E00";
}
PrivateFontCollection privateFontCollection = new PrivateFontCollection();
Is such a thing even possible? I tried various settings of button1.UseCompatibleTextRendering = true and Application.SetCompatibleTextRenderingDefault(true) without success.
The answer to the question: Can Fontello glyph be used for Winforms button in a similar way as for a Xamarin Forms button? is YES.
Thanks to Jimi for the comment pointing out my typo, and also for mentioning the necessity of disposal which I was also not aware of.
Here is the working code:
public partial class MainForm : Form
{
// https://stackoverflow.com/a/36509042/5438626
// https://learn.microsoft.com/en-us/dotnet/desktop/winforms/advanced/how-to-create-a-private-font-collection?view=netframeworkdesktop-4.8
public MainForm()
{
InitializeComponent();
// For the sake of simplicity, the TTF is copied to output directory...
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Fonts", "smiley.ttf");
// ... and loaded here.
privateFontCollection.AddFontFile(path);
var fontFamily = privateFontCollection.Families[0];
Debug.Assert(fontFamily.Name == "smiley", "Expecting 'smiley' is the font family name");
button1.Font = new Font(fontFamily, 12F);
button1.UseCompatibleTextRendering = true;
button1.Text = "\uE800";
button1.BackColor = Color.Yellow;
}
PrivateFontCollection privateFontCollection = new PrivateFontCollection();
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
privateFontCollection.Dispose();
}
base.Dispose(disposing);
}
}

Winforms WebBrowser control without IE popups not appearing [duplicate]

I am trying to implement a simple web browser control in one of my apps. This is to help integrate a web app into a toolset i am creating.
The problem is, this web app absolutly loves popup windows....
When a popup is opened, it opens in an IE window which is not a child of the MDI Container form that my main window is part of.
How can i get any and all popups created by clicking links in my WebBrowser to be a child of my MDI container (similar to setting the MDIParent property of a form)?
Thanks in advance.
The web browser control supports the NewWindow event to get notified about a popup window. The Winforms wrapper however does not let you do much with it, you can only cancel the popup. The native COM wrapper permits passing back a new instance of the web browser, that instance will then be used to display the popup.
Taking advantage of this requires some work. For starters, use Project + Add Reference, Browse tab and select c:\windows\system32\shdocvw.dll. That adds a reference to the native COM interface.
Create a form that acts as the popup form. Drop a WebBrowser on it and make its code look similar to this:
public partial class Form2 : Form {
public Form2() {
InitializeComponent();
}
public WebBrowser Browser {
get { return webBrowser1; }
}
}
The Browser property gives access to the browser that will be used to display the web page in the popup window.
Now back to the main form. Drop a WebBrowser on it and make its code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
webBrowser1.Url = new Uri("http://google.com");
}
SHDocVw.WebBrowser nativeBrowser;
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
nativeBrowser = (SHDocVw.WebBrowser)webBrowser1.ActiveXInstance;
nativeBrowser.NewWindow2 += nativeBrowser_NewWindow2;
}
protected override void OnFormClosing(FormClosingEventArgs e) {
nativeBrowser.NewWindow2 -= nativeBrowser_NewWindow2;
base.OnFormClosing(e);
}
void nativeBrowser_NewWindow2(ref object ppDisp, ref bool Cancel) {
var popup = new Form2();
popup.Show(this);
ppDisp = popup.Browser.ActiveXInstance;
}
}
The OnLoad method obtains a reference to the native COM interface, then subscribes an event handler to the NewWindow2 event. I made sure to unsubscribe that event in the FormClosing event handler, not 100% sure if that's necessary. Better safe then sorry.
The NewWindow2 event handler is the crux, note that the first argument allows passing back an untyped reference. That should be the native browser in the popup window. So I create an instance of Form2 and Show() it. Note the argument to Show(), that ensures that the popup is an owned window. Substitute this as necessary for your app, I assume you'd want to create an MDI child window in your case.
Do beware that this event doesn't fire for the window displayed when Javascript uses alert(). The browser doesn't treat that window as an HTML popup and doesn't use a browser window to display it so you cannot intercept or replace it.
I found that the best way to do this was to implement/sink the NewWindow3 event
Add the reference to c:\windows\system32\shdocvw.dll as mentioned in the other answers here.
Add event handler
SHDocVw.WebBrowser wbCOMmain = (SHDocVw.WebBrowser)webbrowser.ActiveXInstance;
wbCOMmain.NewWindow3 += wbCOMmain_NewWindow3;
Event method
void wbCOMmain_NewWindow3(ref object ppDisp,
ref bool Cancel,
uint dwFlags,
string bstrUrlContext,
string bstrUrl)
{
// bstrUrl is the url being navigated to
Cancel = true; // stop the navigation
// Do whatever else you want to do with that URL
// open in the same browser or new browser, etc.
}
Set "Embed Interop Types" for the "Interop.SHDocVw" assembly to false
Set the "local copy" to true.
Source for that help MSDN Post
Refining Hans answer, you can derive the WebBrowser for accessing the COM without adding the reference. It is by using the unpublished Winforms WebBrowser.AttachInterface and DetachInterface methods.
More elaborated here.
Here is the code:
Usage (change your WebBrowser instance to WebBrowserNewWindow2)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.webBrowser1.NewWindow2 += webBrowser_NewWindow2;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
webBrowser1.NewWindow2 -= webBrowser_NewWindow2;
base.OnFormClosing(e);
}
void webBrowser_NewWindow2(object sender, WebBrowserNewWindow2EventArgs e)
{
var popup = new Form1();
popup.Show(this);
e.PpDisp = popup.Browser.ActiveXInstance;
}
public WebBrowserNewWindow2 Browser
{
get { return webBrowser1; }
}
}
Code:
using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace SHDocVw
{
public delegate void WebBrowserNewWindow2EventHandler(object sender, WebBrowserNewWindow2EventArgs e);
public class WebBrowserNewWindow2EventArgs : EventArgs
{
public WebBrowserNewWindow2EventArgs(object ppDisp, bool cancel)
{
PpDisp = ppDisp;
Cancel = cancel;
}
public object PpDisp { get; set; }
public bool Cancel { get; set; }
}
public class WebBrowserNewWindow2 : WebBrowser
{
private AxHost.ConnectionPointCookie _cookie;
private WebBrowser2EventHelper _helper;
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void CreateSink()
{
base.CreateSink();
_helper = new WebBrowser2EventHelper(this);
_cookie = new AxHost.ConnectionPointCookie(
this.ActiveXInstance, _helper, typeof(DWebBrowserEvents2));
}
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void DetachSink()
{
if (_cookie != null)
{
_cookie.Disconnect();
_cookie = null;
}
base.DetachSink();
}
public event WebBrowserNewWindow2EventHandler NewWindow2;
private class WebBrowser2EventHelper : StandardOleMarshalObject, DWebBrowserEvents2
{
private readonly WebBrowserNewWindow2 _parent;
public WebBrowser2EventHelper(WebBrowserNewWindow2 parent)
{
_parent = parent;
}
public void NewWindow2(ref object pDisp, ref bool cancel)
{
WebBrowserNewWindow2EventArgs arg = new WebBrowserNewWindow2EventArgs(pDisp, cancel);
_parent.NewWindow2(this, arg);
if (pDisp != arg.PpDisp)
pDisp = arg.PpDisp;
if (cancel != arg.Cancel)
cancel = arg.Cancel;
}
}
[ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
TypeLibType(TypeLibTypeFlags.FHidden)]
public interface DWebBrowserEvents2
{
[DispId(0xfb)]
void NewWindow2(
[In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppDisp,
[In, Out] ref bool cancel);
}
}
}
I know the question is very old but I solved it this way: add new reference, in COM choose Microsoft Internet Controls and in the code, before the click that opens a new window add the following:
SHDocVw.WebBrowser_V1 axBrowser = (SHDocVw.WebBrowser_V1)webBrowser1.ActiveXInstance;
axBrowser.NewWindow += axBrowser_NewWindow;
and then add the following method:
void axBrowser_NewWindow(string URL, int Flags, string TargetFrameName, ref object PostData, string Headers, ref bool Processed)
{
Processed = true;
webBrowser1.Navigate(URL);
}

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();
}
}

Open new Window from view model

Hi I have a beginner problem. I have shell (it is wpf window) and in this shell is screen (it is an user control / view model).
I would like open new window from view model, not show user control in shell.
So I create new window - ChatView
<Window x:Class="Spirit.Views.ChatView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:extToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit.Extended" Title="ChatView" Height="545" Width="763">
<Grid Margin="4,4,4,4">
</Grid>
</Window>
Export ChatViewModel with MEF.
public interface IChatViewModel
{
}
[Export("ChatScreen",typeof(IChatViewModel))]
public class ChatViewModel
{
}
In view model I have this method:
With ShowScreen class help me Mr.Marco Amendola. It look likes this:
public class ShowScreen : IResult
{
readonly Type _screenType;
readonly string _name;
[Import]
public IShellViewModel Shell { get; set; }
Action<object> _initializationAction = screen => { };
public ShowScreen InitializeWith<T>(T argument)
{
_initializationAction = screen =>
{
var initializable = screen as IInitializable<T>;
if (initializable != null)
initializable.Initialize(argument);
};
return this;
}
public ShowScreen(string name)
{
_name = name;
}
public ShowScreen(Type screenType)
{
_screenType = screenType;
}
public void Execute(ActionExecutionContext context)
{
var screen = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_screenType, null);
_initializationAction(screen);
Shell.ActivateItem(screen);
Completed(this, new ResultCompletionEventArgs());
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowScreen Of<T>()
{
return new ShowScreen(typeof(T));
}
}
My problem is if I try show new window it doesn’t works, it works only if I show new user control in shell(window).
I would like achieve behavior something like in skype. You have a main window with listbox, you double clicked on item and it show new chat window.
Main window can publish with EventAggregator on chat window and also chat window can publish on main window. This is my goal.
I know that I can not use class ShowScreen on showing new Window. I would like to know what is correct way to create new window from view model and inject event aggregator
to this vie model.
Any advice? Thank for your help and time.
Have you looked at WindowManager.Show or WindowManager.ShowDialog? Rob has a sample at http://caliburnmicro.codeplex.com/wikipage?title=The%20Window%20Manager. You can inject this dependency into your view model as IWindowManager.
I'm using this. Maybe could save a question about "where's the code ?".
DialogHelper:
public class DialogHelper
{
public void ShowDialog<T>(params Object[] param) where T : class
{
var windowManager = new WindowManager();
T viewModel = Activator.CreateInstance(typeof(T), param) as T;
windowManager.ShowWindow(viewModel);
}
}
How to use:
Without constructor parameter:
Dialog.ShowDialog<TestTableViewModel>();
With constructor paramater:
Dialog.ShowDialog<TestTableViewModel>(dt);
Note that I'm not using MEF

Resources