Home » Android » c# – Pass custom objects to next activity in Xamarin Android

c# – Pass custom objects to next activity in Xamarin Android

Posted by: admin May 14, 2020 Leave a comment

Questions:

I’ve got a few custom objects like RootObject and Form that I want to pass on to the next activity.

This is an example of RootObject:

public class RootObject
{
    public Form Form { get; set; }
}

But how can I pass RootObject to the next activity with an Intent and get it in the next Activity? In Form there are again multiple properties with Lists and stuff and I need to access some of the properties in my next Activity. My intent is called like this:

saveButton.Click += delegate {
    if(ValidateScreen()){
        SaveData();
        Intent intent = new Intent(this, typeof(MainActivity));
        Bundle b = new Bundle();
        b.PutSerializable("RootObject", RootObject);
        StartActivity(intent);
    }
};
How to&Answers:

I know this is an old question but I think my answer might help someone.
What I did is that I used JSON.Net package that you can install with Nuget Manager. By using JSON you can serialize your object in first activity and then deserialize in the second activity.
This is how I did it:

Activity 1

using Newtonsoft.Json;
...
void mBtnSignIn_Click(object sender,EventArgs e)
{
      User user = new User();
      user.Name = "John";
      user.Password = "password";

      Intent intent = new Intent(this,typeof(Activity2));
      intent.PutExtra("User",JsonConvert.SerializeObject(user));
      this.StartActivity(intent);
      this.Finish();

}

Activity 2

using Newtonsoft.Json;
...
private User user;
protected override void OnCreate(Bundle savedInstanceState)
{
      base.OnCreate(savedInstanceState);

      SetContentView(Resource.Layout.Activity2);
      user = JsonConvert.DeserializeObject<User>(Intent.GetStringExtra("User"));
}

Hope this would help someone.

Answer:

This is how you can go about it. Your class needs to implement Serializable or Parcelable.
In the first Activity(where you want to send from):

final Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("my_class", your_object);
startActivity(intent);

Then in your second Activity, you would do:

final Intent passedIntent = getIntent();
final YourClass my_class = (YourClass) passedIntent.getSerializableExtra("my_class");

There is also another way of doing it, using Bundles::

Create a Bundle from your class like:

public Bundle toBundle() {
    Bundle b = new Bundle();
    b.putString("SomeKey", "SomeValue");

    return b;
}

Then pass this bundle with INTENT. Now you can recreate your class object by passing bundle like

public CustomClass(Context _context, Bundle b) {
    context = _context;
    classMember = b.getString("SomeKey");
}

Answer:

Well, in my case the objects I want to pass don’t know about Android or IOS, nothing like that. So implementing (Java version of) ISerializable or IParcelable is not an option.

So, what I did was to take the JSON (using Newtonsoft.Json NuGet package) approach and use Extension Methods.

public static class IntentExtension
{
    public static Intent PutExtra<TExtra>(this Intent intent, string name, TExtra extra)
    { 
        var json = JsonConvert.SerializeObject(extra);
        intent.PutExtra(name, json);
        return intent;
    }

    public static TExtra GetExtra<TExtra>(this Intent intent, string name)
    {
        var json = intent.GetStringExtra(name);
        if (string.IsNullOrWhiteSpace(json))
        {
            return default(TExtra);
        }

        return JsonConvert.DeserializeObject<TExtra>(json);
    }
}

And call it like:

// Put extra
MyDto dto = ...; // Get from server or something
StartActivity(new Intent(this, typeof(MyActivity)).PutExtra("MY_EXTRA", dto))

// Get the extra back
var myDto = Intent.GetExtra<MyDto>("MY_EXTRA");

Answer:

The key to getting an efficient interaction between activities in Xamarin Android, is to get a reference “connection” between two activities.

This is not done quite as one would expect (passing data through constructor parameters). And because the framework is responsible for instantiating the second activity, you don’t even get a reference to it.

This is a BIG problem (at least to me personally). Now what I might suggest, is something along the following lines:

public static class NavigationBridge
{
    public static Action<object> FinishedNavigating { get; set; }
}

public class Activity1
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Main);

        someButton.Click += (sender, args) => 
        {
            NavigationBridge.FinishedNavigating = activity2 =>
            {
                Activity2 activity = (Activity2)activity2;
                // Vuola, you now have a reference to the instance.
                // Use this reference to set public properties in Activity2, 
                // using data from *this* Activity1
            };
            var intent = new Intent(this, typeof(Activity2));
            this.StartActivity(intent);
        };
    }
}

public class Activity2
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.Main);
        if (NavigationBridge.FinishedNavigating != null)
        {
            NavigationBridge.FinishedNavigating(this);
            NavigationBridge.FinishedNavigating = null;
        }
    }
}

I don’t think there’s any easy way to avoid a static anywhere, but this way, you’ll only ever need 1, because you define the job to execute, in that FinishedNavigating action.

Answer:

I did it with a different approach that was suggested on a forum from Xamarin.

The solution was to make the object that needed to be sent to the next Activity public so it could be accessed from anywhere in the application.

public class RootObject
{
    private RootObject()
    {
    }

    private static RootObject _instance;
    public static RootObject Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new RootObject();
             }
            return _instance;
        }
    }

    public Form Form { get; set; }
}

And the object can be accessed anywhere with the following code:

Form myForm = RootObject.Instance.Form;