Can't start a simple view with MVVMCross Xamarin IOS - c#

Please help!
Just spend more than 8 hours trying to get ONE simple view presented in iOS, but it is not working. No idea whats going on.
This is my code.
Setup.cs
public class Setup : MvxIosSetup
{
public Setup(MvxApplicationDelegate applicationDelegate, UIWindow window)
: base(applicationDelegate, window)
{
}
public Setup(MvxApplicationDelegate applicationDelegate, IMvxIosViewPresenter presenter)
: base(applicationDelegate, presenter)
{
}
protected override void InitializeIoC()
{
base.InitializeIoC();
Mvx.RegisterSingleton<ILanguageService>(() => new LanguageService());
Mvx.RegisterSingleton<ISoundPlayerService>(() => new SoundPlayerService());
Mvx.RegisterSingleton<IAlertService>(() => new AlertService());
Mvx.RegisterSingleton<ITagManagerService>(() => new TagManagerService());
Mvx.RegisterSingleton<IBackCompatService>(() => new BackCompatService());
Mvx.RegisterSingleton<IPlatformService>(() => new PlatformService());
}
protected override IMvxApplication CreateApp()
{
return new App();
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
}
AppDelegate.cs
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var presenter = new MvxIosViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
}
SplashView
public partial class SplashView : MvxViewController
{
public SplashView() : base("SplashView", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var set = this.CreateBindingSet<SplashView, SplashViewModel>();
set.Apply();
}
public new SplashViewModel ViewModel
{
get { return (SplashViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
}
Now the error I'm getting when I run this is:
MvvmCross.Platform.Exceptions.MvxException: Failed to construct and initialize ViewModel for type SplashViewModel from locator MvxDefaultViewModelLocator - check InnerException for more information

Related

FreshMVVM Switching Navigation Stacks Between ContentPage and TabbedPage

I'm converting an app to use FreshMVVM from a non-MVVM format. When the app launches, there is a login page, then after login, a tabbed page with modal pages called from the buttons on each tabbed page.
Given the process I'm following, I figured that this would be a perfect option to use Michael Ridland's process to switch out NavigationStacks (https://github.com/rid00z/FreshMvvm#switching-out-navigationstacks-on-the-xamarinforms-mainpage), but the steps appear to be missing quite a few important instructions, and these steps are not in the sample app on the GitHub.
In my App.xaml.xs file, I have the following code:
public App()
{
InitializeComponent();
var loginPage = FreshMvvm.FreshPageModelResolver.ResolvePageModel<LoginPageModel>();
var loginContainer = new STNavigationContainer(loginPage, NavigationContainerNames.AuthenticationContainer);
var homePageViewContainer = new FreshTabbedNavigationContainer(NavigationContainerNames.MainContainer);
MainPage = loginContainer;
}
public FreshTabbedNavigationContainer(string navigationServiceName)
{
NavigationServiceName = navigationServiceName;
RegisterNavigation();
}
protected void RegisterNavigation()
{
FreshIOC.Container.Register<IFreshNavigationService>(this, NavigationServiceName)
}
public void SwitchOutRootNavigation(string navigationServiceName)
{
IFreshNavigationService rootNavigation =
FreshIOC.Container.Resolve<IFreshNavigationService>(navigationServiceName);
}
public void LoadTabbedNav()
{
var tabbedNavigation = new FreshTabbedNavigationContainer();
tabbedNavigation.AddTab<CharactersPageModel>("Characters", "characters.png");
tabbedNavigation.AddTab<AdventuresPageModel>("Adventures", "adventures.png");
tabbedNavigation.AddTab<AccountPageModel>("Account", "account.png");
MainPage = tabbedNavigation;
}
public class NavigationContainerNames
{
public const string AuthenticationContainer = "AuthenticationContainer";
public const string MainContainer = "MainContainer";
}
This seems to match with the steps provided in the ReadMe.md, but the calls to NavigationServiceName return an error, since there's no instruction on creating such a class or what it should contain, nor am I clear on where the FreshTabbedNavigationContainer or SwitchOutRootNavigation would be called.
Has anyone been able to get this to work? What steps am I missing on this?
EDIT: I forgot that I have extended the FreshNavigationContainter class and the FreshNavigationPage class. Here are my extended classes:
STNavigationContainer:
public class STNavigationContainer : FreshNavigationContainer
{
public STNavigationContainer(Page page) : base(page)
{
}
public STNavigationContainer(Page page, string navigationPageName) : base(page, navigationPageName)
{
}
protected override Page CreateContainerPage(Page page)
{
if (page is NavigationPage || page is MasterDetailPage || page is TabbedPage)
return page;
return new STNavigationPage(page);
}
}
STNavigationPage:
public class STNavigationPage : NavigationPage
{
public STNavigationPage()
{
}
public STNavigationPage(Page page) : base(page)
{
BarBackgroundColor = Color.FromHex("#2DAFEB");
BarTextColor = Color.FromHex("#C9371D");
}
}
Edit 2: Re-reading https://michaelridland.com/xamarin/implementing-freshmvvm-mvvm-xamarin-forms/ and the Github post, I was able to figure out that I needed to do this in a Custom Navigation Service, so here is my updated code.
App.xaml.cs:
public partial class App : Application
{
public static Account account = new Account();
public App()
{
InitializeComponent();
FreshIOC.Container.Register<IDatabaseService, DatabaseService>();
if (account.Equals(null))
{
LoadSingleNav();
}
else
{
LoadTabbedNav();
}
var navPage = new NavigationPage(new LoginPage())
{
BarBackgroundColor = Color.FromHex("#2DAFEB"),
BarTextColor = Color.FromHex("#C9371D")
};
NavigationPage.SetHasNavigationBar(navPage.CurrentPage, false);
MainPage = navPage;
MainPage = loginContainer;
}
public FreshTabbedNavigationContainer(string navigationServiceName)
{
NavigationContainerNames = navigationServiceName;
RegisterNavigation();
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
CustomNavService.cs:
public class CustomNavService : NavigationPage, IFreshNavigationService
{
FreshTabbedNavigationContainer _tabbedNavigationPage;
Page _charactersPage, _adventuresPage, _accountPage;
public CustomNavService(Page page) : base (page)
{
NavigationServiceName = "CustomNavService";
LoadTabbedNav();
CreateLoginPage();
RegisterNavigation();
}
public string NavigationServiceName { get; private set; }
public void NotifyChildrenPageWasPopped()
{
throw new NotImplementedException();
}
public async Task PopPage(bool modal = false, bool animate = true)
{
if (modal)
await Navigation.PopModalAsync (animate);
else
await Navigation.PopAsync (animate);
}
public async Task PopToRoot(bool animate = true)
{
await Navigation.PopToRootAsync(animate);
}
public async Task PushPage(Page page, FreshBasePageModel model, bool modal = false, bool animate = true)
{
if (modal)
await Navigation.PushModalAsync(page, animate);
else
await Navigation.PushAsync(page, animate);
}
public Task<FreshBasePageModel> SwitchSelectedRootPageModel<T>() where T : FreshBasePageModel
{
IFreshNavigationService rootNavigation =
FreshIOC.Container.Resolve<IFreshNavigationService>(NavigationServiceName);
}
public void LoadTabbedNav()
{
_tabbedNavigationPage = new FreshTabbedNavigationContainer();
_charactersPage = _tabbedNavigationPage.AddTab<CharactersPageModel>("Characters", "characters.png");
_adventuresPage = _tabbedNavigationPage.AddTab<AdventuresPageModel>("Adventures", "adventures.png");
_accountPage = _tabbedNavigationPage.AddTab<AccountPageModel>("Account", "account.png");
this = _tabbedNavigationPage;
}
private void CreateLoginPage()
{
var loginPage = FreshPageModelResolver.ResolvePageModel<LoginPageModel>(null);
var loginContainer = new STNavigationContainer(loginPage, CustomNavService.NavigationContainerNames.AuthenticationContainer);
var homePageViewContainer = new FreshTabbedNavigationContainer(CustomNavService.NavigationContainerNames.MainContainer);
}
protected void RegisterNavigation()
{
FreshIOC.Container.Register<IFreshNavigationService>(this, NavigationServiceName);
}
public class NavigationContainerNames
{
public const string AuthenticationContainer = "AuthenticationContainer";
public const string MainContainer = "MainContainer";
}
}
The SwitchSelectedRootPageModel<T> task in my Custom Navigation Service is showing that a return is needed, and I'm not quite clear on what the LoadTabbedNav is supposed to include with the this; the example shows this.Detail, but that's showing as an invalid reference.
Looking through our FreshMvvm implementation, our SwitchSelectedRootPageModel looks like this:
public Task<FreshBasePageModel> SwitchSelectedRootPageModel<T>() where T : FreshBasePageModel
{
return Task.FromResult<FreshBasePageModel>(null);
}
Our code correctly uses
this.Detail = _tabbedNavigationPage;
So if you are getting an invalid reference, there must be something else missing.

Xamarin initializing ViewModel prior to PushAsync()

I have a Xamarin App with a login page, I register my DB, Alert, Media, and Navigation Services with ServiceContainer. I then try to navigate to to the Login Page but before it appears on the screen it calls the constructor for every ViewModel and I can't figure out why???
public partial class App : Application
{
INavigationService NavigationService { get; set; }
public App()
{
InitializeComponent();
RegisterRepositories();
RegisterServices();
NavigationService.ReplaceRoot(ServiceContainer.GetInstance<LoginViewModel>(), false);
}
void RegisterServices()
{
ServiceContainer.Register<IAlertService>(() => new AlertService());
ServiceContainer.Register<IMediaService>(() => new MediaService());
NavigationService = new NavigationService();
NavigationService.AutoRegister(typeof(App).Assembly);
ServiceContainer.Register(NavigationService);
}
void RegisterRepositories()
{
ServiceContainer.Register<IUserProfileRepository>(() => new UserProfileRepository());
}
ServiceContainer:
public static class ServiceContainer
{
static readonly Container _container = new Container();
public static void Register<TService, TImplementation>(bool transient = false) where TService : class where TImplementation : class, TService
{
Lifestyle style = transient ? Lifestyle.Transient : Lifestyle.Singleton;
_container.Register<TService, TImplementation>(style);
}
public static void Register<TService>(Func<TService> generator, bool transient = false) where TService : class
{
Lifestyle style = transient ? Lifestyle.Transient : Lifestyle.Singleton;
_container.Register(generator, style);
}
public static void Register(Type serviceType, Type implementationType, bool isTransient = false)
{
if (isTransient)
{
_container.Register(serviceType, implementationType, Lifestyle.Transient);
}
else
{
_container.Register(serviceType, implementationType, Lifestyle.Singleton);
}
}
public static void Register<TService>(TService instance) where TService : class
{
_container.RegisterInstance(instance);
}
public static T GetInstance<T>() where T : class
{
try
{
return _container.GetInstance<T>();
}
catch (ActivationException)
{
return null;
}
}
internal static T GetRequiredInstance<T>() where T : class
{
return GetInstance<T>() ?? throw new InvalidOperationException(
$#"A required dependency injection class is missing ({typeof(T).FullName}).");
}
}
NavigationService:
public interface IViewFor
{
object ViewModel { get; set; }
}
public interface IViewFor<T> : IViewFor where T : BaseViewModel
{
new T ViewModel { get; set; }
}
public class NavigationService : INavigationService
{
INavigation FormsNavigation => Application.Current.MainPage.Navigation;
readonly Dictionary<Type, Type> _viewModelViewDictionary = new Dictionary<Type, Type>();
public void AutoRegister(Assembly asm)
{
// Loop through everything in the assembly that implements IViewFor<T>
foreach (var type in asm.DefinedTypes.Where(dt => !dt.IsAbstract &&
dt.ImplementedInterfaces.Any(ii => ii == typeof(IViewFor))))
{
// Get the IViewFor<T> portion of the type that implements it
var viewForType = type.ImplementedInterfaces.FirstOrDefault(
ii => ii.IsConstructedGenericType &&
ii.GetGenericTypeDefinition() == typeof(IViewFor<>));
// Register it, using the T as the key and the view as the value
Register(viewForType.GenericTypeArguments[0], type.AsType());
ServiceContainer.Register(viewForType.GenericTypeArguments[0], viewForType.GenericTypeArguments[0], true);
}
}
public void Register(Type viewModelType, Type viewType)
{
if (!_viewModelViewDictionary.ContainsKey(viewModelType))
{
_viewModelViewDictionary.Add(viewModelType, viewType);
}
}
public void ReplaceRoot<T>(bool withNavigationEnabled = true) where T : BaseViewModel
{
ReplaceRoot(ServiceContainer.GetInstance<T>(), withNavigationEnabled);
}
public void ReplaceRoot(BaseViewModel viewModel, bool withNavigationEnabled = true)
{
if (InstantiateView(viewModel) is Page view)
{
if (withNavigationEnabled)
{
Application.Current.MainPage = new NavigationPage(view);
}
else
{
Application.Current.MainPage = view;
}
}
}
public Task PopAsync() => FormsNavigation.PopAsync(true);
public Task PopToRootAsync(bool animate) => FormsNavigation.PopToRootAsync(animate);
public Task PushAsync(BaseViewModel viewModel) => FormsNavigation.PushAsync((Page)InstantiateView(viewModel));
IViewFor InstantiateView(BaseViewModel viewModel)
{
var viewModelType = viewModel.GetType();
var viewType = _viewModelViewDictionary[viewModelType];
var view = (IViewFor)Activator.CreateInstance(viewType);
view.ViewModel = viewModel;
return view;
}
}

Passing data to fragment viewmodel mvvmcross

so I have an activity that has a tabbed page with 2 fragments.
public class RecipeDetailActivity : BaseFragmentActivity<RecipeDetailViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.RecipeDetailView);
AttachActionBar();
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
SupportActionBar.Title = "Recipe details";
var viewPager = FindViewById<ViewPager>(Resource.Id.main_view_pager);
if (viewPager != null)
{
var fragments = new List<MvxViewPagerFragmentInfo>();
fragments.Add(
new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel)));
fragments.Add(
new MvxViewPagerFragmentInfo("Flavours", typeof(RecipeIngridientsFragment), typeof(RecipeIngridientsViewModel)));
viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments);
viewPager.Adapter = new MvxFragmentPagerAdapter(this, SupportFragmentManager, fragments);
var tabLayout = FindViewById<TabLayout>(Resource.Id.main_tablayout);
tabLayout.SetupWithViewPager(viewPager);
}
}
}
I show this page using the following code.
private void SelectRecipe(RecipeModel recipe)
{
var recipeJson = JsonConvert.SerializeObject(recipe);
ShowViewModel<RecipeDetailViewModel>(new { recipe = recipeJson });
}
Now what I would like is to pass some data to child view models.
RecipeFlavoursViewModel
RecipeIngridientsViewModel
I've tried so far :
Using parameterValueObject
fragments.Add(
new MvxViewPagerFragmentInfo("Ingrediente", typeof(RecipeFlavoursFragment), typeof(RecipeFlavoursViewModel), new { recipe = ViewModel.Recipe }));
Using IMvxBundle
In RecipeDetailViewModel
protected override void SaveStateToBundle(IMvxBundle bundle)
{
bundle.Data["Recipe"] = JsonConvert.SerializeObject(Recipe);
base.SaveStateToBundle(bundle);
}
In RecipeIngridientsViewModel
protected override void InitFromBundle(IMvxBundle parameters)
{
base.InitFromBundle(parameters);
if (parameters.Data.Count != 0)
{
Recipe = JsonConvert.DeserializeObject<RecipeModel>(parameters.Data["recipe"]);
}
}
None of them have worked so far. Any ideas what am I doing wrong? Do I have to use the navigation service from MvvmCross 5 to be able to use InitFromBundle and SaveStateToBundle.
InitFromBundle it's called everytime my fragments is displayed, but SaveStateToBundle from RecipeDetailViewModel never gets called.
In order to do this, you could take advantage of the MvxViewPagerFragmentPresentationAttribute so that the Presenter takes the responsibility to show the fragments and you just show the ViewModels passing the Recipe parameter as any other but it has some minor bugs for the moment.
However one way to solve this is to have in your RecipeDetailViewModel properties with the fragments' ViewModels you want to have in your ViewPager and load them in the Initialize so then you can reference them from your RecipeDetailActivity:
Using Mvx 5 you can use new Navigation to show the ViewModels. If the details are openned from RecipeListViewModel then:
public class RecipeDetailViewModelArgs
{
public RecipeDetailViewModelArgs(RecipeModel recipe)
{
this.Recipe = recipe;
}
public RecipeModel Recipe { get; }
}
public class RecipeListViewModel : MvxViewModel
{
private readonly IMvxNavigationService navigationService;
public RecipeListViewModel(IMvxNavigationService navigationService)
{
this.navigationService = navigationService;
}
private async Task SelectRecipe(RecipeModel recipe)
{
await this.navigationService.Navigate<RecipeDetailViewModel, RecipeDetailViewModelArgs>(new RecipeDetailViewModelArgs(recipe));
}
}
Then in your details ViewModel you just cache the recipe, load the children ViewModels (ingredients and flavours) and set the recipe to them:
public class RecipeDetailViewModel : MvxViewModel<RecipeDetailViewModelArgs>
{
private readonly IMvxViewModelLoader mvxViewModelLoader;
private readonly IMvxJsonConverter jsonConverter;
private RecipeModel recipe;
public RecipeDetailViewModel(IMvxViewModelLoader mvxViewModelLoader, IMvxJsonConverter jsonConverter)
{
this.mvxViewModelLoader = mvxViewModelLoader;
this.jsonConverter = jsonConverter;
}
public override void Prepare(RecipeDetailViewModelArgs parameter)
{
this.recipe = parameter.Recipe;
}
protected override void SaveStateToBundle(IMvxBundle bundle)
{
base.SaveStateToBundle(bundle);
bundle.Data["RecipeKey"] = this.jsonConverter.SerializeObject(this.recipe);
}
protected override void ReloadFromBundle(IMvxBundle state)
{
base.ReloadFromBundle(state);
this.recipe = this.jsonConverter.DeserializeObject<RecipeModel>(state.Data["RecipeKey"]);
}
public override async Task Initialize()
{
await base.Initialize();
this.InitializeChildrenViewModels();
}
public RecipeFlavoursViewModel FlavoursViewModel { get; private set; }
public RecipeIngridientsViewModel IngredientsViewModel { get; private set; }
protected virtual void InitializeChildrenViewModels()
{
// Load each childre ViewModel and set the recipe
this.FlavoursViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeFlavoursViewModel>(null, null), null);
this.FlavoursViewModel.Recipe = this.recipe;
this.IngredientsViewModel = this.mvxViewModelLoader.LoadViewModel(new MvxViewModelRequest<RecipeIngridientsViewModel>(null, null), null);
this.FlavoursViewModel.Recipe = this.recipe;
}
}
Then when you load the ViewPager you can take advantage from the other constructor of MvxViewPagerFragmentInfo => public MvxViewPagerFragmentInfo (string title, string tag, Type fragmentType, IMvxViewModel viewModel, object parameterValuesObject = null) so you can pass the ViewModels previously loaded:
this.viewPager = view.FindViewById<ViewPager>(Resource.Id.viewPagerDetails);
if (viewPager != null)
{
var fragments = new List<MvxViewPagerFragmentInfo>();
fragments.Add(new MvxViewPagerFragmentInfo("Ingredients", "RecipeIngridientsViewModelTag", typeof(RecipeIngridientsView), this.ViewModel.IngridientsViewModel));
fragments.Add(new MvxViewPagerFragmentInfo("Flavours", "RecipeFlavoursViewModelTag", typeof(RecipeFlavoursView), this.ViewModel.FlavoursViewModel));
this.viewPager.Adapter = new MvxFragmentPagerAdapter(this.Activity, this.ChildFragmentManager, fragments);
}
That's it.
BTW if you don't want to use Navigation or you are not using Mvx 5.x then you just Initialize the children ViewModels in the void Start() method.
And to conclude if you want to change values of your Recipe from the children one simple way is to have a Singleton initialized with your Recipe and then you just Inject the singleton in the constructors so you always have the reference to the same Recipe and you don't have to pass the Recipe object forth & back to those ViewModels and merge the changes made from each of them. More info in MvvmCross: Accessing models by reference from everywhere
HIH

WinPhone 8.1 project with Xamarin.Forms and Caliburn.Micro failes to create view

I have a Xamarin.Forms project with a WinPhone 8.1 project that uses Caliburn.Micro.
This is my App.xaml:
<caliburn:CaliburnApplication x:Class="MyProject.WinPhone.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:caliburn="using:Caliburn.Micro"
xmlns:local="using:MyProject.WinPhone" />
This is my App.xaml.cs:
public sealed partial class App : Caliburn.Micro.CaliburnApplication
{
private WinRTContainer container;
private TransitionCollection transitions;
public App()
{
InitializeComponent();
}
protected override void Configure()
{
container = new WinRTContainer();
container.RegisterWinRTServices();
//TODO: Register your view models at the container
container.PerRequest<MainViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new Exception("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
container.BuildUp(instance);
}
protected override void PrepareViewFirst(Frame rootFrame)
{
container.RegisterNavigationService(rootFrame);
}
protected override IEnumerable<Assembly> SelectAssemblies()
{
return new[] { typeof(MainViewModel).GetTypeInfo().Assembly, typeof(Windows.Foundation.AsyncActionCompletedHandler).GetTypeInfo().Assembly };
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
return;
try
{
DisplayRootView<MainView>();
//DisplayRootView<MainPage>();
//DisplayRootViewFor<MainViewModel>();
}
catch (Exception ex)
{
var x = 8;
throw;
}
}
}
These are the NuGet packages:
It compiles, but when I try to run it, I get the following exception at DisplayRootView:
Could not find Windows Runtime type
'Windows.Foundation'.":"Windows.Foundation
at System.StubHelpers.WinRTTypeNameConverter.GetTypeFromWinRTTypeName(String typeName, Boolean& isPrimitive)
at System.StubHelpers.SystemTypeMarshaler.ConvertToManaged(TypeNameNative* pNativeType, Type& managedType)
at Windows.UI.Xaml.Controls.Frame.Navigate(Type sourcePageType, Object parameter)
at Caliburn.Micro.CaliburnApplication.DisplayRootView(Type viewType, Object paramter)
at Caliburn.Micro.CaliburnApplication.DisplayRootView[T](Object parameter)
at MyProject.WinPhone.App.OnLaunched(LaunchActivatedEventArgs args)
Can you tell me what I should do about this?
UPDATE:
The root of the problem lies in this part of the code:
private global::MyProject.WinPhone.MyProject_WinPhone_XamlTypeInfo.XamlTypeInfoProvider _provider;
public global::Windows.UI.Xaml.Markup.IXamlType GetXamlType(global::System.Type type)
{
if(_provider == null)
{
_provider = new global::MyProject.WinPhone.MyProject_WinPhone_XamlTypeInfo.XamlTypeInfoProvider();
}
return _provider.GetXamlTypeByType(type);
}
public global::Windows.UI.Xaml.Markup.IXamlType GetXamlType(string fullName)
{
if(_provider == null)
{
_provider = new global::MyProject.WinPhone.MyProject_WinPhone_XamlTypeInfo.XamlTypeInfoProvider();
}
return _provider.GetXamlTypeByName(fullName);
}
public global::Windows.UI.Xaml.Markup.XmlnsDefinition[] GetXmlnsDefinitions()
{
return new global::Windows.UI.Xaml.Markup.XmlnsDefinition[0];
}
}
}
namespace MyProject.WinPhone.MyProject_WinPhone_XamlTypeInfo
{
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks", "4.0.0.0")]
internal partial class XamlTypeInfoProvider
{
public global::Windows.UI.Xaml.Markup.IXamlType GetXamlTypeByType(global::System.Type type)
{
global::Windows.UI.Xaml.Markup.IXamlType xamlType;
if (_xamlTypeCacheByType.TryGetValue(type, out xamlType))
{
return xamlType;
}
int typeIndex = LookupTypeIndexByType(type);
if(typeIndex != -1)
{
xamlType = CreateXamlType(typeIndex);
}
var userXamlType = xamlType as global::MyProject.WinPhone.MyProject_WinPhone_XamlTypeInfo.XamlUserType;
if(xamlType == null || (userXamlType != null && userXamlType.IsReturnTypeStub && !userXamlType.IsLocalType))
{
global::Windows.UI.Xaml.Markup.IXamlType libXamlType = CheckOtherMetadataProvidersForType(type);
if (libXamlType != null)
{
if(libXamlType.IsConstructible || xamlType == null)
{
xamlType = libXamlType;
}
}
}
if (xamlType != null)
{
_xamlTypeCacheByName.Add(xamlType.FullName, xamlType);
_xamlTypeCacheByType.Add(xamlType.UnderlyingType, xamlType);
}
return xamlType;
}
It seems that my incoming MainView cannot be converted into an appropriate XAML type, as GetXamlTypeByType returns null.
I seems that the Xamarin.Forms view is not compatible with the WinPhone world...well, Xamarin.Forms should be about abstract views if I recall correctly.
What should I do now?
After I carefully read in the Xamarin Forms tutorial how a Windows Phone 8.1 application is added, and modified accordingly, everything worked as expected.
In my case, the MainPage.xaml.cs has its original code as I didn't change it according to the tutorial.
public sealed partial class MainPage // REMOVE ": PhonePage"
As Caliburn.Micro uses the Xamarin infrastructure, these steps are required.

How to get data from window to another with WPF and MVVM pattern

I build a small task app, in my first window I have a list of my task, when I click Create button it open new windows with textbox to write a title and content for the new task.
My need is to get the title and the content written in the second windows in my first windows to add this new task in my list.
My code look like this :
MainViewModel :
public class MainViewModel : Notifyer
{
ObservableCollection<Task> mTasks;
public ObservableCollection<Task> Tasks { get { return this.mTasks; } set { this.mTasks = value; Notify("Tasks"); } }
private ICommand m_ButtonAddCommand;
public ICommand ButtonAddCommand { get { return m_ButtonAddCommand; } set { m_ButtonAddCommand = value; } }
private ICommand m_ButtonDeleteCommand;
public ICommand ButtonDeleteCommand { get { return m_ButtonDeleteCommand; } set { m_ButtonDeleteCommand = value; } }
public MainViewModel()
{
ButtonAddCommand = new CommandHandler(() => add_task(), true);
ButtonDeleteCommand = new CommandHandler(() => delete_task(), true);
mTasks = new ObservableCollection<Task>();
mTasks.Add(new Task("title1", "content1", true));
mTasks.Add(new Task("title2", "content2", false));
}
private void add_task()
{
NewTaskWindow w = new NewTaskWindow();
w.Show();
//how to get my content ???
}
private void delete_task()
{
}
}
and NewTaskViewModel :
public class NewTaskViewModel : Notifyer
{
private ICommand m_ButtonAddCommand;
public ICommand ButtonAddCommand { get { return m_ButtonAddCommand; } set { m_ButtonAddCommand = value; } }
private String title;
public String Title { get { return this.title; } set { this.title = value; Notify("Title"); } }
private String content;
public String Content { get { return this.content; } set { this.content = value; Notify("Content"); } }
public NewTaskViewModel()
{
ButtonAddCommand = new CommandHandler(() => add_task(), true);
}
private void add_task()
{
Console.WriteLine(Title);
Console.WriteLine(Content);
}
}
You shouldn't create windows inside your viewodel, but with dependency injection. To solve your problem try this:
private void add_task()
{
NewTaskWindow w = new NewTaskWindow();
var taskViewModel = ( NewTaskViewModel )w.DataContext;
var title = taskViewModel.Title;
var content = taskViewModel.Content;
w.Show();
}
You can pass the model into the window without any injection. It is not necessary.
WindowModel windowModel = new WindowModel();
Window window = new Window(windowModel);
Inside Window Class
public Window(windowModel)
{
DataContext = windowModel;
}
Dont forget to remove the references for model injection for this window.

Categories