Non-static field, method, or property - c#

Looking at this How to show loading image or progress bar on WebView it clearly says that the following approach should work but I am having an issue as I cannot access my ProgressBar in public override void OnPageFinished(WebView view, string url)
My Code is the following
webView.Settings.JavaScriptEnabled = true;
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.UseWideViewPort = true;
webView.SetWebViewClient(new WebViewClientClass());
webView.LoadDataWithBaseURL("file:///android_asset/", HTML_DATA, "text/html", "UTF-8", null);
Where my WebViewClientClass is the following
#region Webview URL handler
internal class WebViewClientClass : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
Android.Net.Uri url = request.Url;
view.LoadUrl(url.ToString());
return true;
}
public override void OnPageStarted(WebView view, string url, Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
Log.Info("101028", "loading started");
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
Log.Info("101028", "loading finised");
PB.Visibility = ViewState.Gone; ***<<<<<<<------ ERROR HERE***
}
}
#endregion
I can see the results in logcat but when I try to access my ProgressBar such as PB.Visibility = ViewState.Gone in OnPageFinished method - I get the following error
ERROR
An object reference is required for the non-static field, method, or property
PogressBar Code
private ProgressBar PB;
protected override async void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Read);
PB = FindViewById<ProgressBar>(Resource.Id.MyProgressBar);
}
Anyone knows what is the right approach for this? How do i make this work
Cheers

From your code it seems that your ProgressBar belongs to your Activity and you are trying to access it in WebViewClientClass that's why your object reference is missing.
So you should have a method inside your activity class which will hide/show ProgressBar and there must be some kind of call back mechanism to call this method
Define an interface like below
interface ProgressBarHandler{
public void hideProgress();
}
And in your activity class implement this interface
class MyActivity extends Activity implements ProgressBarHandler{
//other usual things in your activity
protected override async void OnCreate(Bundle savedInstanceState){
//here pass the activity instance to WebViewClientClass
webView.SetWebViewClient(new WebViewClientClass(this));
}
public void hideProgress(){
PB.Visibility = ViewState.Gone;
}
}
AND finally in your WebViewClientClass
class WebViewClientClass : WebViewClient
{
private ProgressBarHandler handler;
public WebViewClientClass(ProgressBarHandler handler){
this.handler = handler;
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
Log.Info("101028", "loading finised");
handler.hideProgress();
}
}

Related

Progressbar while loading webview in xamarin android

I want to show a progressbar while the webview gets loaded and hide the progessbar when the webview gets loaded completely.
Here is my Activity.cs
progress = new ProgressDialog(this);
progress.Indeterminate = true;
progress.SetProgressStyle(Android.App.ProgressDialogStyle.Spinner);
progress.SetMessage("Loading... Please wait...");
progress.SetCancelable(false);
progress.Show();
_faq = FindViewById<Android.Webkit.WebView>(Resource.Id.wv_FAQ);
_web.AppWebViewClients(progress);
_web.ShouldOverrideUrlLoading(_faq, _url);
_web.OnPageFinished(_faq, _url);
here is my ResourceWebView.cs
public class ResourceWebView: WebViewClient
{
Android.App.ProgressDialog progress;
public void AppWebViewClients(ProgressDialog progressBar)
{
this.progress = progressBar;
progress.Show();
}
public bool ShouldOverrideUrlLoading(WebView view, String url)
{
view.LoadUrl(url);
progress.Show();
return true;
}
public void OnPageFinished(WebView view, String url)
{
OnPageFinished(view, url);
progress.Hide();
}
}
But this code is not work for me..
Try this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
urlEditText = (EditText) findViewById(R.id.urlField);
webView = (WebView) findViewById(R.id.webView);
webView.setWebViewClient(new MyWebViewClient());
progress = (ProgressBar) findViewById(R.id.progressBar);
progress.setVisibility(View.GONE);
Button openUrl = (Button) findViewById(R.id.goButton);
openUrl.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
String url = urlEditText.getText().toString();
if (validateUrl(url)) {
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(url);
}
}
private boolean validateUrl(String url) {
return true;
}
});
}
private class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
progress.setVisibility(View.GONE);
WebViewActivity.this.progress.setProgress(100);
super.onPageFinished(view, url);
}
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
progress.setVisibility(View.VISIBLE);
WebViewActivity.this.progress.setProgress(0);
super.onPageStarted(view, url, favicon);
}
}
Reference this for more reference : http://stacktips.com/tutorials/android/progressbar-while-loading-webview

InstantiateViewController calls ViewDidLoad

I have a classic master-detail logic and trying to create instance of detail UIViewController with InstantiateViewController inside a click event.
Here is my code,
MasterViewController.cs
detailButton += delegate {
UIStoryboard Storyboard = UIStoryboard.FromName ("Main", null);
Console.WriteLine("InstantiateViewController() started");
var profileController = Storyboard.InstantiateViewController("ProfileController") as ProfileController;
Console.WriteLine("InstantiateViewController() ended");
profileController.Id = 5;
};
ProfileViewController.cs
public partial class ProfileController : UIViewController
{
public int Id { get; set; }
public override void ViewDidLoad()
{
Console.WriteLine("ViewDidLoad() called");
}
}
When I click the button output is,
InstantiateViewController() started
ViewDidLoad() called
InstantiateViewController() ended
This means profileController.Id is set after ViewDidLoad() which means I can't load data by Id in ViewDidload event beacuse Id is null.
So my question is why ViewDidLoad() called by InstantiateViewController(), in which method should I load data by Id?
Thanks.
VoewDidLoad is called when the ViewController is loaded into memory.
So, the correct place to get the data is on ViewDidAppear.
ViewDidAppear notifies the ViewController that its view was added to a view hierarchy.
UPDATE:
Based on the new information provided in comments you could do something like this:
public partial class ProfileController : UIViewController
{
private int _id;
public void SetupProfile (int id)
{
// Save the Id if necessary.
_id = id;
// Add event with this id related.
}
public override void ViewDidLoad()
{
Console.WriteLine("ViewDidLoad() called");
}
}
In alternative if you still want to do the event setup in ViewDidAppear you could use this approach with the events:
yourClass.Event -= MyHandler;
yourClass.Event += MyHandler;
I would do this via a custom segue.
1) Create a custom segue that can be re-used throughout your app:
public class CustomSeque : UIStoryboardSegue // or UIStoryboardPopoverSegue depending upon UI design so you can "POP" controller
{
public CustomSeque(String identifier, UIViewController source, UIViewController destination) : base (identifier, source, destination) { }
public override void Perform()
{
if (Identifier == "StackOverflow")
{
// Are you using a NavigationController?
if (SourceViewController.NavigationController != null)
SourceViewController.NavigationController?.PushViewController(DestinationViewController, animated: true);
else
SourceViewController.ShowViewController(DestinationViewController, this);
} else
base.Perform();
}
}
2) Then you can:
UIStoryboard Storyboard = UIStoryboard.FromName("Main", null);
Console.WriteLine("InstantiateViewController() started");
var profileController = Storyboard.InstantiateViewController("ProfileController") as ProfileController;
var seque = new CustomSeque($"StackOverflow", this, profileController);
profileController.Id = 5;
profileController.PrepareForSegue(seque, this); // instead of *this*, you can pass any NSObject that contains data for your controller
seque.Perform();
Console.WriteLine("InstantiateViewController() ended");
If your ProfileController looks like this:
public partial class ProfileController : UIViewController
{
public ProfileController (IntPtr handle) : base (handle)
{
Id = -99;
}
public int Id { get; set; }
public override bool ShouldPerformSegue(string segueIdentifier, NSObject sender)
{
if (segueIdentifier == "StackOverflow")
return true;
return base.ShouldPerformSegue(segueIdentifier, sender);
}
[Export("prepareForSegue:sender:")]
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue(segue, sender);
Console.WriteLine("ProfileController.PrepareForSegue()");
Console.WriteLine($" - ID = {Id}");
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Console.WriteLine("ProfileController.ViewDidLoad()");
Console.WriteLine($" - ID = {Id}");
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
Console.WriteLine("ProfileController.ViewWillAppear()");
Console.WriteLine($" - ID = {Id}");
}
}
Your sequenced output would be:
InstantiateViewController() started
ProfileController.PrepareForSegue()
- ID = 5
ProfileController.ViewDidLoad()
- ID = 5
InstantiateViewController() ended
ProfileController.ViewWillAppear()
- ID = 5

Getting System.InvalidCastException in c#

I'm trying to access global activity variables (which I can't make as static) from a BroadcastReceiver . For that, I create a instance of the activity this way:
class wifiReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
MainActivity activity = (MainActivity)context.ApplicationContext;
...
But i get System.InvalidCastException: Specified cast is not valid. in instance creation line. What am i doing wrong?
EDIT: Some code of my activity
public class MainActivity : Activity
{
private WifiManager _manager;
private List<string> _wifiSignals;
private wifiReceiver _wifiReceiver;
private TextView _Text;
protected override void OnCreate(Bundle bundle)
{
...
_wifiReceiver = new wifiReceiver();
_manager = (WifiManager)GetSystemService(Context.WifiService);
_wifiSignals = new List<string>();
if (_manager.IsWifiEnabled)
{
_manager.StartScan();
}
...
}
And more extensive code from BroadcastReceiver:
public override void OnReceive(Context context, Intent intent)
{
MainActivity activity = (MainActivity)context.ApplicationContext;
activity._wifiSignals.Clear();
activity._wifiSignals.Add("Lista de wifi:\n");
IList<ScanResult> wifiScanList = activity._manager.ScanResults;
foreach (ScanResult wifiNetwork in wifiScanList)
{
activity._wifiSignals.Add(wifiNetwork.Ssid + ": " + wifiNetwork.Level);
}
//activity.presentation(activity._wifiSignals, activity);
activity._manager.StartScan();
}
Although I remember to call MainActivity properties from another activities in previous apps I developed, I'm pretty sure you cant call a function like you try to do with the StartScan().
The option I use normally is to store the data serialized, and call it in Main.
I do a class with some methods like:
class persistence
{
ISharedPreferences prefs;
ISharedPreferencesEditor editor;
public persistence(Context cont)
{
prefs = PreferenceManager.GetDefaultSharedPreferences(cont);
editor = prefs.Edit();
}
public void store(List<articulo> articulos)
{
string raw = JsonConvert.SerializeObject(articulos);
editor.PutString("articulos", raw);
editor.Commit();
}
public List<articulo> recover()
{
string raw = prefs.GetString("articulos", null);
List<articulo> lista;
if (raw == null)
lista = new List<articulo>();
else
lista = JsonConvert.DeserializeObject<List<articulo>>(raw);
return lista;
}
}
In your OnReceive function I call to the store function
In your OnCreate function you can do directly
persistence datos;
protected override void OnCreate(Bundle bundle)
{
_wifiReceiver = new wifiReceiver();
_manager = (WifiManager)GetSystemService(Context.WifiService);
datos = new persistence (this);
_wifiSignals = datos.recover();
if(_wifiSignals.Count>0)
StartScan();
}
This will also keep data from one usage to another, if you don't want just clear the persistence data after call the BroadcastReceiver;

Android C# WebView OnPageStart Toast

I am trying to complete an action of some sort while the the WebView is starting, like a loading spinning wheel or a toast message...
What I have done crashes the app. (I'm new to C#, so simple answers are appreciated).
public class MainActivity : Activity
{
private static Context context;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
WebView mWebView = FindViewById<WebView>(Resource.Id.webView);
mWebView.Settings.JavaScriptEnabled = true;
mWebView.SetWebViewClient(new MyWebViewClient());
//Load url to be randered on WebView
mWebView.LoadUrl("https://google.com");
}
public class MyWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
view.LoadUrl(url);
return true;
}
public override void OnPageStarted(WebView view, string url, Android.Graphics.Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
Toast.MakeText(context, "Loading...", ToastLength.Short).Show();
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
}
public override void OnReceivedError(WebView view, ClientError errorCode, string description, string failingUrl)
{
base.OnReceivedError(view, errorCode, description, failingUrl);
}
}
}
The private static Context context; field in MainActivity never gets assigned; using it in the Toast.MakeText(context ... ) call will result in a null exception. Fix this by using the global context:
Toast.MakeText(Android.App.Application.Context, "Loading...", ToastLength.Short).Show();

MVVMCross - BaseActivity and OnViewModelSet()

I have a little problem, which I can't solve..
Well, I built a BaseActivity.cs Class:
public class BaseActivity<T> : MvxBindingTabActivityView<T> where T : class, IMvxViewModel
{
protected override void OnViewModelSet()
{ }
public override bool OnCreateOptionsMenu(IMenu menu)
{
// GroupId, ItemId, OrderId
menu.Add(0, 0, 0, "Einstellungen").SetIcon(Android.Resource.Drawable.IcMenuManage);
menu.Add(0, 1, 1, "Info").SetIcon(Android.Resource.Drawable.IcMenuInfoDetails);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
var id = item.ItemId + 1; // (Id is zero-based :)
if (id == 1) // First Item
{
StartActivity(typeof(SettingsShowActivity));
}
else if (id == 2) // Second Item
{
Android.App.AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog ad = builder.Create();
ad.SetTitle("Information");
ad.SetIcon(Android.Resource.Drawable.IcDialogAlert);
ad.SetMessage("Version: 0.1");
ad.SetButton("OK", (s, e) => { Console.WriteLine("OK Button clicked, alert dismissed"); });
ad.Show();
}
return true;
}
}
The goal of this class is, that I can put things in that I will use in every other Activity, just like here, the OptionsMenu, which is more or less on all Activities..
Then my other two Activities which are inheriting from BaseActivity.cs:
the MainScreenActivity.cs:
[Activity]
public class MainScreenActivity : BaseActivity<MainScreenViewModel>
{
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.MainScreenLayout);
TabHost.TabSpec spec;
Intent intent;
intent = base.CreateIntentFor<AddressesSearchViewModel>();
intent.AddFlags(ActivityFlags.NewTask);
spec = TabHost.NewTabSpec("adressen");
spec.SetIndicator("Adressen");
spec.SetContent(intent);
TabHost.AddTab(spec);
intent = base.CreateIntentFor<ContactsSearchViewModel>();
intent.AddFlags(ActivityFlags.NewTask);
spec = TabHost.NewTabSpec("kontaktpersonen");
spec.SetIndicator("Kontaktpersonen");
spec.SetContent(intent);
TabHost.AddTab(spec);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
}
and the LoginActivity.cs:
[Activity]
public class LoginActivity : BaseActivity<LoginViewModel>
{
protected override void OnResume()
{
base.OnResume();
App.IsLoggedIn = false;
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.Login);
//App.MessageHub.Subscribe<ErrorMessage>((m) => { ErrorMessageAlert(m.Message, m.Title); });
}
}
Its compiling fine, but the app crashes when I start it, and thats the errormessage I get: Your content must have a TabHost whose id attribute is 'android.R.id.tabhost' . I suggest, that it is because I "needed" to implement the abstract interface into the BaseActivity.cs :
protected override void OnViewModelSet()
{ }
So maybe he walks into the 'false' OnViewModelSet(), (In the empty one instead of the one which is building the Tabhost).. but I'm actually not sure.. btw this comes from: MvxBindingTabActivityView..
Hmm any help would be appreciated
I think this is a quite simple problem...
MvxBindingTabActivityView inherits from TabActivity (see source) and it's this class that requires the content - Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'
If you don't want to use Tabs, then just inherit from MvxBindingActivityView instead - this is what the conference sample does - https://github.com/slodge/MvvmCross/blob/vnext/Sample%20-%20CirriousConference/Cirrious.Conference.UI.Droid/Views/BaseView.cs
If one of your activities needs to do tabs, but the other doesn't then they need to inherit using different inheritance trees. If you want to share code between the two base classes, then the best way to do this in C# seems to be using extension methods - e.g. see BaseViewExtensionMethods.cs shared between BaseView.cs, BaseTabbedView.cs and BaseMapView.cs in the conference sample.

Categories