MasterDetail with Prism, how to? - xamarin

I am trying to test and example with MasterDetail. You can see the code in:
https://github.com/jrariasf/MD8/tree/master/MD8
The Master have 5 buttons to access to 4 detail pages (Home, MainPage, ViewA, ViewB and ViewC).
From ViewA, with 2 buttons I am able to load ViewB and ViewC
But I am not able to do that pushing a button in hambubrger menu then load the detail page adequated.
It only works if I put an absolute path in the CommandParameter in "PrismMasterDetailPage.xaml":
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="MD8.Views.PrismMasterDetailPage">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<StackLayout Padding="20">
<!-- TODO: // Update the Layout and add some real menu items -->
<Button Text="Home" Command="{Binding NavigateCommand}" CommandParameter="/PrismMasterDetailPage/NavigationPage/ViewA" />
<Button Text="MainPage" Command="{Binding NavigateCommand}" CommandParameter="/NavigationPage/MainPage" />
<Button Text="ViewA" Command="{Binding NavigateCommand}" CommandParameter="../ViewA" />
<Button Text="ViewB" Command="{Binding NavigateCommand}" CommandParameter="./ViewB" />
<Button Text="ViewC" Command="{Binding NavigateCommand}" CommandParameter="ViewC" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
Then, in "PrismMasterDetailPageViewModel.cs"
void ExecuteCommandName(string page)
{
Console.WriteLine("PrismMasterDetailPageViewModel - ExecuteCommandName() Vamos a {0}", page);
_navigationService.NavigateAsync(page);
}
If I am in "/PrismMasterDetailPage/NavigationPage/ViewA", what I have to do to unload ViewA and load ViewB ?
For example, in App.xaml.cs the code is:
await NavigationService.NavigateAsync("PrismMasterDetailPage/NavigationPage/ViewA");
Then, execute the app on android emulator, push buttons of hamburger menu and the result it is not as I expected.
Pressing Home button, the _navigationService.GetNavigationUriPath() returns: /PrismMasterDetailPage/NavigationPage/ViewA/NavigationPage?useModalNavigation=true/ViewA
why?
If I press button ViewA or ViewB or ViewC, it don't show anything. But the OnNavigatedFrom() method is called on each View*ViewModel.cs
What is wrong?
Thanks!!

One fo the first things you need to do is understand where you are Navigating from. Navigation in Xamarin.Forms is very dependent on where you are Navigating from. Remember that without Prism you would do something like this:
var mdp = new MyMasterDetailPage
{
Detail = new NavigationPage(new ViewA)
};
In order to achieve a Hamburger Menu with Prism you typically would want a MasterDetailPage as the Application's Main Page. The next segment in the Navigation Uri must be a NavigationPage and the following page would generally be a ContentPage.
<Button Text="Home"
Command="{Binding NavigateCommand}"
CommandParameter="/PrismMasterDetailPage/NavigationPage/ViewA" />
Ok so looking at this first one, this is generally what you would use when navigating from the PrismApplication which is why it works.
<Button Text="MainPage"
Command="{Binding NavigateCommand}"
CommandParameter="/NavigationPage/MainPage" />
Looking at this one, this is really close to what you do want except you are doing an absolute navigation which means that you are resetting the Application.MainPage. What you actually need is a relative Uri because you are navigating from the MasterDetailPage.
<Button Text="ViewA"
Command="{Binding NavigateCommand}"
CommandParameter="../ViewA" />
This is completely wrong because ../{path} is only supported from within a NavigationPage and you are in a MasterDetailPage...
<Button Text="ViewB"
Command="{Binding NavigateCommand}"
CommandParameter="./ViewB" />
This is just not supported by Prism at all.
<Button Text="ViewC"
Command="{Binding NavigateCommand}"
CommandParameter="ViewC" />
This is setting the MasterDetailPage.Detail like:
mdp.Detail = new ViewC()
Of course as I mentioned above you need it to be
mdp.Detail = new NavigationPage(new ViewC());

Thanks a lot Dan Siegel ! (#Dan S.)
I have just seen your comment, a month ago, sorry.
I was a newbie in C# and Xamarin and Prism and I am still a newbie but less :-)
I have learnt and tested a lot in the last weeks and I understand better the concepts.
After reading you I know that ../{path} is only valid with a NavigationPage .
Futhermore, one problem I had was thar I was checking the value of "_navigationService.GetNavigationUriPath()" inside OnNavigatedTo method and as I read later, at that moment the UriPath is not the real final UriPath.
In my code I wrote:
public void OnNavigatedTo(INavigationParameters parameters)
{
Console.WriteLine("DEBUG - ViewAVM: We are in {0}",
_navigationService.GetNavigationUriPath());
}
I moved the GetNavigationUriPath() call to another method and the results were as I expected.
Thanks!!

Related

How to fix this exception:Cannot convert a NSColorType.Catalog color without specifying the color space, use the overload to specify an NSColorSpace

I am trying to build a Xamarin.Forms 5 MacOS project - the XAML is the basic as provided by the default boiler plate code.
When I compile the project I get an exception:
The exception:
Cannot convert a NSColorType.Catalog color without specifying the color space, use the overload to specify an NSColorSpace
As I said, I have not added any custom code yet. How do I fix this?
Update
If I downgrade to Xamarin Form 4.8 it builds and runs with no exception:
So why is 5.x failing?
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VisitsRota.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Label Text="Start developing now" FontSize="Title" Padding="30,10,30,10"/>
<Label Text="Make changes to your XAML file and save to see your UI update in the running app with XAML Hot Reload. Give it a try!" FontSize="16" Padding="30,0,30,0"/>
<Label FontSize="16" Padding="30,24,30,0">
<Label.FormattedText>
<FormattedString>
<FormattedString.Spans>
<Span Text="Learn more at "/>
<Span Text="https://aka.ms/xamarin-quickstart" FontAttributes="Bold"/>
</FormattedString.Spans>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ContentPage>
AppDelegate
using AppKit;
using Foundation;
using VisitsRota;
using Xamarin.Forms;
using Xamarin.Forms.Platform.MacOS;
namespace VisitsRota.MacOS
{
[Register("AppDelegate")]
public class AppDelegate : FormsApplicationDelegate
{
NSWindow window;
public AppDelegate()
{
var style = NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Titled;
var rect = new CoreGraphics.CGRect(200, 1000, 1024, 768);
window = new NSWindow(rect, style, NSBackingStore.Buffered, false);
window.Title = "Visits Rota for Mac";
window.TitleVisibility = NSWindowTitleVisibility.Hidden;
}
public override NSWindow MainWindow
{
get { return window; }
}
public override void DidFinishLaunching(NSNotification notification)
{
Forms.Init();
LoadApplication(new App());
base.DidFinishLaunching(notification);
}
}
}
The exception is on base.DidFinishLaunching(notification); I think.
Updating to latest Xamarin.Forms resolved this issue.

Add Shell Navigation templates for various pages Xamarin.Forms Shell

I have multi-page app, which has preLogin section and postLogin section.
Which is the best approach to make shell templates for these two sections?
preLogin pages doesnt have backbutton in navigationBar. I tried in xaml page NavigationPage.HasNavigationBar="false" (not AppShell.xaml, either in my page xaml) as well as in code behind NavigationPage.SetHasNavigationBar(this, false);
example xaml page which regardless on my NavigationPage.HasBackButton="false"
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:customViewEntry="clr-namespace:App.CustomViews.Entries"
xmlns:header="clr-namespace:App.CustomViews.CustomHeaders"
mc:Ignorable="d"
x:Class="App.Views.Login.EnterEmailAndPasswordPage"
NavigationPage.HasBackButton="false"
BackgroundImageSource="Login_Screen_Green.png">
<ContentPage.Content>
<StackLayout>
<header:HeaderWithTopRightIcon ImageUri="greenIcon.png"/>
<StackLayout VerticalOptions="EndAndExpand" Padding="0,50">
<customViewEntry:EvEntry
Placeholder="Enter your email"/>
<customViewEntry:EvEntry
Placeholder="Enter your password"
IsPassword="True"/>
<Button
Text="LOGIN"
Margin="25,0"
CornerRadius="7"
BackgroundColor="{StaticResource DarkGrayColor}"
TextColor="White"
Command="{Binding OpenEmailVerificationPageCommand}"/>
<Label
Padding="0,25,0,0"
Text="Don't have an account?"
FontSize="Small"
Style="{StaticResource WhiteSmallLabelStyle}"
HorizontalOptions="Center"
VerticalOptions="Start"/>
<Label
Text="SIGN UP"
TextDecorations="Underline"
FontSize="Small"
Style="{StaticResource WhiteSmallBoldLabelStyle}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding SignUpClickCommand}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
my AppShell looks like:
<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:views="clr-namespace:App.Views"
xmlns:loginViews="clr-namespace:App.Views.Login"
x:Class="App.AppShell"
FlyoutBehavior="Disabled">
<!--Styles and Resources-->
<Shell.Resources>
<ResourceDictionary>
...removed due simplicity...
</ResourceDictionary>
</Shell.Resources>
<ShellContent ContentTemplate="{DataTemplate loginViews:EnterEmailPage}"/>
</Shell>
So, my questions are
1. How to dynamically hide back button on some pages and on some not? For navigation Im using await Shell.Current.GoToAsync(route)
2. Also I need a redirection when user leaves application, and again enter it based on elapsed time, to redirect to preLogin page (login) or to let him directly to postLogin (full access to the app) so I need maybe two AppShell classes, and to call it different shells OnResume() ?
How to dynamically hide back button on some pages and on some not? For navigation Im using await Shell.Current.GoToAsync(route)
About this question , here is a Workaround for you .
Shell application has Back button behavior to ovveride Back Button dynamically , even can Hide/Show it .
If using Shell.Current.GoToAsync(route) to navigate to destination page , you can follow the below code to use in your destination page.
// Button click to show Back Button
private void Button_Clicked_Show(object sender, EventArgs e)
{
Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
IsEnabled = true
});
}
// Button click to hide Back Button
private void Button_Clicked_Hide(object sender, EventArgs e)
{
Shell.SetBackButtonBehavior(this, new BackButtonBehavior
{
IconOverride = "null.png", // null.png not exists in project ,just want
the program to show a blank Back button
IsEnabled = false
});
}
I will show the effect with a Gif :
Also I need a redirection when user leaves application, and again enter it based on elapsed time, to redirect to preLogin page (login) or to let him directly to postLogin (full access to the app) so I need maybe two AppShell classes, and to call it different shells OnResume() ?
About this question , here is a Suggestion for you , you can conside that whether fit your needs .
You can use Navigation.PushModalAsync(new LoginPage()) to redirect to preLogin page based on elapsed time . Otherwise using Shell.Current.GoToAsync(route) to Navigate . You can deal with them in OnResume Method .
For example as follow :
protected override void OnResume()
{
if(time > xxx)
{
Navigation.PushModalAsync(new LoginPage());
}
else
{
Shell.Current.GoToAsync(route);
}
}
Here using PushModalAsyncto Login Page that is a Model Page . After using Navigation.PopModalAsync() can dismiss LoginPage and redirect to other Page with Shell.Current.GoToAsync(route).

How can I create a template / class that will allow me to simplify some my Xaml where there are multiple elements into just one?

Here's an example of what I need to do now. Sometimes I have one span, sometimes more.
Note that this post is similar to that of another question. For the other question I had only one comment to use a custom control with no more advice offered and one answer to use JavaScript. I tried to add a second bounty to that question but it gave me the option of only adding a bounty of 500 points. The question is now so old that I doubt anyone will see it any more and as I cannot add a bounty (unless it's 500 points) I cannot give it more visibility.
Here's what I would like to simplify:
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="Hello " />
<Span Text="Hello " />
<Span Text=" Some more text." />
</FormattedString>
</Label.FormattedText>
</Label>
Here's what I would like to do instead of typing in <Label><Label.FormattedText><FormattedString> I would like to get some way to do this with only entering in <template:FormattedLabel>
<template:FormattedLabel>
<Span Text="Hello " />
<Span Text="Hello " />
<Span Text=" Some more text." />
</template:FormattedLabel>
or
<template:FormattedLabel>
<Span Text="Hello " />
</template:FormattedLabel>
Note that I have looked into custom controls but as far as I see I cannot find a way to make these accept some inside content which in this case would be one or more spans.
I have an example of something similar which could perhaps be used but I am not sure how to apply it. What I was hoping for was a template like this in the XAML below which is does something similar to what I need but for content pages:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=J"
xmlns:t="clr-namespace:J.Templates"
x:Class="Japanese.Templates.ContentScrollable"
x:Name="ContentPage" >
<ContentPage.Content>
<t:Stack Orientation="Vertical">
<ScrollView x:Name="scroll">
<ContentView Content="{Binding Source={x:Reference ContentPage}, Path=InnerContent}"
Margin="{DynamicResource PageMargin}" />
</ScrollView>
</t:Stack>
</ContentPage.Content>
</ContentPage>
With its C# back end:
public partial class ContentScrollable : ContentPage
{
public static readonly BindableProperty InnerContentProperty = BindableProperty.Create(nameof(InnerContent), typeof(View), typeof(ContentScrollable));
public View InnerContent
{
get => (View)this.GetValue(InnerContentProperty);
set => this.SetValue(InnerContentProperty, value);
}
public ContentScrollable()
{
InitializeComponent();
}
}
How can I accomplish what I am looking for?
You can do the following:
<!-- FormattedLabel.xaml -->
<?xml version="1.0" encoding="UTF-8"?>
<Label
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourNamespaceForTemplates.FormattedLabel" />
// FormattedLabel.xaml.cs
[ContentProperty(nameof(Spans))]
public partial class FormattedLabel : Label
{
private readonly ObservableCollection<Span> _spans;
public IList<Span> Spans => _spans;
public FormattedLabel()
{
_spans = new ObservableCollection<Span>();
_spans.CollectionChanged += OnSpansChanged;
InitializeComponent();
}
private void OnSpansChanged(object sender, NotifyCollectionChangedEventArgs e)
{
FormattedText?.Spans?.Clear();
FormattedText = FormattedText ?? new FormattedString();
Spans.ForEach(FormattedText.Spans.Add);
}
}
Basically, in this extension of Label we define the content to be a list of Span items, which will allow you to define them in XAML inside <FormattedLabel></FormattedLabel>. To make it work, we pass these items down to this.FormattedText.Spans.
To use it:
<template:FormattedLabel>
<Span Text="Hello " />
<Span Text="Hello " />
<Span Text=" Some more text." />
</template:FormattedLabel>
I have just checked it and it works perfectly. I hope this helps!

How to get context from NativeScript modal opener through frame?

I'm unable get showModal to pass context through a frame to the default page. Also, showingModally does not seem to fire when modal is loaded (I guess args from showingModally should pass context?).
I've tried the example provided on https://docs.nativescript.org/ui/modal-view under Custom Actionbar, because I need the modal to load within a frame. The modal opens and closes fine, but showingModally in the modal XML does not seem to run.
home/home-page.js
const modalView = "home/modal-root";
function openModal(args) {
console.log('Opens modal');
const mainpage = args.object.page;
const context = "some context";
mainpage.showModal(modalView, context, () => {
console.log('Modal closed');
}, true);
}
exports.openModal = openModal;
home/home-page.xml
<Page xmlns="http://www.nativescript.org/tns.xsd">
<Page.actionBar>
<ActionBar title="Modal view Navigation" />
</Page.actionBar>
<GridLayout rows="auto, *">
<Button text="Open modal" tap="openModal" textWrap="true" />
</GridLayout>
</Page>
home/modal-root.xml
<Frame defaultPage="home/modal-view-page" />
home/modal-view-page.js
function onShowingModally(args) {
console.log("onShowingModally");
}
exports.onShowingModally = onShowingModally;
function onCloseModal(args) {
args.object.closeModal();
}
exports.onCloseModal = onCloseModal;
home/modal-view-page.xml
<Page backgroundColor="green" showingModally="onShowingModally">
<Page.actionBar>
<ActionBar backgroundColor="red" title="Modal view" icon="">
</ActionBar>
</Page.actionBar>
<StackLayout backgroundColor="lightGreen">
<Label text="Modal view with ActionBar" style="text-align:center;" textWrap="true" />
<Button text="Close Modal" tap="onCloseModal" />
</StackLayout>
</Page>
I've added the example to https://play.nativescript.org/?template=play-js&id=lFxTi4&v=9
with console logging.
Console show "Opens modal" on open and "Modal closed" on close as expected (from home/home-page.js), but "onShowingModally" (home/modal-view-page.js) nevers shows in console.
I think the docs need to be fixed. When you have the Frame being shown modally, the event will be called on the Frame not on the page within. If you are showing a simple view instead of Frame, then the event should be called on the view, basically whatever is the root view for your modal and in your example it's the Frame.
Updated Playground

How to use checkbox in datagrid in silverlight

i am making an application in silver light.In that application i am using data grid as
<data:DataGrid Grid.Row="1" HorizontalAlignment="Left" IsReadOnly="True" Name="dataGrid1" VerticalAlignment="Top" AutoGenerateColumns="False" DataContext="{Binding}" SelectionMode="Single" LoadingRow="ResultsGrid_LoadingRow">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header=" BedId " Binding="{Binding BedID }" />
<data:DataGridTextColumn Header="PatientName" Binding="{Binding PatientName}" />
<data:DataGridTextColumn Header="AdmitDate" Binding="{Binding AdmitDate}" />
<data:DataGridTextColumn Header="BirthDate" Binding="{Binding BirthDate}" />
<data:DataGridCheckBoxColumn Header="checkbox" Binding="{ Binding }" IsReadOnly="False" />
</data:DataGrid.Columns>
</data:DataGrid>
Whenever in my application i am loading the data grid, it shows the data grid along with respective values.In my application i want to show the check box in front of every column and above code shows the check box in front of every column. but whenever i am clicking on that check box it doesn't shows any check event.i want to select single or multiple check boxes as per condition.But i am not getting how to do it.Please help me.Thanks in advance.
You have a few options.
1. Setup a selection changed event on the datagrid and whenever a row is selected modify the bound checkbox in that aspect.
If you need more control such as an event to fire when the checkbox is selected I would repalce the DataGridColum with an DataTemplate such as:
<DataGridTemplateColumn Header="Checkbox">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=someProperty, UpdateSourceTrigger=PropertyChanged}" Click="CheckBox_Click"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Then in your code behind
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
//I realize you don't have message box in silverlight but this demonstrates the firing of the event
MessageBox.Show("click");
}

Resources