Xamarin Prism cannot navigate with parameters - xamarin

for my app i create my own buttons using a frame and adding a tapgesture to it. here i use the navigation of prism to go to a specific page with a parameter. however. the viewmodel i'm going to does not trigger the Navigated to method. here is some code.
during debugging it seems that the adding of the parameters is no problem. however the constructor for the viewmodel is called instead.
button
public class FolderButton : Frame
{
public FolderButton(Folder folder, INavigationService navigationService)
{
var navParams = new NavigationParameters();
navParams.Add("folder", folder);
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(async () => { await navigationService.NavigateAsync("FolderInventory", navParams); }),
});
BackgroundColor = Color.CornflowerBlue;
var thickness = new Thickness();
thickness.Bottom = 10;
thickness.Left = 10;
thickness.Right = 10;
thickness.Top = 10;
Margin = thickness;
CornerRadius = 5;
var completeStack = new StackLayout();
var imgStack = new StackLayout();
imgStack.Padding = thickness;
imgStack.Children.Add(new Image { Source = "folder.png" });
completeStack.Children.Add(imgStack);
var lblStack = new StackLayout();
lblStack.Padding = thickness;
lblStack.Children.Add(new Label
{
Text = folder.Name,
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Start
});
completeStack.Children.Add(lblStack);
Content = completeStack;
}
}
called viewmodel
public class FolderInventoryViewModel : BindableBase, INavigatedAware
{
public Folder Folder => _folder;
private readonly INavigationService _navigationService;
private Folder _folder;
private readonly ISQLiteService _sqlService;
private List<Frame> _buttons;
public List<Frame> Buttons
{
get => _buttons;
set => _buttons = value;
}
public FolderInventoryViewModel(Folder folder, INavigationService navigationService, ISQLiteService sqlService)
{
_folder = folder;
_sqlService = sqlService;
_navigationService = navigationService;
GetItemsForFolder();
}
private void GetItemsForFolder()
{
var itemList = _sqlService.GetAllFolderItems(Folder.Name);
foreach (var item in itemList)
{
var itemButton = new ItemButton(_navigationService, item);
_buttons.Add(itemButton);
}
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
if (parameters["folder"] is Folder folder)
{
_folder = folder;
}
}
public void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters["folder"] is Folder folder)
{
_folder = folder;
}
}
}

This is not the essence of using the framework. To properly use the Prism with its NavigationParameters you first properly maintain the MVVM idea behind it.
E.g.
<Button Command="{Binding testCommand}" text="TestButton"/>
Your ViewModel (Pardon about this, you need to inject NavigationService to your ViewModel's constructor)
private DelegateCommand _testCommand;
public DelegateCommand testCommand =>
_testCommand?? (_testCommand= new DelegateCommand(ExecuteTest));
private void ExecuteTest()
{
NavigationParameters navigationParameters = new NavigationParameters();
navigationParameters.Add("yourParameterId", value);
NavigationService.NavigateAsync("YourPage", navigationParameters);
}
And then onto your next page
Inherit INavigationAware to your NextPage : YourNextPage: BaseViewModel, INavigationAware
INavigationAware has 3 methods NavigatingTo, NavigatedTo, NavigatedFrom
Inside OnNavigatedTo you can call the parameters you have passed
public void OnNavigatedTo(NavigationParameters parameters)
{
//You can check if parameters has value before passing it inside
if(parameters.ContainsKey("YourParameterId")
{
yourItem = (dataType)parameters[YourParameterId];
}
}
Also note: The constructor will always be called first before the Navigating methods

Related

I would like to add children to a template using get => ContentStack.Children; but this does not work properly

I have a template frame that provides some padding and accepts multiple elements:
[Xamarin.Forms.ContentProperty("Contents")]
public class ContentFrame : StackLayout
{
public StackLayout ContentStack = new StackLayout();
public IList<View> Contents { get => ContentStack.Children; }
public ContentFrame()
{
CustomFrame cf = new CustomFrame()
{
Content = ContentStack,
HasShadow = false,
};
cf.SetDynamicResource(BackgroundColorProperty, "ContentFrameBackgroundColor");
cf.SetDynamicResource(Frame.CornerRadiusProperty, "ContentFrameCornerRadius");
cf.SetDynamicResource(MarginProperty, "ContentFrameMargin");
this.Children.Add(cf);
}
I would like to add child labels like this: c1.Children.Add - But when I do this the BackgroundColor, CornerRadius and Margin don't get used (see first part of image for ABC and ABC)
The only way I can get it to use these is by exposing ContentStack as a public property and by adding to that (see below for ABC and GHI)
public class TestPage : HeadingView
{
public TestPage() : base()
{
var s = new Stack();
var c1 = new ContentFrame();
c1.Children.Add(new Label() { Text = "ABC" });
c1.Children.Add(new Label() { Text = "DEF" });
var c2 = new ContentFrame();
c2.ContentStack.Children.Add(new Label() { Text = "DEF" });
c2.ContentStack.Children.Add(new Label() { Text = "GHI" });
s.Children.Add(c1);
s.Children.Add(c2);
this.InnerContent = s;
}
}
Question > Can anyone explain why the first case (with get => ContentStack.Children) doesn't show the frame background, radius etc.
make ContentStack private and add a method to expose the add functionality
private StackLayout ContentStack = new StackLayout();
public void Add(View view)
{
this.ContentStack.Children.Add(view);
}
if you want to add multiple you could also do
public void Add(List<View> views)
{
foreach(var v in views)
{
this.ContentStack.Children.Add(v);
}
}

Initialize an xamarin view after an async method

Good evening everyone.
For some time now I have been to Xamarin. My first tests are rather conclusive. I decided to try to make a small application that retrieves information in a database via an API and then update this data via a ListView.
When I launch the application on my emulator everything works but as soon as I install the application on my phone it crashes. I thought this was because the API but I have an API that I use to check the Login / password that works correctly.
The API that returns the data reviews a lot of line about 3500/4000, can this be the reason?
So I passed the loading of the data in my viewModel in an async method but the problem now is that the view loads before the data is loaded correctly. Is there a way to get the view initialized after the data is loaded?
Below my code.
Initializing my viewModel
class ManageInventViewModel
{
public ObservableCollection<InventViewModel> InventProduct { get; set; }
public Command<InventViewModel> UpdateCommand
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = invent.IndexLigneInventaire;
InventProduct.Remove(invent);
InventProduct.Insert(index, invent);
});
}
}
public Command<InventViewModel> ResetStock
{
get
{
return new Command<InventViewModel>(invent =>
{
var index = InventProduct.IndexOf(invent);
InventProduct.Remove(invent);
invent.RealStockProduct = 0;
InventProduct.Insert(index, invent);
});
}
}
public ManageInventViewModel()
{
LoadInventaire();
}
private async void LoadInventaire()
{
var listMvt = await Utils.Utils.GetListMouvementUntilDate();
var listStock = Utils.Utils.GetStockByProduct(listMvt).Take(20);
InventProduct = new ObservableCollection<InventViewModel>();
var indexLine = 0;
foreach (var stock in listStock)
{
var inventViewModel = new InventViewModel
{
LibelleProduit = stock.LibelleProduit,
PrCodeProduit = stock.PrCodeProduit,
UpCodeProduit = stock.UpCodeProduit,
RealStockProduct = stock.StockTheoProdct,
StockTheoProdct = stock.StockTheoProdct,
IndexLigneInventaire = indexLine
};
++indexLine;
InventProduct.Add(inventViewModel);
}
}
}
Initializinz my view
public partial class InventPage : ContentPage
{
public InventPage()
{
InitializeComponent();
TableInvent.ItemSelected += (sender, e) =>
{
if (TableInvent.SelectedItem != null)
{
if (TableInvent.SelectedItem is InventViewModel item)
{
PopupNavigation.Instance.PushAsync(new ChangeStockModal(item, this));
}
TableInvent.SelectedItem = null;
}
};
}
private void Reset_Stock(object sender, EventArgs e)
{
var input = sender as Button;
var inventViewModel = input?.BindingContext as InventViewModel;
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.ResetStock.Execute(inventViewModel);
}
public void Update_Stock_List(InventViewModel dataStockUpdate)
{
var listViewModel = BindingContext as ManageInventViewModel;
listViewModel?.UpdateCommand.Execute(dataStockUpdate);
PopupNavigation.Instance.PopAsync();
}
}
Thanks
I managed to create the ActivityIndicator but I can not get my data loaded while I'm displaying the wait screen.
Regarding this issue, I don't see you useActivityIndicator from your code,maybe you didn't update your code, I think if you use useActivityIndicator , You can bind one property to ActivityIndicator IsRunning and IsVisible, then you can solve your issue.
Related use ActivityIndicator step, you can take a look:
ActivityIndicator

FreshMvvm PushNewNavigationServiceModal not working

I'm trying to change from FreshNavigationContainer to FreshMasterDetailNavigationContainer when the user is loggedin within the method SuccessfulLogin by using freshMvvm method PushNewNavigationServiceModal but nothing is happening.
public void SuccessfulLogin()
{
App.IsLoggedIn = true;
var masterDetailNav = new FreshMasterDetailNavigationContainer();
masterDetailNav.Init("Menu");
masterDetailNav.AddPage<ProfilePageModel>("Profile", null);
CoreMethods.PushNewNavigationServiceModal(masterDetailNav);
}
Edit :
I just noticed that after using this method navigation isn't working anymore.
You need to use CoreMethods.SwitchOutRootNavigation
First Setup your NavigationStacks
public class NavigationStacks
{
public static string LoginNavigationStack = "LoginNavigationStack";
public static string MainAppStack = "MainAppStack";
}
In you App.xaml.cs define the Navigations
FreshNavigationContainer loginMain;
FreshMasterDetailNavigationContainer masterDetailNav;
var loginPage = FreshPageModelResolver.ResolvePageModel<UserLoginPageModel>();
loginMain = new FreshNavigationContainer(loginPage, NavigationStacks.LoginNavigationStack);
var masterDetailNav = new FreshMasterDetailNavigationContainer(NavigationStacks.MainAppStack);
masterDetailNav.Init("Menu");
masterDetailNav.AddPage<ProfilePageModel>("Profile", null);
Then in your Login ViewModel
public void SuccessfulLogin()
{
App.IsLoggedIn = true;
CoreMethods.SwitchOutRootNavigation(NavigationStacks.MainAppStack);
}

Multiple owin listeners with their own set of controllers, with Autofac for DI

I am trying to use multiple in-process owin listeners. Each should have a distinct set of controllers, where they may have the same route handled by a different controller. For instance
localhost:1234/api/app/test should resolve to ControllerA
localhost:5678/api/app/test should resolve to ControllerB
controller a, in owin host 1, has route attribute
[Route("api/app/test")]
controller b, in owin host 2, has route attribute
[Route("api/app/{*path}")]
and is used to forward requests to the other owin host.
We are using Autofac for dependency injection. Routes are configured through attribute routing.
autofac requires a line such as
builder.RegisterApiControllers(typeof(ControllerA).Assembly)
Our OWIN configuration contains:
var config = ConfigureWebApi();
// Configure Autofac
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
However when starting two listeners, I need to include both assemblies for controller resolving. This leads to a 'duplicate route' exception:
Multiple controller types were found that match the URL. This can
happen if attribute routes on multiple controllers match the requested
URL.\r\n\r\nThe request has found the following matching controller
types:
\r\nLib1.Controllers.ControllerA\r\nLib2.Controllers.ControllerB"
When running the OWIN listeners in separate processes, there are no issues.
I have also tried to use multiple DI containers, one for each OWIN listener, but that conflicts with Web Api 2 as it requires GlobalConfiguration.Configuration.DependencyResolver to be set. Which conflicts with the concept of multiple DI containers.
Can someone guide me how to configure such a setup?
Use the OWIN environment and customize the HttpControllerSelector
Using the OWIN pipeline you can pass information about the request to a custom HttpControllerSelector. This allows you to be selective about which controllers are used to match which routes.
Of course this is easier said than done. The inner workings of WebAPI with respect to routing are not very transparent - source code is often the best documentation in this area.
I could not get the HttpControllerSelector to fully work, so there's an ugly workaround in CustomHttpActionSelector. It may still be sufficient if all you need to do is forward requests from one host to the other.
The end result is:
GET to http://localhost:1234/api/app/test returns "HellofromAController" (directly invokes AController)
GET to http://localhost:5678/api/app/test returns "(FromBController): \"HellofromAController\"" (invokes BController, which forwards the request to AController)
See the full source on github
I left the logging code as-is in case it's useful, but it's not relevant to the solution.
So without further ado:
CustomHttpControllerSelector.cs:
Uses the port-specific OWIN env variable ApiControllersAssembly in to filter the controllers.
public sealed class CustomHttpControllerSelector : DefaultHttpControllerSelector
{
private static readonly ILog Logger;
static CustomHttpControllerSelector()
{
Logger = LogProvider.GetCurrentClassLogger();
}
public CustomHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
{
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var apiControllerAssembly = request.GetOwinEnvironment()["ApiControllersAssembly"].ToString();
Logger.Debug($"{nameof(CustomHttpControllerSelector)}: {{{nameof(apiControllerAssembly)}: {apiControllerAssembly}}}");
var routeData = request.GetRouteData();
var routeCollectionRoute = routeData.Route as IReadOnlyCollection<IHttpRoute>;
var newRoutes = new List<IHttpRoute>();
var newRouteCollectionRoute = new RouteCollectionRoute();
foreach (var route in routeCollectionRoute)
{
var filteredDataTokens = FilterDataTokens(route, apiControllerAssembly);
if (filteredDataTokens.Count == 2)
{
var newRoute = new HttpRoute(route.RouteTemplate, (HttpRouteValueDictionary)route.Defaults, (HttpRouteValueDictionary)route.Constraints, filteredDataTokens);
newRoutes.Add(newRoute);
}
}
var newRouteDataValues = new HttpRouteValueDictionary();
foreach (var routeDataKvp in routeData.Values)
{
var newRouteDataCollection = new List<IHttpRouteData>();
var routeDataCollection = routeDataKvp.Value as IEnumerable<IHttpRouteData>;
if (routeDataCollection != null)
{
foreach (var innerRouteData in routeDataCollection)
{
var filteredDataTokens = FilterDataTokens(innerRouteData.Route, apiControllerAssembly);
if (filteredDataTokens.Count == 2)
{
var newInnerRoute = new HttpRoute(innerRouteData.Route.RouteTemplate, (HttpRouteValueDictionary)innerRouteData.Route.Defaults, (HttpRouteValueDictionary)innerRouteData.Route.Constraints, filteredDataTokens);
var newInnerRouteData = new HttpRouteData(newInnerRoute, (HttpRouteValueDictionary)innerRouteData.Values);
newRouteDataCollection.Add(newInnerRouteData);
}
}
newRouteDataValues.Add(routeDataKvp.Key, newRouteDataCollection);
}
else
{
newRouteDataValues.Add(routeDataKvp.Key, routeDataKvp.Value);
}
HttpRouteData newRouteData;
if (newRoutes.Count > 1)
{
newRouteCollectionRoute.EnsureInitialized(() => newRoutes);
newRouteData = new HttpRouteData(newRouteCollectionRoute, newRouteDataValues);
}
else
{
newRouteData = new HttpRouteData(newRoutes[0], newRouteDataValues);
}
request.SetRouteData(newRouteData);
}
var controllerDescriptor = base.SelectController(request);
return controllerDescriptor;
}
private static HttpRouteValueDictionary FilterDataTokens(IHttpRoute route, string apiControllerAssembly)
{
var newDataTokens = new HttpRouteValueDictionary();
foreach (var dataToken in route.DataTokens)
{
var actionDescriptors = dataToken.Value as IEnumerable<HttpActionDescriptor>;
if (actionDescriptors != null)
{
var newActionDescriptors = new List<HttpActionDescriptor>();
foreach (var actionDescriptor in actionDescriptors)
{
if (actionDescriptor.ControllerDescriptor.ControllerType.Assembly.FullName == apiControllerAssembly)
{
newActionDescriptors.Add(actionDescriptor);
}
}
if (newActionDescriptors.Count > 0)
{
newDataTokens.Add(dataToken.Key, newActionDescriptors.ToArray());
}
}
else
{
newDataTokens.Add(dataToken.Key, dataToken.Value);
}
}
return newDataTokens;
}
}
CustomHttpActionSelector.cs:
You shouldn't need a CustomHttpActionSelector, this only exists to work around an issue with the ActionDescriptors for BController. It works as long as BController has only one method, otherwise you'll need to implement some route-specific logic.
public sealed class CustomHttpActionSelector : ApiControllerActionSelector
{
private static readonly ILog Logger;
static CustomHttpActionSelector()
{
Logger = LogProvider.GetCurrentClassLogger();
}
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
try
{
var actionDescriptor = base.SelectAction(controllerContext);
return actionDescriptor;
}
catch (Exception ex)
{
Logger.WarnException(ex.Message, ex);
IDictionary<string, object> dataTokens;
var route = controllerContext.Request.GetRouteData().Route;
var routeCollectionRoute = route as IReadOnlyCollection<IHttpRoute>;
if (routeCollectionRoute != null)
{
dataTokens = routeCollectionRoute
.Select(r => r.DataTokens)
.SelectMany(dt => dt)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
else
{
dataTokens = route.DataTokens;
}
var actionDescriptors = dataTokens
.Select(dt => dt.Value)
.Where(dt => dt is IEnumerable<HttpActionDescriptor>)
.Cast<IEnumerable<HttpActionDescriptor>>()
.SelectMany(r => r)
.ToList();
return actionDescriptors.FirstOrDefault();
}
}
}
Program.cs:
internal class Program
{
private static readonly ILog Logger;
static Program()
{
Log.Logger = new LoggerConfiguration()
.WriteTo
.LiterateConsole()
.MinimumLevel.Is(LogEventLevel.Verbose)
.CreateLogger();
Logger = LogProvider.GetCurrentClassLogger();
}
internal static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule(new LogRequestModule());
builder.RegisterApiControllers(typeof(AController).Assembly);
builder.RegisterApiControllers(typeof(BController).Assembly);
var container = builder.Build();
var config = GetHttpConfig();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
var options = new StartOptions();
options.Urls.Add("http://localhost:1234");
options.Urls.Add("http://localhost:5678");
var listener = WebApp.Start(options, app =>
{
app.Use((ctx, next) =>
{
if (ctx.Request.LocalPort.HasValue)
{
var port = ctx.Request.LocalPort.Value;
string apiControllersAssemblyName = null;
if (port == 1234)
{
apiControllersAssemblyName = typeof(AController).Assembly.FullName;
}
else if (port == 5678)
{
apiControllersAssemblyName = typeof(BController).Assembly.FullName;
}
ctx.Set("ApiControllersAssembly", apiControllersAssemblyName);
Logger.Info($"{nameof(WebApp)}: Port = {port}, ApiControllersAssembly = {apiControllersAssemblyName}");
}
return next();
});
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseWebApi(config);
});
Logger.Info(#"Press [Enter] to exit");
Console.ReadLine();
listener.Dispose(); ;
}
private static HttpConfiguration GetHttpConfig()
{
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Services.Add(typeof(IExceptionLogger), new LogProviderExceptionLogger());
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Services.Replace(typeof(IHttpControllerSelector), new CustomHttpControllerSelector(config));
config.Services.Replace(typeof(IHttpActionSelector), new CustomHttpActionSelector());
var traceSource = new TraceSource("LibLog") { Switch = { Level = SourceLevels.All } };
traceSource.Listeners.Add(new LibLogTraceListener());
var diag = config.EnableSystemDiagnosticsTracing();
diag.IsVerbose = false;
diag.TraceSource = traceSource;
return config;
}
}
LibA\Controllers\AController.cs:
[RoutePrefix("api/app")]
public class AController : ApiController
{
private static readonly ILog Logger;
static AController()
{
Logger = LogProvider.GetCurrentClassLogger();
Logger.Debug($"{nameof(AController)}: Static Constructor");
}
public AController()
{
Logger.Debug($"{nameof(AController)}: Constructor");
}
[HttpGet, Route("test")]
public async Task<IHttpActionResult> Get()
{
Logger.Debug($"{nameof(AController)}: Get()");
return Ok($"Hello from {nameof(AController)}");
}
}
LibB\Controllers\BController.cs:
[RoutePrefix("api/app")]
public class BController : ApiController
{
private static readonly ILog Logger;
static BController()
{
Logger = LogProvider.GetCurrentClassLogger();
Logger.Debug($"{nameof(BController)}: Static Constructor");
}
public BController()
{
Logger.Debug($"{nameof(BController)}: Constructor");
}
[HttpGet, Route("{*path}")]
public async Task<IHttpActionResult> Get([FromUri] string path)
{
if (path == null)
{
path = Request.RequestUri.PathAndQuery.Split(new[] {"api/app/"}, StringSplitOptions.RemoveEmptyEntries)[1];
}
Logger.Debug($"{nameof(BController)}: Get({path})");
using (var client = new HttpClient {BaseAddress = new Uri("http://localhost:1234/api/app/")})
{
var result = await client.GetAsync(path);
var content = await result.Content.ReadAsStringAsync();
return Ok($"(From {nameof(BController)}): {content}");
}
}
}
I might have another go at it when I have more time.
Let me know if you make any progress!

Bind object data to Int xamarin forms

I have a MasterDetailPage that creates several Department objects. I want to grab the current department number so I can use it to sort a list later on in my program. How do I go about doing that? I have tried binding it to a label and then getting the data from that (very hacky, I know) but that's the only thing I could think of.
Department[] departments = {
new Department ("D", 1),
new Department ("De", 7),
new Department ("G", 4),
new Department ("M", 9),
new Department ("Pr", 167),
new Department ("Fr", 187),
new Department ("H", 169),
new Department ("B", 11),
new Department ("S", 399),
new Department ("N", 407),
new Department ("O", 201),
new Department ("U", 023)
};
ListView listView = new ListView {
ItemsSource = departments
};
this.Master = new ContentPage {
Title = "Departments", // Title required!
Content = new StackLayout {
Children = {
header,
listView
}
}
};
DetailPage2 detailPage = new DetailPage2 ();
this.Detail = detailPage; //detail page is where I want to use deptNum for sorting
listView.ItemSelected += (sender, args) => {
// Set the BindingContext of the detail page.
this.Detail.BindingContext = args.SelectedItem;
// Show the detail page.
this.IsPresented = false;
};
// Initialize the ListView selection.
listView.SelectedItem = departments [0];
}
}
}
Then in my detailpage I want to be able to pull the departmentNumber out and use it as an int
using System;
using Xamarin.Forms;
namespace irisxamarin
{
public class Department :BindableObject
{
public Department (string name, int deptNumber)
{
this.Name = name;
this.DeptNum = deptNumber;
}
public string Name { private set; get; }
public int DeptNum { private set; get; }
public override string ToString ()
{
return Name;
}
}
}
And here is some logic in the detailpage. This is where I would like to grab the current deptNum.
namespace irisxamarin
{
public class DetailPage2 : ContentPage
{
public DetailPage2 ()
{
Request request = new Request ();
Button settingsButton = new Button {
Text = "Settings",
TextColor = Color.Gray
};
//......................
//code above and below
ListView itemsList = new ListView {
ItemsSource = request.GetList (deptNum) //USE INT HERE
};
itemsList.ItemSelected += (sender, args) => {
this.BindingContext = args.SelectedItem;
};
itemLabel.SetBinding (Label.TextProperty, "DeptNum");
//DeptNum is the data I want but not in a label, just the int val
var listFrame = new Frame {
Content = itemsList,
OutlineColor = Color.Silver,
};
Each page is just a C# class. You can pass a value to it the way you would do with any class - generally the easiest way is to
pass values in the constructor
or if the page already exists, create public properties and set the value via the setter
If you want to set a value globally for use throughout your app, you can create a static class that is available everywhere and set state values in that class.

Resources