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.
Related
I have an interface as below, which I use to add a specific language if it does not exist:
public interface IGetLanguagesService
{
void GetLanguages(ILocalizationService localization);
}
public class LanguageService : IGetLanguagesService
{
ILocalizationService _localizationService;
public void GetLanguages(ILocalizationService localization)
{
_localizationService = localization;
var currentLanguages = _localizationService.GetAllLanguages();
bool exists = false;
foreach (var currentLan in currentLanguages)
{
if (currentLan.IsoCode == "es-ES")
{
exists = true;
}
}
if (!exists)
{
AddLanguage(_localizationService);
}
}
public void AddLanguage(ILocalizationService localization)
{
var languageSE = new Language("es-ES") { CultureName = "es-ES", IsMandatory = true };
localization.Save(languageSE);
}
}
I want to use this at start-up so have created a component composer, which on Initialize() I want to call CallGetLanguages() but Im not entirely sure what should be in Initialize(), I think my DI may be wrong?
public class LanguagesComposer : ComponentComposer<LanguagesComponent>
{
public void Compose(Composition composition)
{
composition.Register<IGetLanguagesService>(Lifetime.Singleton);
composition.Register<ILocalizationService>(Lifetime.Singleton);
composition.Components().Append<LanguagesComponent>();
}
}
public class LanguagesComponent : IComponent
{
public void Initialize()
{
???????
}
public void Terminate()
{
throw new NotImplementedException();
}
IGetLanguagesService _getLanguagesService;
ILocalizationService _localization;
public void CallGetLanguages(IGetLanguagesService getLanguages, ILocalizationService localization)
{
_getLanguagesService = getLanguages;
_localization = localization;
_getLanguagesService.GetLanguages(localization);
}
}
You've passed ILocalizationService localization instance to LanguageService twice, pass it to constructor instead and use a constructor injection. The same issue with LanguagesComponent, pass all its dependencies to constructor instead of methods
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
I'm deriving from UIApplication and overriding SendEvent to capture user taps.
Everything works fine, except when closing modals of type IMvxModalIosView.
Here's the method:
public override void SendEvent(UIEvent uievent)
{
NSSet touches = uievent.AllTouches;
if (touches != null)
{
UITouch touch = (UITouch)touches.AnyObject;
switch (touch.Phase)
{
case UITouchPhase.Ended:
StartDoingStuffAgain();
break;
default:
StopDoingStuff();
break;
}
}
base.SendEvent(uievent);
}
Debugging this while closing an IMvxModalIosView, if I set a breakpoint at the start of the method, as well as StartDoingStuffAgain(), the latter gets hit. If however I only set a breakpoint at StartDoingStuffAgain(), it never gets hit. StopDoingStuff() gets hit regardless.
Why is this?
Edit 1: As per nmilcoff's request for more code:
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
//...
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
//...
_app = application as MyUIApplication;
//...
}
//...
}
[Register("MyUIApplication")]
public class MyUIApplication : UIApplication
{
//...
// Bit of code in original question goes here.
//...
}
//The iPad view presenter (iPhone has MyIPhoneViewPresenter)
public class MyIPadViewPresenter : MvxBaseIosViewPresenter
{
//...
private UINavigationController _navController;
private UINavigationController _modalNavController;
private IMvxMessenger _messenger;
protected readonly SemaphoreSlim _autoLock = new SemaphoreSlim(1, 1);
//...
public override async void Show(MvxViewModelRequest request)
{
await _autoLock.WaitAsync();
//...
if(_messenger == null)
{
_messenger = Mvx.Resolve<IMvxMessenger>();
}
// Among other things, the following method checks if the page
// corresponding to the VM request is a Xamarin.Forms or a native view.
var presentation = MvxIOSPresentationHelpers.CreateView(request, TargetIdiom.Tablet);
switch(presentation.Type)
{
//...
case ViewType.ModalWithNav:
showNavModalView(presentation.ViewController);
break;
//...
}
messenger.Publish(new ViewModelShowMessage(request.ViewModelType, presentation.ViewController, this));
//..
_autoLock.Release();
}
//...
private void showNavModalView(UIViewController viewController)
{
if(_modalNavController == null)
{
_modalNavController = new ModalNavController(viewController);
_modalNavController.ModalPresentationStyle = viewController.ModalPresentationStyle;
_navController.PresentViewController(_modalNavController, false, delegate { });
}
else
{
_modalNavController.PushViewController(viewController, true);
}
//...
}
//...
public override async void ChangePresentation(MvxPresentationHint hint)
{
var close_hint = hint as MvxClosePresentationHint;
if (close_hint != null)
{
await _autoLock.WaitAsync();
close(close_hint.ViewModelToClose);
_autoLock.Release();
}
else
{
//...
}
}
//...
protected void close(IMvxViewModel toClose)
{
//...
_messenger.Publish(new MvxMessage(this));
//...
if (_modalNavController != null)
{
if(_modalNavController.ChildViewControllers.Length <= 1)
{
// This is where the close sequence comes down to.
_modalNavController.DismissViewController(true, delegate{ });
_modalNavController = null;
}
else
{
//...
}
return;
}
//..
}
//...
}
// The VM corresponding to the IMvxModalIosView
public class MyProblematicModalVM : MvxViewModel
{
//...
public ICommand CloseCommand
{
get { return new MvxCommand(() => Close(this)); }
}
//...
}
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
We are using HttpSessionStateBase to store messages in a set up similar to this working example:
public class HttpSessionMessageDisplayFetch : IMessageDisplayFetch
{
protected HttpSessionStateBase _session;
private IList<ICoreMessage> messages
{
get
{
if (_session[EchoCoreConstants.MESSAGE_KEY] == null)
_session[EchoCoreConstants.MESSAGE_KEY] = new List<ICoreMessage>();
return _session[EchoCoreConstants.MESSAGE_KEY] as IList<ICoreMessage>;
}
}
public HttpSessionMessageDisplayFetch()
{
if (HttpContext.Current != null)
_session = new HttpSessionStateWrapper(HttpContext.Current.Session);
}
public void AddMessage(ICoreMessage message)
{
if (message != null)
messages.Add(message);
}
public IEnumerable<IResultPresentation> FlushMessagesAsPresentations(IResultFormatter formatter)
{
var mToReturn = messages.Select(m => m.GetPresentation(formatter)).ToList();
messages.Clear();
return mToReturn;
}
}
When we pass in a QualityExplicitlySetMessage (which inherits from ICoreMessage, see below) it is saved correctly to messages.
This is how the object looks after being inserted into the messages list, at the end of AddMessage(ICoreMessage message) above.
But when we come to access it after changing controllers the inherited member's properties are null, which causes a variety of null reference exceptions.
This is how the object now looks after we call FlushMessagesAsPresentations. I've commented out var mToReturn... as this tries to access one of these null ref properties.
I'd like to ask the following:
Why is the HttpSessionStateBase failing to capture these values taken
by the inherited type?
Is this an issue in saving to the HttpSession or in retrieving?
Is this anything to do with, as I suspect, inheritance?
Or is the fact I'm potentially calling a new controller that dependency injects the HttpSessionMessageDisplayFetch causing an issue?
I'm a first-time poster so please let me know if I'm making any kind of faux pas - Super keen to learn! Any input is very welcome.
Some potentially useful code snippets:
QualityExplicitlySetMessage
public class QualityExplicitlySetMessage : QualityChangeMessage
{
public QualityExplicitlySetMessage(IQPossession before, IQPossession after, IQEffect qEffect)
: base(before, after, qEffect)
{
IsSetToExactly = true;
}
}
QualityChangeMessage - Working example
public abstract class QualityChangeMessage : CoreMessage, IQualityChangeMessage
{
protected PossessionChange Change;
public PossessionChange GetPossessionChange()
{
return Change;
}
protected QualityChangeMessage(IQPossession before, IQPossession after, IQEffect qEffect)
{
Change = new PossessionChange(before, after, qEffect);
StoreQualityInfo(qEffect.AssociatedQuality);
}
public override IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetQualityResult(this);
}
#region IQualityChangeMessage implementation
public int LevelBefore
{
get { return Change.Before.Level; }
}
//... And so on with values dependent on the Change property.
}
CoreMessage - Working example
public abstract class CoreMessage : ICoreMessage
{
public string MessageType
{
get { return GetType().ToString(); }
}
public string ImageTooltip
{
get { return _imagetooltip; }
set { _imagetooltip = value; }
}
public string Image
{
get { return _image; }
set { _image = value; }
}
public int? RelevantQualityId { get; set; }
protected void StoreQualityInfo(Quality q)
{
PyramidNumberIncreaseLimit = q.PyramidNumberIncreaseLimit;
RelevantQualityId = q.Id;
RelevantQualityName = q.Name;
ImageTooltip = "<strong>" + q.Name + "</strong><br/>" + q.Description + "<br>" +
q.EnhancementsDescription;
Image = q.Image;
}
public virtual IResultPresentation GetPresentation(IResultFormatter formatter)
{
return formatter.GetResult(this);
}
}
UserController - Working example.
public partial class UserController : Controller
{
private readonly IMessageDisplayFetch _messageDisplayFetch;
public UserController(IMessageDisplayFetch messageDisplayFetch)
{
_messageDisplayFetch = messageDisplayFetch;
}
public virtual ActionResult MessagesForStoryletWindow()
{
var activeChar = _us.CurrentCharacter();
IEnumerable<IResultPresentation> messages;
messages = _messageDisplayFetch.FlushMessagesAsPresentations(_storyFormatter);
var vd = new MessagesViewData(messages)
{
Character = new CharacterViewData(activeChar),
};
return View(Views.Messages, vd);
}
}