Why does a Global object lead to non working code? - c#

I have a pretty basic android application. Most of it is still from the template. I use an own class library for a XMPP client. Now what is happening, is that if I declare a global TextView for use in events or different methods(OnCreate, OnResume etc.) my events seem not to get raised.
My library is definetly not the reason since I tested it under different circumstances.
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public XMPPClient client;
TextView consoleText;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;
//consoleText = FindViewById<TextView>(Resource.Id.consoleText);
client = new XMPPClient("10.0.2.2", 5222, "chris3", "Define")
{
AuthenticationInfo = true
};
client.OnPublishEvent += Client_OnPublishEvent;
client.OnConnection += Client_OnConnection;
client.OnMessageReceived += Client_OnMessageReceived;
}
private void Client_OnMessageReceived(object sender, MessageEventArgs e)
{
//consoleText.Text += "\nMessage: " + e.text + " from " + e.from;
Log.Debug("mytag", "Message!");
}
private void Client_OnPublishEvent(object sender, SubscriptionEventArgs e)
{
//consoleText.Text += "\nPublication: " + e.title + " with content " + e.content + " at " + e.published;
Log.Debug("mytag", "Publication");
}
private void Client_OnConnection(object sender, ConnectionEventArgs e)
{
//consoleText.Text += "\nConnection status changed: " + e.Status;
Log.Debug("mytag", "ConnectionChange");
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_subscribe)
{
client.Subscribe("Node1");
Log.Debug("mytag", "Subscribed");
return true;
}
return base.OnOptionsItemSelected(item);
}
private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
client.Connect();
}
}
When the TextView consoleText object is there, my OnConnection-event gets fired in the beginning, and when I close the client.
However, if it isn't there, every event works perfectly fine.
For now as I am a beginner with Xamarin I'm really interested on the source of the problem.

You likely need to wrap the text assignments to be processed against the primary dispatcher (UI Thread).
So for example it would look something like this.
private void Client_OnMessageReceived(object sender, MessageEventArgs e)
{
Activity.RunOnUiThread(() => {
consoleText.Text += "\nMessage: " + e.text + " from " + e.from;
});
Log.Debug("mytag", "Message!");
}
Give that a try and let me know if it still errors, don't forget you'll need to wrap all of the consoleText.Text assignments inside of this.
Activity.RunOnUiThread(() => {
});
// You might not need the Activity part, I can't remember exactly which
// one it is for an AppCompatActivity.
RunOnUiThread(() => {
});

Related

C# EventHandler invoke doesn't always work

I have a class with the following code:
public delegate void EventHandler(HMIEventArgs e);
public event EventHandler OnEvent;
private void ReadReg(RegisterMap register)
{
if (!_hmiConnected) return;
var eventArgs = new HMIEventArgs();
var handler = OnEvent;
try
{
byte slaveId = 1;
ushort startAddress = register.Register;
eventArgs.Event = Events.GotData;
eventArgs.IPAddress = _hostname;
eventArgs.Name = register.FriendlyName;
ushort[] val = _master.ReadHoldingRegisters(slaveId, startAddress, 1);
eventArgs.Data = val[0];
handler?.Invoke(eventArgs);
// -------- THIS GETS PRINTED ------------
Debug.WriteLine("Got data from " + _hostname + ":" + register.Register + "(" + register.FriendlyName + ") : " + val[0]);
}
catch (Exception err)
{
Debug.WriteLine(err.ToString());
}
}
Several instances of this class are created in another class :
new Thread(() =>
{
tasks.Add(Task.Factory.StartNew(() =>
{
_masters.Add(new HMIMaster().Connect(ipAddress, port).SetRegisters(registers));
_masters.Last().OnEvent += HMIEvent;
Debug.WriteLine(_masters.Count + " masters");
}));
}).Start();
private static void HMIEvent(HMIEventArgs e)
{
// HOWEVER THIS SOMETIMES DOESN'T SHOW FOR
// ALL INSTANCES OF THE PREVIOUS CLASS
Debug.WriteLine(">> in logger (" + e.IPAddress + " " + e.Event + ") >> " + e.Name + " :: " + e.Data);
var handler = OnEvent;
handler?.Invoke(e);
}
Am I doing something wrong here?
I would use a static event to avoid registering every time on new instance and un-register on dispose (to void memory leaks). No locking is required in your case so I would simplify it like that:
(First class)
public static event EventHandler<HMIEventArgs> HMIEvent;
private void OnHMIEvent(HMIEventArgs e)
{
HMIEvent?.Invoke(this, e);
}
private void ReadReg(RegisterMap register)
{
...
OnHMIEvent(new HMIEventArgs()
{
Name = register.FriendlyName,
Event = Events.GotData,
IPAddress = _hostname,
eventArgs.Data = val[0]
});
...
}
(Second Class)
...
FirstClass.HMIEvent += FirstClass_HMIEvent; // Probably in your static constructor, register only once (unregister if required on disposal)
...
private void FirstClass_HMIEvent(object sender, HMIEventArgs e)
{
// (FirstClass)sender can be used here if required
}
By the way those two lines at your sample code should not be there (on your static HMIEvent method, you didn't provide us what is the OnEvent on your second class and you dont need to pass it on the handler var every time):
var handler = OnEvent;
handler?.Invoke(e);

Windows Forms - detecting an external application closing to trigger an event

I am in need of a solution to trigger code when an external application is closing / closes.
I am unable to use System.Diagnostics Process.GetProcessByName to detect if the process is running since it might conflict with an anticheat system. I would need trigger the snippet of code only when the program closes and only then.
I made a good, event-based implementation.
class Monitor
{
public event EventHandler ProgramStarted;
public event EventHandler ProgramClosed;
public Monitor(string process)
{
string pol = "2";
if (!process.EndsWith(".exe")) process += ".exe";
var queryString =
"SELECT *" +
" FROM __InstanceOperationEvent " +
"WITHIN " + pol +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + process + "'";
var s = #"\\.\root\CIMV2";
ManagementEventWatcher watcher = new ManagementEventWatcher(s, queryString);
watcher.EventArrived += new EventArrivedEventHandler(OnEventArrived);
watcher.Start();
}
private void OnEventArrived(object sender, EventArrivedEventArgs e)
{
if (e.NewEvent.ClassPath.ClassName.Contains("InstanceDeletionEvent"))
{
EventHandler handler = ProgramClosed;
handler?.Invoke(this, e);
}
else if (e.NewEvent.ClassPath.ClassName.Contains("InstanceCreationEvent"))
{
EventHandler handler = ProgramStarted;
handler?.Invoke(this, e);
}
}
}
To use it, you just create an instance of the class and set up the events. For example:
static void Main(string[] args)
{
var mon = new Monitor("chrome");
mon.ProgramClosed += Mon_ProgramClosed;
mon.ProgramStarted += Mon_ProgramStarted;
Console.ReadKey(true);
}
private static void Mon_ProgramStarted(object sender, EventArgs e)
{
MessageBox.Show("Program started.");
}
private static void Mon_ProgramClosed(object sender, EventArgs e)
{
MessageBox.Show("Program closed.");
}
Make sure to add reference to System.Drawing if you're using a console app, and ,for winforms, adjust the modifiers.

Implementing a camera directly in the application

I am implementing a camera function directly in an app, as I do not want to use Intent to open the default camera application. I have followed the code provided here:
The app crashes as soon as the photo gets taken, with the following error message:
Java.Lang.RuntimeException: Fail to connect to camera service
Here is how I set it up. I have ommited the unneccessary code.
namespace camera_test
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Android.Support.V7.App.AppCompatActivity, Android.Hardware.Camera.IPictureCallback, Android.Hardware.Camera.IPreviewCallback,
Android.Hardware.Camera.IShutterCallback, ISurfaceHolderCallback
{
static Android.Hardware.Camera camera = null;
Button btnStart;
Button btnEnd;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
SurfaceView surface = (SurfaceView)FindViewById(Resource.Id.surface);
var holder = surface.Holder;
holder.AddCallback(this);
holder.SetType(Android.Views.SurfaceType.PushBuffers);
btnStart = FindViewById<Button>(Resource.Id.buttonStart);
btnEnd = FindViewById<Button>(Resource.Id.buttonEnd);
btnStart.Click += BtnStart_Click;
btnEnd.Click += BtnEnd_Click;
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.SetVmPolicy(builder.Build());
}
private void BtnStart_Click(object sender, EventArgs e)
{
camera.StartPreview();
private void BtnEnd_Click(object sender, EventArgs e)
{
Android.Hardware.Camera.Parameters p = camera.GetParameters();
p.PictureFormat = Android.Graphics.ImageFormatType.Jpeg;
camera.SetParameters(p);
camera.TakePicture(this, this, this);
StartActivity(typeof(MainActivity));
}
void Android.Hardware.Camera.IPictureCallback.OnPictureTaken(byte[] data, Android.Hardware.Camera camera)
{
Java.IO.FileOutputStream outStream = null;
Java.IO.File dataDir = Android.OS.Environment.ExternalStorageDirectory;
DateTime DT = DateTime.Now;
String DateTimeStamp = DT.Year.ToString("D4") + "-" + DT.Month.ToString("D2") + "-" + DT.Day.ToString("D2") + "-" + DT.Hour.ToString("D2") + DT.Minute.ToString("D2") + DT.Second.ToString("D2");
String PictureFilename = "Photo-" + DateTimeStamp + ".jpg";
if (data != null)
{
try
{
outStream = new Java.IO.FileOutputStream(dataDir + "/" + PictureFilename);
outStream.Write(data);
outStream.Close();
}
catch (FileNotFoundException e)
{
System.Console.Out.WriteLine(e.Message);
}
catch (IOException ie)
{
System.Console.Out.WriteLine(ie.Message);
}
}
}
void Android.Hardware.Camera.IPreviewCallback.OnPreviewFrame(byte[] b, Android.Hardware.Camera c)
{
}
void Android.Hardware.Camera.IShutterCallback.OnShutter()
{
}
public void SurfaceCreated(ISurfaceHolder holder)
{
try
{
camera = Android.Hardware.Camera.Open();
Android.Hardware.Camera.Parameters p = camera.GetParameters();
p.PictureFormat = Android.Graphics.ImageFormatType.Jpeg;
camera.SetParameters(p);
camera.SetPreviewCallback(this);
camera.Lock();
camera.SetPreviewDisplay(holder);
// camera.StartPreview();
}
catch (IOException e)
{
}
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
camera.Unlock();
camera.StopPreview();
camera.SetPreviewCallback(null);
camera.Release();
camera = null;
}
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format f, int i, int j)
{
}
}
}
I am guessing it has something to do with the way in which the camera is opened and closed, but I can't figure out how to solve this problem. Note that the start button correctly starts the camera viewer. It is only when the end button is clicked does the app crash. Any help or suggestions would be appreciated. Also, I know that camera has been depreciated. Thanks.
UPDATE:
The error occurs when:
the device changes orientation
or when I call this line: StartActivity(typeof(MainActivity));.
I.e, the error occurs when the acitvity gets restarted (I think that if the orientation changes then it restarts the activity as well). I have no idea how else to restart my activity because I need to. Interestingly, if I switch to a different activity then switch back to my main activity that has the camera function, the error does not occure. I am very puzzled.
Is there a reason why you use the deprecated camera api?
You should use camera2:
https://developer.android.com/reference/android/hardware/camera2/package-summary
Xamarin also provides a basic example for this:
https://developer.xamarin.com/samples/monodroid/android5.0/Camera2Basic/

Event handler not working perfectly with C#

I am dynamically creating buttons which each selection of a dropdownlist.
With the following code I am adding an event handler to each button.
button.Click += new System.EventHandler(button_Click);
PlaceHolder1.Controls.Add(button);
private void button_Click(object sender, EventArgs e)
{
//Do something...
Response.Write("hello");
}
But unfortunately it does not fire that event and gives me an error as following
button_Click 'Index.button_Click(object, System.EventArgs)' is a 'method', which is not valid in the given context
How do I handle this?
protected void DropDownList1_SelectedIndexChanged1(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(this, typeof(Page), "Close", "javascript:OpenPopUp1();", true);
if (Session["filter"] == DropDownList1.SelectedValue)
{
}
else
{
if (Session["filter"] == "")
{
Session["filter"] = DropDownList1.SelectedValue + ":";
}
else
{
Session["filter"] = DropDownList1.SelectedValue + ":" + Session["filter"];
}
}
string asd = Session["filter"].ToString();
string[] split = asd.Split(':');
DropDownList1.Items.RemoveAt(DropDownList1.SelectedIndex);
for (int i = 0; i < split.Count(); i++)
{
string filter = split[i].ToString();
Button button = new Button();
button.Text = split[i].ToString();
button.ID = split[i].ToString();
button.Attributes.Add("onclick", "remove(" + split[i].ToString() + ")");
button.Click += new System.EventHandler(button_Click);
PlaceHolder1.Controls.Add(button);
}
}
The above shows the whole code of dropdownselected index.
button.Click += new System.EventHandler(button_Click);
PlaceHolder1.Controls.Add(button);
} // <-- end your current method with a curly brace
// Now start a new method
private void button_Click(object sender, EventArgs e)
{
//do something...
Response.Write("hello");
}
It's hard to say what you're going after, as there are a number of issues going on here. Since your dynamically generated buttons are being created in the SelectedIndexChanged event handler of your dropdown, they are not going to exist, nor are their event bindings, on the next postback. This means they can show up on the page, but clicking them won't do anything.
Secondly, since you are storing the SelectedValue to Session, and then using that value to set the Button IDs, you are going to be creating buttons with duplicate IDs if the user ever comes back to the page. (I noticed you are removing the list item once selected, but it would come back if the user refreshed the page, while the session object would remain populated.)
Last oddity, I wasn't able to find where your particular exception is handling, nor able to reproduce it. Which version of .NET are you programming against? Are you invoking the button click event anywhere from code-behind?
Now, that all said, I'm providing the following fix (or at least improvement):
protected void Page_Init(object sender, EventArgs e)
{
CreateButtons();
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(this, typeof(Page), "Close", "javascript:OpenPopUp1();", true);
if (Session["filter"] == DropDownList1.SelectedValue)
{
}
else
{
if (Session["filter"] == "")
{
Session["filter"] = DropDownList1.SelectedValue + ":";
}
else
{
Session["filter"] = DropDownList1.SelectedValue + ":" + Session["filter"];
}
}
DropDownList1.Items.RemoveAt(DropDownList1.SelectedIndex);
CreateButtons();
}
private void CreateButtons()
{
PlaceHolder1.Controls.Clear();
if (Session["filter"] != null)
{
string asd = Session["filter"].ToString();
string[] split = asd.Split(':');
for (int i = 0; i < split.Count(); i++)
{
string filter = split[i].ToString();
Button button = new Button();
button.Text = split[i].ToString();
button.ID = split[i].ToString();
button.Attributes.Add("onclick", "remove(" + split[i].ToString() + ")");
button.Click += new System.EventHandler(button_Click);
PlaceHolder1.Controls.Add(button);
}
}
}
private void button_Click(object sender, EventArgs e)
{
//do something...
Response.Write("hello");
}

C#, passing events doesn't work

I have this example code that DOES work.
main form:
FileTransferManager fm = new FileTransferManager();
...
public FrmMain()
{
InitializeComponent();
...
fm.OnFile += fm_OnFile;
}
...
void fm_OnFile(object sender, FileTransferEventArgs e)
{
var recvFile = new FrmReceiveFile(fm, e);
recvFile.Show();
e.Accept = true;
}
and FrmReceiveFile:
public partial class FrmReceiveFile : Form
{
private FileTransferManager fm;
private FileTransferEventArgs ftea;
public FrmReceiveFile(FileTransferManager ftm, FileTransferEventArgs fea)
{
InitializeComponent();
fm = ftm;
ftea = fea;
Text = "File transfer: " + ftea.Jid;
lblSize.Text = Util.HumanReadableFileSize(ftea.FileSize);
lblFileName.Text = ftea.Filename;
lblDescription.Text = ftea.Description;
fm.OnError += fm_OnError;
fm.OnEnd += fm_OnEnd;
fm.OnStart += fm_OnStart;
fm.OnProgress += fm_OnProgress;
}
void fm_OnStart(object sender, FileTransferEventArgs e)
{
MessageBox.Show("file transfer started"); ///// THIS APPEARS & EVERYTHING WORKS!
if (e.Sid != ftea.Sid)
return;
}
...
And here is my code, all in one form, yet somehow it does not work.
public partial class Form1 : Form
{
private string sid = "";
FileTransferManager fmout = new FileTransferManager(); //// this FileTransferManager is for outgoing files
FileTransferManager fmin = new FileTransferManager(); //// this FileTransferManager is for incomeing files
FileTransferEventArgs fta = new FileTransferEventArgs();
Jid _jid = new Jid();
public Form1()
{
InitializeComponent();
fmout.OnError += fmout_OnError;
fmout.OnEnd += fmout_OnEnd;
fmout.OnStart += fmout_OnStart;
fmout.OnProgress += fmout_OnProgress;
fmout.XmppClient = xmppClient;
fmin.XmppClient = xmppClient;
fmin.OnFile += fmin_OnFile;
fmin.OnEnd += fmin_OnEnd;
fmin.OnStart += fmin_OnStart;
fmin.OnProgress += fmin_OnProgress;
}
////////////////////////////////////////////////////////////////////////////////////////////
void fmin_OnFile(object sender, FileTransferEventArgs e)
{
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize); ///// THIS APPEARS CORRECTLY
e.Accept = true;
fta = e;
}
void fmin_OnStart(object sender, FileTransferEventArgs e) /// THIS WON'T START! :(
{
MessageBox.Show("Incoming file!"); /// THIS WON'T START! :(
if (e.Sid != fta.Sid)
return;
}
Looks like e.Accept = true; does not launch fmin_OnStart ... any ideas what might be the problem?
Thanks!
The difference (that can be made out from the code you have shared) in the two pieces of code is that in the first one you are registering the "fm.OnStart += fm_OnStart" event when your "OnFileHandler" is called, while in the other one (not working) you are doing that upfront, even when your OnFileHandler is not called.
Though, as an user of FileTransferManager, i dont think that should matter.
Still, you can try the same thing in the second code.. so do it as below.
void fmin_OnFile(object sender, FileTransferEventArgs e)
{ fmin.OnStart += fmin_OnStart;
DisplayEvent("INCOMING FILE: " + e.Filename + " - " + e.FileSize);
e.Accept = true; fta = e; }
If that works, i would rather question the programmer of FileTransferManager.

Categories