I'm using ToDoActiviy.cs for user login, this class got this method:
[Java.Interop.Export()]
public async void LoginUser(View view)
{
if(await authenticate())..
This method is called from .axml file from Button widget android:onClick="LoginUser" I changed this for android:onClick="LoginUserClick" This last method create a dialog fragment for show different logins accounts.
Now from the Dialog Fragment(Is situated on another class) I want to hand the event for the button click on the dialog fragment and call this method from ToDoActivity.cs.
On dialog fragment class I hand the click event like this:
private void ButtonSignInFacebook_Click(object sender, EventArgs args){
//Here code for call to LoginUser method from 'ToDoActivity.cs'
ToDoActiviy.cs act = new ToDoActivity();
act.LoginUser();
}
I need to pass a View but I tried a lot of things and any works..
Someone can help me?
Thanks in advance ;)
I would like to make a slight modification to #guido-gabriel 's answer.
In C# syntax, it will be
((ToDoActivity)Activity).yourPublicMethod();
Getter/Setter Methods in Java are mapped to Getter Setter properties in Xamarin.Android
Finally I fix it ! I had to change the parameters of the method and create it without parameters.. and now Is working. Both solutions are good:
((ToDoActivity)Activity).LoginUserFacebook();
//ToDoActivity act = new ToDoActivity();
//act.LoginUserFacebook();
Adapt and use the snipped below in your fragment
var casted = Activity as MyActivityName;
if (casted != null) {
casted.IWantToCallThisMethodFromMyFragment();
}
You have to call the method from the activity. Have you tried?
((YourActivityClassName)getActivity()).yourPublicMethod();
I.E.
((ToDoActivity)getActivity()).yourPublicMethod();
This is not really a good practice to do. Why?
Doing this couples the Fragment tightly to this particular Activity type, meaning it will not be possible to reuse the Fragment elsewhere in the code.
Instead I suggest you rely on the Activity subscribing to an event or implementing some kind of callback method in order to do the desired action after login.
It could also seem like your Activity might be containing a lot of logic that could be split out into a shared library of some kind. Making it possible to reuse that code on another platform, for instance iOS in the future.
So since your are in charge of newing up the Fragment, I would do something like this instead:
public class LoginFragment : Fragment
{
Action _onLoggedIn;
public static void NewInstance(Action onLoggedIn)
{
var fragment = new LoginFragment();
fragment._onLoggedIn = onLoggedIn;
return fragment;
}
private void Login()
{
// login user
// after loggedin
_onLoggedIn?.Invoke();
}
}
Then in your Activity:
private void LoginUser()
{
// whatever
}
var loginFragment = LoginFragment.NewInstance(LoginUser);
// fragment transaction here...
Related
I have created a segue in my storyboard. It is a modal segue and I gave it an identifier of LoginSegue.
In my controller, I do:
PerformSegue("LoginSegue", this);
All is well. New controller comes up as expected.
To return to the calling view, I can use an unwind segue by wiring a button to the unwind segue in the calling controller. This works fine.
Or I can call (from code):
DismissViewController(true, null);
The thing is that I want to pass back some data.
The ParentViewController is null.
I do need to do some validation before I return to the calling view, so doing the auto unwind from a button is not an option.
[Action("UnwindToCaller:")]
public void UnwindToCaller(UIStoryboardSegue seque)
{
var loginViewController = (LoginViewController)seque.SourceViewController;
var data = loginViewController.getData();
Console.WriteLine("Unwind to Caller Here.");
}
I also cannot seem to find a way to give the unwind segue a storyboard Id.
If I could perform the unwind segue from code everything would be good, or if I could pass some data back with the DismissViewController that would be good too.
Again, this seems like a pretty common thing to do.
OK, found a solution. Hope this helps others.
So in my calling view controller, when I PrepareForSegue, I pass a reference to the calling view controller. Then I have a method that is called from the modal view.
Here is the Xamarin reference: http://developer.xamarin.com/recipes/ios/general/storyboard/storyboard_a_tableview/
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
if (segue.Identifier == "LoginSegue")
{
var destCtrl = segue.DestinationViewController as LoginViewController;
if (destCtrl != null)
{
// pass in a reference to THIS view controller.
destCtrl.SetData(this);
}
}
base.PrepareForSegue(segue, sender);
}
Also in the Calling View Controller
public void LoggedIn (string someFlag)
{
Console.WriteLine("Logged in with : " + someFlag);
DismissViewController(true, null);
}
And the LoginViewController
public void SetData (CallingViewController callingCtrl)
{
calllingViewController = callingCtrl;
}
Then when I am ready to return.
callingViewController.LoggedIn("the flag");
I want to add a new form to an existing solution. The solution already has a Validator class, so I want to expand this class.
The Form I want to create contains a Textbox (for the input) and a Button. When the input is the correct format the submit button is enabled. The input must adhere to a certain regular expression: "^[A-Za-z]{2}[0-9]{5}$". I'm checking the input (on-the-fly) in the Form class like this:
private void inputTbx_TextChanged(object sender, TextChangedEventArgs e)
{
SubmitButton.IsEnabled = Validator.IsInputValid(inputTbx.Text, RegexExpression);
}
I've put the regular expression as a variable in the Form class. I put it here because it is relevant to the textbox of this form only.
private const string RegexExpression = "^[A-Za-z]{2}[0-9]{5}$";
Here's the validation code:
public static bool IsInputValid(string inputToBeChecked, string regexExpression)
{
if (inputToBeChecked == null || regexExpression == null)
{
return false;
}
var regex = new Regex(regexExpression, RegexOptions.None);
return regex.IsMatch(inputToBeChecked);
}
So far so good. It seems to work fine. But I want to unit test it like so:
[TestCase("aZ13579")]
public void ValidateInputOkTest(string input)
{
Assert.IsTrue(Validator.IsInputValid(input, RegexExpression));
}
But to do it like this I have to have a string in my ValidatorTest class similar to the Regular-expression used in the Form class. This doesn't seem like the right way to do it. What I really want to do is get the Regex expression from the form class, so I am sure it's the correct Regex-expression that I'm using. Otherwise the Regex-expressions could easily get out of sync.
Here are the questions:
What is best practice here?
How do I get to this expression? I've tried doing it using Reflection, but I get a Threadstat error because it's a GUI component. Should I move the Regular-expression? If so where to?
I'm thinking there must be a smart way to do this. A smart design perhaps. Suggestions and comments are welcome.
You're probably going to want to back up a step and start to research the 'MVVM' design pattern. When you hear people talk about putting no code in the code behind, testing like this is one of the big benefits (among many others).
MVVM is too big a topic to handle in a simple answer like this. I'd search around on the web, and I'm sure other people have some good tutorials.
Just to be clear, it can be a big learning curve, but it's totally worth it. MVVM is what makes WPF much much (MUCH) better than WinForms, rather than merely different.
Just to address your question a little more specifically, you won't be testing a GUI object like a Window or UserControl. You'll be testing a view model which is just a regular class.
Here's a simplified version of what you might see
public class MyScreenViewModel : INotifyPropertyChanged
{
private const string RegexExpression = "^[A-Za-z]{2}[0-9]{5}$";
public bool UserInputIsValid { get { stuff; } set { stuff; }}
public string UserInput { get { stuff; } set { stuff; ValidateUserInput();} }
private void ValidateUserInput()
{
if (UserInput == null)
{
return false;
}
var regex = new Regex(RegexExpression, RegexOptions.None);
UserInputIsValid = regex.IsMatch(UserInput);
}
}
A view model in MVVM is the real logic of your screen. It will expose simple properties that the view can bind to for display/input, but the view isn't necessary for testing the logic.
Then your test looks something like:
[TestCase("aZ13579")]
public void ValidateInputOkTest()
{
var vm = new MyScreenViewModel();
vm.UserInput = "SomeValidText";
Assert.IsTrue(vm.UserInputIsValid);
}
[TestCase("aZ13580")]
public void ValidateInputNotOkTest()
{
var vm = new MyScreenViewModel();
vm.UserInput = "SomeInvalidText";
Assert.IsFalse(vm.UserInputIsValid);
}
I'm trying to update a datagridview with some data calculated in a different class and thread, using a delegate. Unfortunately I'm having trouble with a variety of different errors, depending on the approach I try.
The code I am trying to execute in the form thread looks like this:
public partial class AcquireForm : Form
//
// ...
//
// update the grid with results
public delegate void delUpdateResultsGrid(int Index, Dictionary<string, double> scoreCard);
public void UpdateResultsGrid(int Index, Dictionary<string, double> scoreCard)
{
if (!this.InvokeRequired)
{
//
// Some code to sort the data from scoreCard goes here
//
DataGridViewRow myRow = dataGridViewResults.Rows[Index];
DataGridViewCell myCell = myRow.Cells[1];
myCell.Value = 1; // placeholder - the updated value goes here
}
}
else
{
this.BeginInvoke(new delUpdateResultsGrid(UpdateResultsGrid), new object[] { Index, scoreCard});
}
}
Now, I need to get this method to run from my other thread and class. I have tried:
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm.delUpdateResultsGrid updatedelegate = new AcquireForm.delUpdateResultsGrid(AcquireForm.UpdateResultsGrid);
updatedelegate(myIndex, myScoreCard);
}
Unfortunately this gives an "Object reference is required for the non-static field, method, or property AcquireForm.UpdateResultsGrid(int, System.Collections.Generic.Dictionary)" error. I seem to be unable to reference the UpdateResultsGrid method at all...
I have noticed that
public class myOtherClass
//
// ...
//
private void myOtherClassMethod(int myIndex)
{
// ...
AcquireForm acquireForm = new AcquireForm();
acquireForm.UpdateResultsGrid(myIndex,myScoreCard);
}
does not throw any errors when compiling, but it tries to create a new form and that is something I do not want to do. I don't want to create a new instance of AcquireForm, I want to reference the pre-existing one, if that's possible.
I have also tried making the UpdateResultsGrid method static, but this throws up problems with several things incuding the use of "this.(anything)".
I've also tried moving the majority of the UpdateResultsGrid method into myOtherClassMethod, leaving behind in the AcquireForm class just the delegate. Again, this does not work because many of the references to UI objects break (there aren't any dataGridViews in scope).
I'm starting to run out of ideas here. Unfortunately I'm rather new to C# (as you can probably tell), and I'm editing someone else's code rather than writing my own entirely from scratch. If anyone could offer some advice on this problem it'd be most appreciated.
Make sure your objects are communicating with each other: Your myOtherClass is going to have to know about the AcquireForm object - you can't just create a new one (as you've discovered). You'll need to pass the AcquireForm object into the myOtherClass object (myOtherObject.SetForm(myAcquireForm, for example) and reference it when you need to.
In case you're having issues with invoking this might be of help - how I invoke a "next" button click:
BeginInvoke(new Action(()=>button_next_Click(null,null)));
Moreover, it sounds like maybe this should not be separate classes and you should be utilising a BackgroundWorkder instead.
I am trying to convert the following iOS code into MonoTouch and cannot figure out the proper conversion for the #selector(removebar) code. Can anyone provide guidance about the best way to handle #selector (since I've come across that in other places as well):
- (void)keyboardWillShow:(NSNotification *)note {
[self performSelector:#selector(removeBar) withObject:nil afterDelay:0];
}
My C# code is:
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification,
notify => this.PerformSelector(...stuck...);
I am basically trying to hide the Prev/Next buttons that show on the keyboard.
Thanks in advance for any help.
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, removeBar);
where removeBar is a method defined elsewhere.
void removeBar (NSNotification notification)
{
//Do whatever you want here
}
Or, if you prefer using a lambda:
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillShowNotification,
notify => {
/* Do your stuffs here */
});
Stephane shows one way you can use our improved bindings to convert that.
Let me share an even better one. What you are looking for is a keyboard notification, which we conveniently provide strong types for, and will make your life a lot easier:
http://iosapi.xamarin.com/?link=M%3aMonoTouch.UIKit.UIKeyboard%2bNotifications.ObserveWillShow
It contains a full sample that shows you how to access the strongly typed data that is provided for your notification as well.
You have to take into account that:
[self performSelector:#selector(removeBar) withObject:nil afterDelay:0];
it's exactly the same that
[self removeBar];
The call to performSelector is just a method call using reflection. So what you really need to translate to C# is this code:
- (void)keyboardWillShow:(NSNotification *)note {
[self removeBar];
}
And I guess that also the notification subscription, that sums up to this code:
protected virtual void RegisterForKeyboardNotifications()
{
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification);
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
}
private void OnKeyboardNotification (NSNotification notification)
{
var keyboardVisible = notification.Name == UIKeyboard.WillShowNotification;
if (keyboardVisible)
{
// Hide the bar
}
else
{
// Show the bar again
}
}
You usually want to call RegisterForKeyboardNotifications on ViewDidLoad.
Cheers!
I'll start of by saying I'm not a developer. Yes this is a c# nightmare. But this is a one time tool and thats it. Quick and Dirty it just needs to work and thats it.
I have the following code:
public string[] get_status(string local_fname)
{
var dts_doc = new HtmlAgilityPack.HtmlDocument();
dts_doc.Load(local_fname);
//Pull the values
var ViewState = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[1]/input[4]/#value[1]");
var EventValidation = dts_doc.DocumentNode.SelectSingleNode("/html[1]/body[1]/div[2]/input[1]/#value[1]");
string ViewState2 = ViewState.Attributes[3].Value;
string EventValidation2 = EventValidation.Attributes[3].Value;
//Display the values
//System.Console.WriteLine(ViewState.Attributes[3].Value);
//System.Console.WriteLine(EventValidation.Attributes[3].Value);
//System.Console.ReadKey();
return new string[] { ViewState2, EventValidation2 };
}
I want to call get_status from a button on my Main.cs which will show 2 Message Boxes with ViewState2 and EventValidation2.
Again, I'm not a developer, this is probably the wrong way of doing things. But I just need a quick and dirty solution to get this job done once.
Make the function static by adding the static keyword to the function definition:
static public string[] get_status(string local_fname)
Use the class name to reference the function from your Main class.
try this:
foreach(string s in get_status(localFname))
{
MessageBox.Show(s);
}
As you said, it is quick and dirty and I stayed faithful to that paradigm.
And yes, if you need to acces another class, make the method static or just simply create an instance and call the method on it. I hope I have understood the problem correctly.
if you are using visual studio, go to the Button you want to click, double-click the button. This will create an eventhandler. In the eventhandler you should call the above method.
protected void Button1_Click(object sender, eventArgs e)
{
string local_fname = someValue;
string results[] = get_status(local_fname);
MessageBox.Show(results[0]);
MessageBox.Show(results[1]);
}