I recently upgraded the cefsharp wpf from 69.0.0 to 71.0.0 and creating the browser using the managedcefbrowseradapter has changed and when using IWindowInfo as parameter the browser is not getting rendered . Earlier when just passing the window handle was working properly. Any breaking changes here which i have to adapt?
Working one - 69.0.0
_managedCefBrowserAdapter.CreateBrowser(BrowserSettings, RequestContext,_window.Handle, address);
not working - 71.0.0
IWindowInfo window = new WindowInfo();
window.WindowHandle = _window.Handle;
_managedCefBrowserAdapter.CreateBrowser(window, BrowserSettings, RequestContext, address);
Edited to add more details.
The CEF browser is hosted within WPF control which is inherited from System.Windows.Controls. The project uses cefsharp.wfp
The WPF control handle is stored in ControlHandle property .
Cef is initialized using the CefSettings from cesharp.wpf namespace.
CefSettings settings = new CefSettings
{
CachePath="xxxxx",
PersistSessionCookies = true,
LogSeverity = LogSeverity.Default,
LogFile = Path.Combine(logsFolder, "xxxxx.log"),
Locale = "en-US",
ResourcesDirPath = "xxxxxx"
}
Cef.Initialize(settings);
We have a custom class CefBrowser which implements cefsharp.internals.IWebBrowserInternal and uses ManagedCefBrowserAdapter to create the cefBrowser.
NativeWindow window = new NativeWindow()
window.CreateHandle(new CreateParams());
IWindowInfo infowindow = new WindowInfo();
infowindow.WindowHandle = window.Handle;
A native window would be created and that would be set as the window handle.
ManagedCefBrowserAdapter createbrowser would be created with the window handle.
_managedCefBrowserAdapter.CreateBrowser(infowindow , BrowserSettings, RequestContext, address);
One the browser is created , In OnAfterBrowserCreated method, SetParent API would be called to set the parent of cefsharp browser host to the WPF control .
void IWebBrowserInternal.OnAfterBrowserCreated(IBrowser browser)
{
_browser = browser;
SetParent(browser.GetHost().GetWindowHandle(), ControlHandle);
ResizeBrowser(Size); //WPF control size
OnBrowserCreated(new EventArgs());
}
#endregion
#region Private Members
private void OnBrowserCreated(EventArgs args)
{
EventHandler<EventArgs> handler = BrowserCreated;
if (handler != null)
{
handler(this, args);
}
}
This was working till 69.0.0 version with the call _managedCefBrowserAdapter.CreateBrowser(BrowserSettings, RequestContext,window.Handle, address).
With 71.0.0 it is broken with the call using IWindowInfo and no pages are rendered.
Related
I'm trying to get HTML file upload control working on WASM. So far I've tried to do the following:
[HtmlElement("input")]
public class FilePickerView : FrameworkElement
{
public FilePickerView()
{
// XAML behavior: a non-null background is required on an element to be "visible to pointers".
// Uno reproduces this behavior, so we must set it here even if we're not using the background.
// Not doing this will lead to a `pointer-events: none` CSS style on the control.
Background = new SolidColorBrush(Colors.Transparent);
this.SetHtmlAttribute("type", "file");
}
}
And then in the view:
<wasm:FilePickerView Height="35" Width="300" x:Name="filePicker" HorizontalAlignment="Left" />
I get the control displayed, I can click on it and it displays the name of the file I've selected.
I am pretty lost after this.
I'd like to be able to do two things:
Access file path in code behind.
Send file contents to code behind for processing.
Would appreciate any pointers on this.
I've been through the following pages in the documentation:
(Wasm) Handling custom HTML events - https://qa.website.platform.uno/docs/articles/wasm-custom-events.html
Embedding Existing JavaScript Components Into Uno-WASM - Part 1 - https://qa.website.platform.uno/docs/articles/interop/wasm-javascript-1.html
Embedding Existing JavaScript Components Into Uno-WASM - Part 2 - https://qa.website.platform.uno/docs/articles/interop/wasm-javascript-2.html
Embedding Existing JavaScript Components Into Uno-WASM - Part 3 - https://qa.website.platform.uno/docs/articles/interop/wasm-javascript-3.html
After connecting pieces from the internet, I came up with this method.
However, it only works with files max 500 kb big.
To enable large file upload I had to upgrade wasm target to .net 5 and use developer versions (2.0.0-dev.167) of Uno.Wasm.Bootstrap and Uno.WasmBootstrap.DevServer (how to upgrade target is described here).
In this code I enabled upload of only .wav audio files
private async void uploadBtn_Click(object sender, RoutedEventArgs e)
{
FileSelectedEvent -=OnFileUploadedEvent;
FileSelectedEvent += OnFileUploadedEvent;
WebAssemblyRuntime.InvokeJS(#"
var input = document.createElement('input');
input.type = 'file';
input.accept = '.wav';
input.onchange = e => {
var file = e.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = readerEvent => {
//this is the binary uploaded content
var content = readerEvent.target.result;
//invoke C# method to get audio binary data
var selectFile = Module.mono_bind_static_method(" + "\"[MyApp.Wasm] MyApp.Shared.MyPage:SelectFile\""+#");
selectFile(content);
};
};
input.click(); "
);
}
public static void SelectFile(string fileAsDataUrl) => FileSelectedEvent?.Invoke(null, new FileSelectedEventHandlerArgs(fileAsDataUrl));
private void OnFileUploadedEvent(object sender, FileSelectedEventHandlerArgs e)
{
FileSelectedEvent -= OnFileUploadedEvent;
var base64Data = Regex.Match(e.FileAsDataUrl, #"data:audio/(?<type>.+?),(?<data>.+)").Groups["data"].Value;
var binData = Convert.FromBase64String(base64Data); //this is the data I want
Console.Out.WriteLine("I got binary data of uploaded file");
}
private static event FileSelectedEventHandler FileSelectedEvent;
private delegate void FileSelectedEventHandler(object sender, FileSelectedEventHandlerArgs args);
private class FileSelectedEventHandlerArgs
{
public string FileAsDataUrl { get; }
public FileSelectedEventHandlerArgs(string fileAsDataUrl) => FileAsDataUrl = fileAsDataUrl;
}
Also I was not able to run it with SQLite at the same time. Sadly, I still haven't figured out why.
Sources:
https://github.com/unoplatform/uno/issues/508
https://github.com/unoplatform/uno/issues/3525
https://platform.uno/blog/uno-platform-3-2-net-5-c-9-support-and-net-5-webassembly-aot-support/
EDIT: Appareantly SQLite for .NET 5/6 is still work in progress and there are some packages that need changes.
I have a cross-platform mobile app (Android/iOS) which implements the generic WebView control. This works well for most circumstances, but some iOS users complain that, when attempting to load a certain resource-intensive web page, the app "goes black" and then focus returns to the main menu view. My suspicion is that the app is choking due to the amount of content and processing overhead of the web page, but frankly this is a blind guess and I don't have the resources (such as an iPhone at my disposal) in order to verify this. Using an iPhone simulator on a Mac does not reproduce the "black screen" issue.
Therefore, I am attempting to implement in parallel WkWebView for iOS devices at version 8.0 and above as this is presumably more performant and might alleviate the problem. It is just about working, but there seems to be a disconnect between the ViewController and ContentPage which is supposed to host the WkWebView control which I have been unable to rectify.
Below is the general implementation:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class WkWebViewPage : ContentPage
{
public WkWebViewPage (string url, string title)
{
InitializeComponent();
Title = title;
App.GetWkWebView(this, url);
}
}
The markup for WkWebPageView has no inner content.
The method App.GetWkWebView is a delegate of type Action implemented as a static property in the main App class. This is assigned in the FinishedLaunching method of the AppDelegate class (iOS project) to a static method in the static class I am using to manage invoking the WkWebView. This class is implemented as such:
public static class WkWebViewController
{
private static WKWebView _wkWebView;
public static void GetWkWebView(Page parentView, string url)
{
if(_wkWebView == null)
{
// INSERT ATTEMPTED APPROACHES BELOW HERE
var frame = view.Frame;
var cgRect = new CoreGraphics.CGRect(frame.X, frame.Y, frame.Width, frame.Height);
_wkWebView = new WKWebView(cgRect, new WKWebViewConfiguration());
view.AddSubview(_wkWebView);
// NavigationDelegate is a custom class; not germane to the issue
_wkWebView.NavigationDelegate = new NavigationDelegate();
}
var nsUrl = new NSUrl(url);
var request = new NSUrlRequest(nsUrl);
_wkWebView.LoadRequest(request);
}
}
Here is where the trouble begins. I have tried two approaches to obtaining the appropriate ViewController -- and more pertinently, the UIView object:
1)
var renderer = Platform.GetRenderer(parentView);
if (renderer == null)
{
renderer = Platform.CreateRenderer(parentView);
Platform.SetRenderer(parentView, renderer);
}
var view = renderer.ViewController.View;
This results in the following:
(source: aquaspy.com)
The content area is white/blank. The http request is submitted successfully as a 200 response is received. Note that the navigation bar above the content area properly displays.
2)
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
var view = vc.View;
Which results in:
(source: aquaspy.com)
In this case, the web page displays; however, the WkWebView control takes up the entire screen, obscuring the navigation bar (and seemingly the Status Bar).
Any suggestions would be greatly appreciated!
I have tried FileUpload referring GWT source doc. Since I wanted to add it on different tab I have created GWT page for that and added FileUpload over there.
Not implmented entryPoint since its been implemented in their root page.
I am not using onModuleLoad method I am just creating method to display element and adding it to FormPanel.
I am able to submit POST request but not able to capture File on servlet. Am I doing something wrong at GWT side or servlet Side.
I have used similar kind of code at GWT side
public class FormPanelExample implements Composite {
public void FormPanelExample() {
// Create a FormPanel and point it at a service.
final FormPanel form = new FormPanel();
form.setAction("/myFormHandler");
// Because we're going to add a FileUpload widget, we'll need to set the
// form to use the POST method, and multipart MIME encoding.
form.setEncoding(FormPanel.ENCODING_MULTIPART);
form.setMethod(FormPanel.METHOD_POST);
// Create a panel to hold all of the form widgets.
VerticalPanel panel = new VerticalPanel();
form.setWidget(panel);
// Create a TextBox, giving it a name so that it will be submitted.
final TextBox tb = new TextBox();
tb.setName("textBoxFormElement");
panel.add(tb);
// Create a ListBox, giving it a name and some values to be associated with
// its options.
ListBox lb = new ListBox();
lb.setName("listBoxFormElement");
lb.addItem("foo", "fooValue");
lb.addItem("bar", "barValue");
lb.addItem("baz", "bazValue");
panel.add(lb);
// Create a FileUpload widget.
FileUpload upload = new FileUpload();
upload.setName("uploadFormElement");
panel.add(upload);
// Add a 'submit' button.
panel.add(new Button("Submit", new ClickHandler() {
public void onClick(ClickEvent event) {
form.submit();
}
}));
// Add an event handler to the form.
form.addSubmitHandler(new FormPanel.SubmitHandler() {
public void onSubmit(SubmitEvent event) {
// This event is fired just before the form is submitted. We can take
// this opportunity to perform validation.
if (tb.getText().length() == 0) {
Window.alert("The text box must not be empty");
event.cancel();
}
}
});
form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
public void onSubmitComplete(SubmitCompleteEvent event) {
// When the form submission is successfully completed, this event is
// fired. Assuming the service returned a response of type text/html,
// we can get the result text here (see the FormPanel documentation for
// further explanation).
Window.alert(event.getResults());
}
});
RootPanel.get().add(form);
}
}
At Servlet side
if (!ServletFileUpload.isMultipartContent(request)) {
throw new FileUploadException("error multipart request not found");
}
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
if (items == null) {
response.getWriter().write("File not correctly uploaded");
return;
}
Iterator<FileItem> iter = items.iterator();
When I am calling iter.next(), it gives error no such elementFound Exception
By exception it looks to be on submit file is not submitting to servlet request.
Try using postman to call the endpoint and upload a file directly to your running Servlet to ensure that is working correctly.
I checked my own implementation of this code and it almost matches yours exactly, except I'm not using anything but the FileUpload on the panel. Remove the TextBox and ListBox so we can check that just the file part is working on its own, then introduce each item and test it separately.
I found this to be more reliable on the server side
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
FileItemIterator iter = servletFileUpload.getItemIterator(request);
Our company would like to give a pre-compiled version of our web application to a 3rd party so they can add their own pages and modules to it.
In trying to accomplish this, I've so far done the following:
Compiled our main web app as a Web Deployment Project
Created a POC web app which references the DLL resulting from step 1 above.
I then added the following static method to our main web app, which should hopefully process requests to its pre-compiled aspx pages:
public static bool TryProcessRequest(HttpContext context)
{
string rawUrl = context.Request.RawUrl;
int aspxIdx = rawUrl.IndexOf(".aspx");
if (aspxIdx > 0)
{
string aspxPagePath = rawUrl.Substring(0, aspxIdx + 5);
string aspxPageClassName = aspxPagePath.Substring(1).Replace('/','_').Replace(".aspx","");
Assembly website = Assembly.GetAssembly(typeof(MCLLogin));
Type pageClass = website.GetType(aspxPageClassName);
ConstructorInfo ctor = pageClass.GetConstructor(new Type[] { });
IHttpHandler pageObj = (IHttpHandler)ctor.Invoke(new object[] { });
context.Server.Execute(pageObj, context.Response.Output, false);
//alternative: invoking the page's ProcessRequest method - same results
//System.Reflection.MethodInfo method = pageClass.GetMethod("ProcessRequest");
//method.Invoke(pageObj, new object[] { context });
return true;
}
return false; //not handled
}
I am then calling this method in the ProcessRequest() method of a HttpHandler of the POC web app whenever I want our main web app to handle the request. This code indeed successfully instantiates a page of the correct class and starts to process the request.
The problem:
Code in my Page_PreLoad handler throws an exception because Page.Form is null. I've also found out the Page.Controls collection is empty.
What am I doing wrong? Should I go down a different path to achieve this?
I have a SWf application built in flex 4. One part of the application relies on accessing a public variable ("step1") set at the application root, and is accessed with
var app:Object = FlexGlobals.topLevelApplication;
trace("step one is "+app.step1);
This, while not optimal, has worked fine. Now, hoever, I need to load this entire application into another application, and I can't figure out how to access my step1 variable any longer.
I have been loading the swf into the new parent application like so:
public var myLoader:Loader = new Loader();
public var pizzaContainer:UIComponent = new UIComponent();
private var myUrl:URLRequest = new URLRequest("chickensoup.swf");
protected function application1_creationCompleteHandler(event:FlexEvent):void {
myLoader.load(myUrl);
pizzaContainer.addChild(myLoader);
addElement(pizzaContainer);
}
And I have tried using FlexGlobals.topLevelApplication.pizzaContainer to no avail. What would be the method of acessing a public variable at the root of the loaded application?
The SWF Loader has a content property which is available after the Loader Completed event has been raised, you can use this to access any exposed property in the loaded SWF
public var loadedSM:SystemManager;
// Initialize variables with information from
// the loaded application.
private function initNestedAppProps():void {
loadedSM = SystemManager(myLoader.content);
}
Now try get values referencing like this:
loadedSM.application["lblOne"].text
loadedSM.application["step1"]
Remember to make sure these are public variables!