Passing Data from ParentViewModel to ChildViewModel - c#

I am trying to pass data from a view model (VM) to ParentViewPagerVM to ChildTabVM. In my first VM I get the data which I want to pass to ChildTabVM. I could not find any solution how to do that.
FirstViewModel.cs
public MvxCommand GoToLocationInfoCommand
{
get
{
return new MvxCommand(
() => ShowViewModel<LocationViewPager>(new { param = "Test"}));
}
}
ParentViewPagerViewModel.cs
public void Init(string param)
{
Debug.WriteLine("Paramter: " + ZipCode);
}
ParentViewPagerFragment.cs
if (viewPager != null)
{
var fragments = new List<MvxCachingFragmentStatePagerAdapter.FragmentInfo>
{
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"Tab1", typeof(Child1Fragment), typeof(Child1ViewModel)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"Tab2", typeof(Child2Fragment), typeof(Child2ViewModel)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"Tab3", typeof(Child3Fragment), typeof(Child3ViewModel))
};
viewPager.Adapter = new MvxCachingFragmentStatePagerAdapter(
Activity, ChildFragmentManager, fragments);
}
Since I am making a tabView, I can't find a way to pass data from my ParentViewPagerVM to Child1VM. Any idea?

MvxCachingFragmentStatePagerAdapter.FragmentInfo constructor provides an option for passing parameters to the ViewModel type you want to have constructed.
public FragmentInfo(
string title,
Type fragmentType,
Type viewModelType,
object parameterValuesObject = null);
Implementation Example
Assuming you have a property ZipCode on your ParentViewPagerViewModel you can pass as a parameter.
var fragments = new List<MvxCachingFragmentStatePagerAdapter.FragmentInfo>
{
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
title: "Tab1",
fragmentType: typeof(Child1Fragment),
viewModel: typeof(Child1ViewModel),
parameterValuesObject: new { zipCode = ViewModel.ZipCode})
};
Then in your Child ViewModel retrieve it via the Init
public void Init(int zipCode)
{
// Do stuff with zipCode
}

Related

How to use an instantiated object from one method to another?

I have these two methods:
public MessagesPage(ContactModel input)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
ConversationsList = new ObservableCollection<ConversationModel>();
ContactModel ConversationPartner = new ContactModel();
ConversationPartner = input;
...
}
And the input which is the parameter in the method above, I also would like to use it in this method (they're in the same class)
private async void Send()
{
Guid guid = Guid.NewGuid();
string ID = guid.ToString();
ConversationModel conversationObject = new ConversationModel()
{
id = ID,
converseeID = dataClass.loggedInUser.uid,
message = Message,
created_at = DateTime.UtcNow
};
await CrossCloudFirestore.Current
.Instance
.GetCollection("contacts")
.GetDocument(ConversationPartner.id)
.GetCollection("conversations")
.GetDocument(ID)
.SetDataAsync(conversationObject);
Message = string.Empty;
}
because as you can see, in .GetDocument, it needs the id of ConversationPartner. I would get object reference not set to an instance of an object when the Send() method is called because it doesn't have access to ContactModel input in MessagesPage above.
you need to declare ConversationPartner as a class level variable, not inside of of a specific method. This will make it available throughout your class
ContactModel ConversationPartner;
public MessagesPage(ContactModel input)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
ConversationsList = new ObservableCollection<ConversationModel>();
ConversationPartner = input;
...
}

In gRPC unable to bind enum values from message to dontnet core Dto

I need to define a string array type dataType in Grpc message. not sure how to do. right now i am doing it as a
repeated string Title= 1,
here i need name field as string array Type. But it is showing error that it is, field is readonly type when bind data in it:
public override async Task<UserResponse> CreateUser(
UserModel request, ServerCallContext context)
{
var eventResponse = new UserResponse();
var createCmd = new CreateUserCommand
{
Model = new UserDto
{
Title = request.Title,
Id = request.Id,
}
}
}
here in title i need to bind data
The generated code from protoc here gives you something like:
private readonly RepeatedField<string> title_ = new RepeatedField<string>();
[DebuggerNonUserCodeAttribute]
public RepeatedField<string> Title {
get { return title_; }
}
So: Title is indeed read-only. This means that instead of assigning it, you should explore what APIs exist for adding to it - i.e.
var user = new UserDto
{
Id = request.Id,
}
user.Title.Add(request.Title);
// or AddRange, etc
You may still be able to use initializer syntax, too:
new UserDto
{
Id = request.Id,
Title = { request.Title }
}
(which is an .Add)

Casting mistake

Currently, in course, I am trying to check the LandCode from the class Landen to get the cities from the selectedItem land, but I am parsing something wrong.
public partial class Landen
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Landen()
{
this.Steden = new HashSet<Steden>();
this.Talen = new HashSet<Talen>();
}
public string LandCode { get; set; }
public string Naam { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Steden> Steden { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Talen> Talen { get; set; }
}
public MainWindow()
{
InitializeComponent();
var context = new LandenStedenTalenEntities();
landenListBox.ItemsSource = (from Landen in context.Landen select Landen.Naam).ToList();
}
private void landenListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
using (var entities = new LandenStedenTalenEntities())
{
List<string> steden = new List<string>();
var landcode = ((Landen)landenListBox.SelectedItem).LandCode.ToString();
var gekozenland = entities.Landen.Find(landcode);
foreach(var stad in gekozenland.Steden)
{
steden.Add(stad.Naam);
}
stedenInLandenListBox.ItemsSource = steden.ToList();
}
}
Exception:
Unable to cast object of type 'System.String' to type 'TestEFDieter.Landen'.
I want to add them to a list and show them in a second Listbox.
Can anyone help me out? Thank you.
I would suggest you modify the code inside of the constructor so that the landenListBox will contain actual Landen object and displays only the Naam as it's item.
Change the code in the constructor to this:
public MainWindow()
{
InitializeComponent();
var context = new LandenStedenTalenEntities();
landenListBox.ItemsSource = context.Landen.ToList();
landenListBox.DisplayMemberPath = "Naam";
}
Adding DisplayMemberPath will inform ListBox to display that particular property as an item instead of calling ToString() method on that object.
Now in your later code you do not have to change much, just remove ToList() and since you're using EntityFramework you should insert the whole model in it's Find() method but it's useless since you already have that object loaded. You can just retrieve stad from it directly and display it in the same way Landen is displayed:
private void landenListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var landen = landenListBox.SelectedItem as Landen; // safe cast just in case
if (landen != null && landen.Steden != null ) // null checks
{
stedenInLandenListBox.ItemsSource = landen.Steden.ToList(); // in case it's proxy object
stadenInLandenListBox.DisplayMemberPath = "Naam";
}
}
I suppose you want to get that instance of Landen which corresponds the selected item in your list. As the elements in the listbox are just strings that represent the Naam-property of every Landen, you could simply iterate your list of Landen and get that one with the desired Naam:
var selectedLanden = landenList.FirstOrDefault(x => x.Naam == landenListBox.SelectedItem);
if(selectedLanden != null)
{
var landCode = selectedLanden.LandCode;
// ...
}
However as selectedLanden already is an instance of Landen, you won´t need to find it again by its LandCode. Thus your code boils donw to this:
List<string> steden = new List<string>();
var selectedLanden = landenList.FirstOrDefault(x => x.Naam == landenListBox.SelectedItem);
if(selectedLanden != null)
{
foreach(var stad in selectedLanden.Steden)
{
steden.Add(stad.Naam);
}
}
stedenInLandenListBox.ItemsSource = steden.ToList();
or a bit shorter:
stedenInLandenListBox.ItemsSource = selectedLanden.SelectMany(x => x.Steden.Select(y => y.Naam)).ToList();
For this to work you of course have to store a reference to the list of Landen somewehere in your class:
class MainWindow
{
List<Landen> landenList;
public MainWindow()
{
InitializeComponent();
this.landenList = new LandenStedenTalenEntities();
landenListBox.ItemsSource = (from Landen in this.landenList select Landen.Naam).ToList();
}
}

How to pass string data from Master-Detail Page in Xamarin Forms

I am trying to make a Master-Detail Navigation using help of this github example. The relevant code sample from my project is -
MasterPageItem.cs
namespace Demo.MenuItems
{
public class MasterPageItem
{
public string Title { get; set; }
public string IconSource { get; set; }
public Type TargetType { get; set; }
}
}
MainPage.Xaml.cs
public partial class MainPage : MasterDetailPage
{
public MainPage()
{
InitializeComponent();
masterPage.ListView.ItemSelected += OnItemSelected;
if (Device.RuntimePlatform == Device.UWP)
{
MasterBehavior = MasterBehavior.Popover;
}
Detail = new NavigationPage(new HomePage());
}
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
}
MasterPage.Xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterPage : ContentPage
{
public ListView ListView { get { return listView; } }
public MasterPage()
{
InitializeComponent();
var masterPageItems = new List<MasterPageItem>();
masterPageItems.Add(new MasterPageItem
{
Title = "Help",
IconSource = "icon-1.jpg",
TargetType = typeof(WebPage)
});
listView.ItemsSource = masterPageItems;
}
}
It works if no data is required to pass in detail page. However, I need to pass one string value url in page WebPage, but I am unable to figure out how to pass string value or any data in following line -
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
For e.g., following is the code sample for the page WebPage -
public WebPage (string URL)
{
InitializeComponent ();
Browser.Source = URL;
}
Here I am unable to figure out, how should I pass url from Master-Detail Navigation?
Generalized Way :
//This will create instance of the page using the parameterized constructor you defined in each DetailPages
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType, myStringParam));
//Your Each Detail Page should have parametrized constructor.
public MyPage (string param)
{
InitializeComponent ();
Browser.Source = param;
}
Here, you can serialize the c# object and pass JSON string into myStringParam. Your page receives this in page’s parameterized constructor you defined and there, you can deserialize, thus you can pass complex objects as JSON into a page as well as simple string.
If you want to add parameterized constructor in only one DetailPage then:
if(item.TargetType == typeof(WebPage))
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType, myStringParam));
}
else
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
}
//Your Page would be:
public WebPage (string URL)
{
InitializeComponent ();
Browser.Source = URL;
}
Yes you can Activator.CreateInstance Method as many overloads
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType, url));
All the overloads can be found here
Activator.CreateInstance Method
The one you want is here
Activator.CreateInstance Method (Type, Object[])
Parameters
type Type: System.Type
The type of object to create
args Type: System.Object[]
An array of arguments that match in number,
order, and type the parameters of the constructor to invoke. If args
is an empty array or null, the constructor that takes no parameters
(the default constructor) is invoke

Error: The non-generic type 'GenericTypeIndicator' cannot be used type arguments

I am new to the implementation of queries in firebase. Below in my code, i am fetching data using snapshot into my list view. I am passing List but reading further, I came across that List cannot be casted to Datasnaphot and the answer requires the use of GenericTypeIndicator. I tried using that to get my data into my listView but that would not do the trick.
Why do i get this error Error: The non-generic type 'GenericTypeIndicator' cannot be used type arguments.?
Activity
GenericTypeIndicator<List<GetUserContent>>messages = new GenericTypeIndicator<List<GetUserContent>>(){};
public void OnDataChange(DataSnapshot snapshot)
{
messages.Clear();
var items = snapshot.Child(post_key);
messages.Add((GetUserContent)items);
ViewAdapter adapter = new ViewAdapter(this, messages);
Console.WriteLine("Data being fetched " + items);
mylistview.Adapter = adapter;
}
Adapter
internal class ViewAdapter: BaseAdapter
{
private List<GetUserContent> messages;
public ViewAdapter(Activity activity,List<GetUserContent> messages)
{
this.activity = activity;
this.messages = messages;
}
GetUserContent Class
internal class GetUserContent
{
public string Email { get; set; }
public string Message { get; set; }
public GetUserContent()
{
}
public GetUserContent(string Email, string Message){
this.Email = Email;
this.Message = Message;
}
}
post
mLike.Child(post_key).Child(mAuth.CurrentUser.Uid).Push().SetValue(edtChat.Text);
Why do i get this error Error: The non-generic type 'GenericTypeIndicator' cannot be used type arguments.?
I don't know how is this library is designed, and why GenericTypeIndicator can not be used with type arguments. You can raise a question on GooglePlayServicesComponents Github Repo.
But referring to Work with Lists of Data. the object you retrieved is a Java.Lang.IIterable<DataSnapShot> object,which can not be casted to List directly. You can use the following codes to cast to List:
public void OnDataChange(DataSnapshot snapshot)
{
var children = snapshot.Child("users")?.Children?.ToEnumerable<DataSnapshot>();
List<HashMap> list = new List<HashMap>();
foreach (DataSnapshot s in children)
{
list.Add((HashMap)s.Value);
}
}
And my Firebase Database structure is like below:
Update:
Currently, I am not able to post my self-defined object directly, I keep getting
Serialization error:
`Firebase.Database.DatabaseException: No properties to serialize found on class md5f5cedb36bdcec41a9de7aed50a9aade0.User`.
But I workaround it by using HashMap as intermediate type:
public class User:Java.Lang.Object
{
public User() {}
// convert current User to HashMap
public HashMap ToMap()
{
HashMap map = new HashMap();
map.Put("username", this.username);
map.Put("email", this.email);
return map;
}
public string username;
public string email;
public User(string username, string email)
{
this.username = username;
this.email = email;
}
}
And Post the Data like this:
FirebaseDatabase.Instance
.Reference
.Child("users")
.Push()
.SetValue(new User("username", "email").ToMap());
And it is not completed to retrieve the HashMap and convert it back to User list:
public void OnDataChange(DataSnapshot snapshot)
{
var children = snapshot.Child("users")?.Children?.ToEnumerable<DataSnapshot>();
List<User> list = new List<User>();
HashMap map;
foreach (DataSnapshot s in children)
{
map = (HashMap)s.Value;
list.Add(new User(map.Get("username")?.ToString(), map.Get("email")?.ToString()));
}
}

Categories