I am working on a small Xamarin.Forms webview application. This is a follow up question to the one answered previously, xamarin-forms-making-webview-go-back
So I have a toolbar and a back button implemented and working. But when I run the program with the emulator already open(im using Genymotion), the program runs and shows the toolbar along with the back button...but no webview will display.
But heres the strange thing, sometimes when I run the program when the emulator is in sleep mode and then switch it back on the program works perfectly. Also, when I tested it on iOS it just showed the toolbar and no webview at all! More often then not, the emulator just wont show the webView. I have also tested this on my Android device and the same thing happens, itll show the toolbar but not the webview.
Abit confusing I know but can anyone help me out with this.
I will attach my code below:
App.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
namespace WebView_form
{
public class App : Application
{
public App()
{
//const string URL = "http://www.google.com";
MainPage = new NavigationPage(new WebPage());
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
WebPage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using Xamarin.Forms;
namespace WebView_form
{
public class WebPage : ContentPage
{
private WebView webView;
public WebPage()
{
webView = new WebView
{
Source = "https://www.google.com"
};
// toolbar
ToolbarItems.Add(new ToolbarItem("Back", null, () =>
{
webView.GoBack();
}));
Content = new StackLayout
{
Children = { webView }
};
}
}
}
If anyone can help me out, it'd be great.
Set the VerticalOptions to FillAndExpand and do the same for HorizontalOptions if that's not working.
Probably the WebView is getting a zero size height because when the layout happens the view is still empty.
So change the code in your WebPage.cs like this;
// ... Other code
public WebPage()
{
webView = new WebView
{
Source = "https://www.google.com",
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand
};
// toolbar
ToolbarItems.Add(new ToolbarItem("Back", null, () =>
{
webView.GoBack();
}));
Content = new StackLayout
{
Children = { webView }
};
}
// ... Other code
Another thing to consider: If you are responding to the WebView.Navigating event, be sure to not set its args.Cancel to true if the page loaded is the WevView.Source. The iOS implementation of WebView.Navigating fires when the WebView.Source is loaded but the Android implementation does not. If you set args.Cancel to true when the page in question is the WebView.Source, the WebView will be blank.
Set this into your Info.plist to bypass the App Transport Security check. Visit https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity for more info.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Note: My URL is already HTTPS, not sure why it's still blocked.
Related
I can't get OnNewIntent to fire. I've read dozens of articles on the issue and tried all combinations of code.
Regardless of whether I use LaunchMode.SingleTask or SingleTop it won't fire and always passes through the OnCreate method.
What am I doing wrong here? Am I missing something? What do I need to add to get it to work?
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Runtime;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
using static MyApp.ClipboardMgr;
namespace MyApp.Droid
{
//[Activity(Label = "SplashActivity")]
[Activity(LaunchMode = LaunchMode.SingleTask, Theme = "#style/Theme.Splash",
MainLauncher = true, NoHistory = true)]
//Can't get this to work with LaunchMode.SingleTop or SingleTask. Always creates a new instance.
[IntentFilter(new[] { Intent.ActionProcessText },
Categories = new[] { Intent.CategoryDefault },
DataMimeType = #"text/plain", Icon = "#drawable/icon", Label = "MyApp")]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
try
{
//taking these out for readability
//AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
//TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
//AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;
base.OnCreate(savedInstanceState);
// Create your application
StartActivity(typeof(MainActivity));
//don't want to do this here, better to do it in event
if (Intent.Action == Intent.ActionProcessText)
{
//always comes here
HandleProcessTextIntent();
}
}
catch(Exception e)
{
App.LogException(e);
throw;
}
}
/// <summary>
/// this is not firing!
/// </summary>
/// <param name="intent"></param>
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
HandleProcessTextIntent();
}
void HandleProcessTextIntent()
{
string input = Intent.GetStringExtra(Intent.ExtraProcessText).Trim();
if (input == string.Empty)
return;
ClipMgr.SetText(input);
}
}
}
You didn't miss anything since when I test with my own intent to start your activity, it works well and the OnNewIntent() is called when SingleTask or SingleTop used, here's how I call the activity from another project(I did this in native Android):
ComponentName component = new ComponentName("com.example.textHandlerDemo", "com.example.textHandlerDemo.TextHandlerActivity");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PROCESS_TEXT);
intent.setComponent(component);
startActivity(intent);
I did some research and find something that may explain why the OnNewIntent() never gets fired:
According to the guidance of ACTION_PROCESS_TEXT, it mentioned:
You can use this as a hint to offer the ability to return altered text to the sending app, replacing the selected text. This works as your Activity was actually started with startActivityForResult()
So, when you click your app's label in the floating text selection toolbar, Android will start your activity registered with ActionProcessText with startActivityForResult(), and by using startActivityForResult(), your activity will be started as a sub-activity of current activity(implied from Android doc of startActivityForResult), that is to say, rather than redirecting to your app which is already running, Android will put your activity into current app's stack.
This way, your target activity will still be called from OnCreate() since it's isolated from your running app.
Moreover, the floating text selection toolbar is used to provide a quick in-app experience for text operations like translation as mentioned in this blog, I didn't find any swich to enable/disable redirect to app, you may need to handle the intent in both OnCreate() and OnNewIntent() if you're using LaunchMode.SingleTask or SingleTop.
I have Xamarin.Android todo list mobile app using Prism.
The problem is:
In android system, I can create shortcut to open specific list in
app.
When I open app, and press home button, it remains on background
(thats ok)
When I then run app from desktop shortcut, it opens
android activity and when I create new PrismApplication (
LoadApplication(new App()); ) everything is running OK, but after
creating viewmodel for view, app is still using old viewmodel from
before.
I made this workaroud and I use same instance of PrismApplication:
static App xamApp;
protected override void OnCreate(Bundle bundle)
{
if (xamApp == null)
{
Forms.Init(this, bundle);
xamApp = new App();
}
LoadApplication(xamApp);
xamApp.Redirect(Intent.GetStringExtra("ListID"));
}
Now, problem is redirecting. This code:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
leads to the error:
System.InvalidOperationException: Master and Detail must be set before adding MasterDetailPage to a container.
Prism should take care of Binding of Detail in MasterDetailPage by the "TodoList" from NavigateAsync uri.
Does enyone know what can be the problem here?
So I finally got it working.
First I used LaunchMode = LaunchMode.SingleTask in my ActivityAttribute of MainActivity
[Activity(Label = "..", LaunchMode = LaunchMode.SingleTask, Icon = "#drawable/icon", Theme = "#style/MainTheme", MainLauncher = true]
public class MainActivity : FormsAppCompatActivity
Then I used OnNewIntent method of FormsAppCompatActivity so after app is on backgroud, only this event is launched :
protected override void OnNewIntent(Intent intent)
{
var listId = intent.GetStringExtra("ListID");
((App)App.Current).Redirect(listId);
}
Now even $"MainNavigationPage/TodoList?id={listId}" works
Based on the info you provided, I am assuming that when the app is launched again, it is already running, your previous MasterDetail page is already on the stack. IN your reset method, you want to reset your navigation stack to the new uri passing in the parameter. IN this case, you should use an absolute uri. This means try adding a "/" prefix to your uri. So something like this:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"/MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
Am new to CefSharp
I have created a class library project and referenced the CefSharp library to render the Web browser, However I am facing some issues showing the web Browser. Please find the exact code
WebBrowser_test1:
public partial class ChildWidget : Window
{
public CefSharp.Wpf.ChromiumWebBrowser webView;
public Widget()
{
InitializeComponent();
CefSharp.CefSettings settings = new CefSharp.CefSettings();
settings.PackLoadingDisabled = true;
if (CefSharp.Cef.Initialize(settings))
{
webView = new CefSharp.Wpf.ChromiumWebBrowser();
main_grid.Children.Add(webView);
webView.Address = "http://www.google.co.uk";
}
}
}
and I am referencing this library (dll) in another project
public MainWindow()
{
InitializeComponent();
Button newbutton = new Button();
newbutton.Width = 50;
main_grid.Children.Add(newbutton);
newbutton.Click += ButtonClick;
}
private void ButtonClick(object sender, RoutedEventArgs e)
{
try
{
Webbrowser_test1.ChildWidget childWidget = new Widget();
childWidget.Show();
}
catch (Exception)
{
throw;
}
}
Now on the Button click I will open the (WebBrowser_test1) child widget in which I will show the web browser .. when the window opens it is showing blank.
Please let me know if I missing anything
Subscribe to IsBrowserInitializedChanged after creating a ChromiumWebBrowser. Then once the browser is initialized you can call Load and your control will be displayed.
...
_browser = new ChromiumWebBrowser();
mainGrid.Children.Add(_browser);
_browser.IsBrowserInitializedChanged += OnIsBrowserInitializedChanged;
...
void OnIsBrowserInitializedChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (_browser.IsBrowserInitialized)
{
_browser.Load("https://www.google.com/");
}
}
I can think of the first three potential issues. But it's hard to tell what the real issue is from your code alone as it strays off a bit from the official examples
Move Cef.Initialize() to your MainWindow constructor. It should only be called once to launch the CefSharp.BrowserSubprocess.exe renderer process.
See my answer to CefSharp 3 always failing Cef.Initialize() for a few things to check regarding binaries and their placement. Really, the recommended approach is to start having the WPF example in the CefSharp.MinimalExample repo running first and then adjust to your use case from there.
I'm not sure a ChromiumWebBrowser() without explicitly setting a width and height works. A 0x0 window might not receive any rendered content. I haven't tried with recent code.
Have you tried replacing
webView.Address = "http://www.google.co.uk";
with
webView.Load("http://www.google.co.uk");
Like jornh mentions, you may have to explicitly set the height and width of the ChromiumWebBrowser. If you don't know the exact size, setting HorizontalAlignment and VerticalAlignment to Stretch (to fill the parent container) will probably also work.
Have you checked if the Cef.Initialize() actually returns true? You could be missing some files, and CefSharp doesn't always give clear error messages when this is the case.
What I Expect to Happen
The webpage opens inside the form.
What Really Happens
The webpage opens in the default browser (in this case Chrome).
The Code
The Navigate(string) snippet is copied directly from MSDN.
using System;
using System.Windows.Forms;
namespace BrowserFrame
{
public partial class BrowserForm: Form
{
public BrowserForm()
{
InitializeComponent();
Navigate("http://www.stackoverflow.com");
}
private void Navigate(String address)
{
if (String.IsNullOrEmpty(address)) return;
if (address.Equals("about:blank")) return;
if (!address.StartsWith("http://") &&
!address.StartsWith("https://"))
{
address = "http://" + address;
}
try
{
webBrowser1.Navigate(new Uri(address));
}
catch (System.UriFormatException)
{
return;
}
}
}
}
What I Did So Far
Changing the default browser (e.g. IE, Firefox) opens the page in the default browser.
Using webBrowser1.Navigate(new Uri("http://www.stackoverflow.com")); directly does the same thing.
Tried calling Navigate from other events (e.g. OnLoad, MouseClick); same result.
Update
Turns out this happens to all web-based UI controls. (Posted a question on superuser.)
I don't understand why it would open in an external browser, as it doesn't happen to me, but you can try this and see if it makes any difference.
Have you tried:
webBrowser1.Navigate(address); without using an Uri?
It works fine for me, and it doesn't need http:// or https://.
I guess they automated that in the class constructor for strings.
Or perhaps you could try changing to this simple version:
public BrowserForm()
{
InitializeComponent();
webBrowser1.Navigate("http://www.stackoverflow.com");
}
Just to see if it helps.
I try to play jpg (in loop), after click mp4 should be played after end, that jpg should play again. I dont know why but after I play in axWindowsMediaPlayer1_PlayStateChange vido play and then stop. Help.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Video
{
public partial class Form1 : Form
{
bool clicked = false;
public Form1()
{
InitializeComponent();
axWindowsMediaPlayer1.URL = "wait2.JPG";
}
private void axWindowsMediaPlayer1_PlayStateChange(object sender, AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (axWindowsMediaPlayer1.playState == WMPLib.WMPPlayState.wmppsMediaEnded & clicked== true)
{
clicked = false;
axWindowsMediaPlayer1.settings.setMode("Loop", true);
axWindowsMediaPlayer1.URL = "wait2.JPG";
axWindowsMediaPlayer1.Ctlcontrols.play();
}
}
private void axWindowsMediaPlayer1_ClickEvent(object sender, AxWMPLib._WMPOCXEvents_ClickEvent e)
{
axWindowsMediaPlayer1.settings.setMode("Loop", false);
axWindowsMediaPlayer1.URL = "video.MP4";
axWindowsMediaPlayer1.Ctlcontrols.play();
clicked = true;
}
}
}
I wish someone had replied to this question the time it was posted. It took me a lot of time to figure out why I was not able to start a new video by setting the URL property. I finally found the answer to this issue here:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd562470%28v=vs.85%29.aspx
The problem is with setting the URL property from within the axWindowsMediaPlayer1_PlayStateChange() event handler. According to the above msdn document:
"Do not call this method from event handler code. Calling URL from an event handler may yield unexpected results."
So the URL property has to be set outside of the even handler. I also tried Dispatcher.Invoke() and even starting a new thread from within the event handler to set the URL property; but that too did not help. It really has to come from outside of the event handler!