Xamarin.Android, How to call another method from another .cs file - c#

Needed some help here, so I tried to run method from another .cs, I had the following code running in my mainActivity.cs
protected override void OnCreate(Bundle savedInstanceState)
{
startGame();
}
public void startGame()
{
firstNum = FindViewById<TextView>(Resource.Id.tvFirstNum);
secondNum = FindViewById<TextView>(Resource.Id.tvSecondNum);
firstNum.Text = Convert.ToString(2);
secondNum.Text = Convert.ToString(2);
}
Heres the thing, I tried to run the code from another CS file
private void BtnRestart_Click(object sender, EventArgs e)
{
MainActivity mp = new MainActivity();
mp.startGame();
}
However I got the error of
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.app.Activity.findViewById(int)' on a null
object reference
Does anyone know why?
Thanks!

You are instantiating your MainActivity class yourself, which does not load the accompanying Layout. That's because the lifecycle methods that Android uses aren't called, so your OnCreate method will never fire (which usually handles the layout). Therefore the FindViewById will return a null value.
A note: You shouldn't call views from another class (especially if it's another Activity) like you are trying to do here. If you still want to call this from another class, you can pass the Activity as a parameter, but I wouldn't recommend that approach.
I would recommend you to read the Activity Lifecycle documentation.

Related

Xamarin Android TextToSpeech.Speak doesn't say anything if run from any of the lifecycle methods

I'm using an instance of TextToSpeech to convert some text, using the Speak() method, like so:
textToSpeech = new TextToSpeech(context, this, "com.google.android.tts");
textToSpeech.SetPitch(1f);
textToSpeech.SetSpeechRate(1f);
textToSpeech.Speak(textToConvert, QueueMode.Flush, null, null);
The function runs fine with no errors, but the speech can only actually be heard (and the isSpeaking property only changes to true) when the function is not called from the lifecycle methods.
I've tried placing it in OnCreate(), OnStart(), and OnResume() all with the same results, although the function runs fine if called from a button event.
Is this a limitation of the class, or something I can fix?
It's because you're calling the Speak() method before the TTS engine has loaded. It takes a few moments for it to initialize.
Fortunately, the TextToSpeech.IOnInitListener interface provides a way to know when the engine has successfully loaded via the OnInit() method.
So, if you want your app to speak in OnCreate(), you'll need to move the Speak() method to the OnInit() method. Here's a working example I put together for you...
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Speech.Tts;
namespace XamdroidMaster.Activities {
[Activity(ParentActivity = typeof(MainActivity), Label = "Text to Speech")]
public class TextToSpeechActivity : Activity, TextToSpeech.IOnInitListener {
private TextToSpeech tts;
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.TextToSpeech);
// Create text to speech object (first parameter: context; second parameter: object implementing TextToSpeech.IOnInitListener)
// Note that it may take a few moments for the TTS engine to initialize (OnInit() will fire when it's ready)
tts = new TextToSpeech(this, this, "com.google.android.tts");
}
public void OnInit([GeneratedEnum] OperationResult status) {
if (status == OperationResult.Success) {
tts.SetPitch(1f);
tts.SetSpeechRate(1f);
tts.Speak("Hello Luke!", QueueMode.Flush, null);
}
}
}
}
Also, by initializing the TTS engine in OnCreate() as I've shown in the example, you should then be able to fire the Speak() command later in OnResume().
Hope this helps!
The problem is that TTS engine initialization takes some time. If initialization is not over, the speak method call will fail.
If you "say" something on button click, you will probably won't need this, because user will take some time to think before pressing the button, and the initialization will be over.
If you want to "say" something as soon initialization finishes, use this codeļ¼š
public class MainActivity : AppCompatActivity
{
private static TextView speechtext;
private static TextToSpeech saytext;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.layout1);
speechtext = FindViewById<TextView>(Resource.Id.textView1);
saytext = new TextToSpeech(this, new MyListener(), "com.google.android.tts");
}
class MyListener : Java.Lang.Object, TextToSpeech.IOnInitListener
{
public void OnInit([GeneratedEnum] OperationResult status)
{
if(status==OperationResult.Success)
{
saytext.SetLanguage(Locale.Us);
saytext.SetPitch(1.5f);
saytext.SetSpeechRate(1.5f);
saytext.Speak(speechtext.Text, QueueMode.Flush, null, null);
}
}
}
}

Implement destructor/dispose in code behind

I use asp net 4.5.
I have Marker.aspx page and code behind page Marker.aspx.cs.
Whenever postback occurred Page_Load function is fired in code behind and GeoMarkup class created.
GeoMarkup markupManager;
protected void Page_Load(object sender, EventArgs e)
{
markupManager = new GeoMarkup("constans",
"mapName",
null);
}
Whenever postback is implemented I need destructor to be fired and put inside destructor this row:
markupManager.Dispose();
My question is how can I implement destructor in code behind?;
Your code behind class is a subclass of Page which has a virtual Dispose method.
You should override that method in your code behind
public override void Dispose()
{
if (markupManager != null) {
markupManager.Dispose();
markupManager = null;
}
}
The HTTP pipeline will call Dispose on classes when they are no longer needed for the processing of the current HTTP request.

C# Call an event (linkLabel2_LinkClicked) from another method

I have an event handler on my form for a LinkLabel linkLabel2_LinkClicked:
private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
//code here
}
I need to call it from another method that does not have an object sender and any eventargs:
private void UpdateMethod()
{
linkLabel2_LinkClicked(this, ?????)
}
If it were a Button I would just call the PerformClick method. But there is no such for a LinkLabel that I could find.
What is the best practice to execute the code in the linkLabel2_LinkClicked event handler?
Update: I guess I was not clear on this. I wonder about the best practice as I have seen this approach. I can see from the answers that this is not the correct approach but to move the code to a separate method and call it directly from the other method. Please let me know if any other reasoning goes with this.
Update 2: I rewrote the code now as follows:
private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
CreatePreview();
}
private void UpdateMethod()
{
CreatePreview();
}
private void CreatePreview()
{
//code comes here
}
It works perfectly.
You can put null in event parameter :
linkLabel2_LinkClicked(this, null);
or create a new event object :
linkLabel2_LinkClicked(this, new LinkLabelLinkClickedEventArgs());
But the best way is create a separate methode and call it in every time you need it.
You could just pass null since you're not using the parameter anyway, but I'd recommend against that. It's discouraged to call an event directly, and it leads to code that's tough to read.
Just have the other method call CreatePreview().
private void UpdateMethod()
{
CreatePreview();
}

Calling non-static void from another cs file

I am new to C# and I probably know somebody asked the same thing here before. I found some info on google and here on stackoverflow , but I just can't get it to work properly.
I need to call a non-static void (MainPage.cs / class MainPage) .
public async void UploadThat()
{
.
.
.
.
Messagebox.Show("Hello there!");
}
from a another cs file (WebServer.cs).
I tried to do it like this in Webserver.cs file:
using MainPage;
.
.
.
public MainPage test;
and than call: test.UploadThat();
It complied my app successfully, but it does not work.
Thanks in advance..
There are several ways to solve this. Here are the two most common:
call the method directly; since the method is not static, you need a reference to the MainPage instance. That instance could be passed to the constructor of WebServer, and stored as a field. Note that this approach causes high coupling between the classes, which is usually not desirable.
in WebServer.cs:
private readonly MainPage _mainPage;
public WebServer(MainPage mainPage)
{
_mainPage = mainPage;
}
...
_mainPage.UploadThat();
in MainPage.cs:
WebServer ws = new WebServer(this);
expose an event in the WebServer class; have MainPage handle this event by calling UploadThat; when WebServer wants to call UploadThat, it just raises the event, and MainPage takes care of it. This way, WebServer doesn't have to know anything about MainPage.
in WebServer.cs:
public event EventHandler UploadRequested;
private void OnUploadRequested()
{
EventHandler handler = UploadRequested;
if (handler != null)
handler(this, EventArgs.Empty);
}
...
// instead of calling UploadThat directly
OnUploadRequested();
in MainPage.cs:
WebServer ws = new WebServer();
ws.UploadRequested += ws_UploadRequested;
...
private void ws_UploadRequested(object sender, EventArgs e)
{
UploadThat();
}
As a side note, you should avoid async void methods, except for event handlers or method overrides. This article explains why.

Monotouch (Xamarin.iOS) memory leaks

I have an app with a complicated view hierarchy, where one view controller (let's call it RootVC) can lead to many others. When I pop RootVC, I want to completely release it. I didn't store the reference in a field or elsewhere and believed that it would be enough. But recently I checked Xamarin's Heap Shot and saw in the "All objects" section that many instances of RootVC remain in memory.
For example this:
sealed class VC1 : UIViewController
{
...
private void OpenVC2()
{
var vc2 = new VC2();
NavigationController.PushViewController(vc2, true);
}
}
sealed class VC2 : UIViewController
{
private UIButton button;
public override ViewDidLoad()
{
...
button = new UIButton(frame);
button.TouchUpInside += HandleButtonTouch;
...
}
private void HandleButtonTouch(object sender, EventArgs e)
{
NavigationController.PopViewControllerAnimated(true):
}
}
Here's the link to the screenshots of Heap Shot (can't post them as images because of low reputation) - http://imgur.com/a/3MYBr#1
When I'm on VC2, I see something like on the first screenshot.
When I pop VC2 and on VC1, Heap Shot look like on the second image.
After a few VC2 appearing it looks like the third screenshot.
And they never get disposed or finalized, they remain till the end. Even through they are not reachable via roots. Which is not acceptable because memory is limited and with all these VC2's in memory we can easily get memory warning or app terminating.
But if I subscribe/unsubscribe to button events on View appearing/disappearing like this:
sealed class VC2 : UIViewController
{
private UIButton button;
public override ViewDidLoad()
{
...
button = new UIButton(frame);
...
}
public override void ViewWillAppear(bool animated)
{
...
button.TouchUpInside += HandleButtonTouch;
}
public override void ViewDidDisappear(bool animated)
{
...
button.TouchUpInside -= HandleButtonTouch;
}
private void HandleButtonTouch(object sender, EventArgs e)
{
NavigationController.PopViewControllerAnimated(true):
}
}
In this case VC2 (with all its views, etc) gets disposed properly when it disappears. ~VC2() is called and all VC2's are no longer visible in Heap Shot.
So, here's the question. What I am doing wrong? Should I subscribe/unsubscribe from control events like above? Or it's a Xamarin.iOS's memory leak?
What happens is that there is a circular dependency (with the event handlers) that crosses the native-managed boundary, and currently the Xamarin.iOS runtime/GC is not able to detect this.
You've already found the fix for this problem: break the circular dependency by removing event handlers.
Here is a video explaining the situation in detail: http://xamarin.com/evolve/2013#session-0w86u7bco2

Categories

Resources