Xamarin UWP + Android ContentPage Navigation PushAsync - c#

I'm converting an UWP application to Xamarin in the aim to use it on both Android and Windows devices.
I never used Xamarin before and I assume i'm doing a beginer mistake.
On my MainPage everything is OK :
But when I'm clicking on the "Options" Button it didn't load a new page exept a grey banner in the upper part of the window :
To navigate from one page to another I followed this explanation : Microsoft doc navigation
There's my code to change page in the MainPage.xaml.cs :
async void ButtonOptions_Click(object sender, EventArgs args)
{
try
{
button_options.Source = "Assets/Option_Icon_1.png";
Application.Current.MainPage = new NavigationPage(new MainPage());
var OptionsView = new OptionsView();
await Device.InvokeOnMainThreadAsync(() => Navigation.PushAsync(OptionsView, true));
//this.Frame.Navigate(typeof(StorageView1));
}
catch (Exception ex) { InterpretException("MainPage.ButtonStorage_Click()", ex); }
}
And my OptionsView.xaml :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Stock_Manager_Xamarin.OptionsView"
Title="Second Page">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<StackLayout Orientation="Horizontal">
<Label Text="Name:" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
</StackLayout>
<Button x:Name="navigateButton" Text="Previous Page"/>
</StackLayout>
</ContentPage.Content>
I tried different version of the xaml code without any change and I can't find a working sample.
Could someone can explain me where I'm doing a mistake ?

Wrap current page inside navigation stack in App.cs, and set it as MainPage, so that we can do the navigation operation .
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
As Jason mentioned , it is no need to set MainPage again , just navigate directly , modify your code as below .
void ButtonOptions_Click(object sender, EventArgs args)
{
try
{
button_options.Source = "Assets/Option_Icon_1.png";
var OptionsView = new OptionsView();
Navigation.PushAsync(OptionsView, true);
}
catch (Exception ex) { InterpretException("MainPage.ButtonStorage_Click()", ex); }
}

Related

Scrollview control not working properly in .Net MAUI (Android)

I have created custom tab control using ScrollView control and Bindable StackLayout control.
I have first created this solution in Xamarin.Forms (VS for Mac 2019) and it works fine in both platforms, but the same solution when developed in .Net MAUI (VS for Mac 2022 Prev) it's not working properly in Android.
Update 30 Jun 2022
There is an issue with BindableLayout (StackLayout) properties in MAUI currently so when we are changing values it does not get reflected, and because of this, I think I'm facing this issue. Here is the reference
Here is what I have done so far:
MainPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:poc_maui.ViewModels"
x:Class="poc_maui.Views.HomePage"
xmlns:tabs="clr-namespace:poc_maui.Views.SubViews"
Title="HomePage">
<ContentPage.BindingContext>
<vm:MainPageViewModel />
</ContentPage.BindingContext>
<Grid RowDefinitions="50, *" RowSpacing="0">
<ScrollView Grid.Row="0" Orientation="Horizontal" VerticalOptions="Start" HorizontalScrollBarVisibility="Never"
Scrolled="ScrollView_Scrolled">
<StackLayout x:Name="TabsView"
Orientation="Horizontal"
BindableLayout.ItemsSource="{Binding Tabs}" Spacing="0">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid RowDefinitions="*, 4" RowSpacing="0">
<Label Grid.Row="0"
Text="{Binding TabTitle}"
TextColor="White"
BackgroundColor="navy"
Padding="20,0"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
FontSize="12"
HeightRequest="40"/>
<BoxView Grid.Row="1"
Color="Yellow"
IsVisible="{Binding IsSelected}"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TabChangedCommand,
Source={x:Reference TabsView}}"
CommandParameter="{Binding .}"/>
</Grid.GestureRecognizers>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
<tabs:ParentRecordTabView Grid.Row="1" IsVisible="{Binding IsParentRecordTabVisible}"
VerticalOptions="FillAndExpand"/>
<tabs:AdditionalInfoTabView Grid.Row="1" IsVisible="{Binding IsAdditionalInfoTabVisible}"
VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage>
MainPageViewModel
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;
using poc_maui.Models;
namespace poc_maui.ViewModels
{
public class MainPageViewModel : BaseViewModel
{
#region Constructor
public MainPageViewModel()
{
GetTabs();
}
#endregion
#region Private Properties
private bool _isParentRecordTabVisible = true;
private bool _isAdditionalInfoTabVisible;
private ObservableCollection<TabViewModel> _tabs { get; set; }
#endregion
#region Public Properties
public bool IsParentRecordTabVisible
{
get => _isParentRecordTabVisible;
set { _isParentRecordTabVisible = value; OnPropertyChanged(nameof(IsParentRecordTabVisible)); }
}
public bool IsAdditionalInfoTabVisible
{
get => _isAdditionalInfoTabVisible;
set { _isAdditionalInfoTabVisible = value; OnPropertyChanged(nameof(IsAdditionalInfoTabVisible)); }
}
public ObservableCollection<TabViewModel> Tabs
{
get => _tabs;
set { _tabs = value; OnPropertyChanged(nameof(Tabs)); }
}
#endregion
#region Commands
public ICommand TabChangedCommand { get { return new Command<TabViewModel>(ChangeTabClick); } }
#endregion
#region Private Methods
private void GetTabs()
{
Tabs = new ObservableCollection<TabViewModel>();
Tabs.Add(new TabViewModel { TabId = 1, IsSelected = true, TabTitle = "Parent record" });
Tabs.Add(new TabViewModel { TabId = 2, TabTitle = "Additional Info" });
Tabs.Add(new TabViewModel { TabId = 3, TabTitle = "Contacts" });
Tabs.Add(new TabViewModel { TabId = 4, TabTitle = "Previous inspections" });
Tabs.Add(new TabViewModel { TabId = 5, TabTitle = "Attachments" });
SelectedTab = Tabs.FirstOrDefault();
}
private void ChangeTabClick(TabViewModel tab)
{
try
{
var tabs = new ObservableCollection<TabViewModel>(Tabs);
foreach (var item in tabs)
{
if (item.TabId == tab.TabId)
{
item.IsSelected = true;
}
else
{
item.IsSelected = false;
}
}
Tabs.Clear();
Tabs = new ObservableCollection<TabViewModel>(tabs);
switch (tab.TabId)
{
case 1:
IsParentRecordTabVisible = true;
IsAdditionalInfoTabVisible = false;
break;
case 2:
IsParentRecordTabVisible = false;
IsAdditionalInfoTabVisible = true;
break;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
#endregion
}
}
#ParentTabView.xaml
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="poc_maui.Views.SubViews.ParentTabView">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand" >
<Label
Text="Welcome to Parent tab!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentView>
#AdditionalInfoTabView.xaml
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="poc_maui.Views.SubViews.AdditionalInfoTabView">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand" >
<Label
Text="Welcome to Additiona info tab!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentView>
So what happens here in Android is when I'm clicking AdditionalInfo Tab then it will show a blank white screen and if you press the hardware back button and open the app again it will show AdditionalTab as selected and its views content as well.
If I remove switch() code part from the ViewModel then it will work fine but tabs will not change. Does anyone have idea about this kind of behavior of scroll view in MAUI?
The full source code is here: maui_sample
Does this work-around fix it?
MainPage.xaml:
<ScrollView x:Name "theScrollView" ... >
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
MessagingCenter.Subscribe<MainPageViewModel>(this, "update", (sender) =>
{
// Tell theScrollView to re-layout its contents.
(theScrollView as IView).InvalidateArrange();
});
}
MainPageViewModel:
private void ChangeTabClick(TabViewModel tab)
{
... make changes ...
MessagingCenter.Send<MainPageViewModel>(this, "update");
}
MAYBE:
I'm not sure if MessagingCenter Subscribe is on Dispatcher (Main) thread. To be reliable, do:
MessagingCenter.Subscribe<MainPageViewModel>(this, "update", (sender) =>
{
Dispatcher.Dispatch( () =>
{
(theScrollView as IView).InvalidateArrange();
});
}
UPDATE
There are other Maui bugs, that have a common "theme": Maui on Android does "something" related to layout only once - at the time the page is first drawn. UNFORTUNATELY, anything that is "not visible" at that time, is skipped. And won't work when later made visible.
Until such bugs are fixed, you'll have to do some work-around.
WORK-AROUND #1:
Start with ALL tabs IsVisible="True".
As soon as the page has been drawn the first time, in code-behind, create the desired Bindings on those IsVisible properties. Page drawn first time can be intercepted in a custom handler. But this is a temp work-around, so its easier to just run a method after a 250 ms delay. Use a boolean "flag" to make the method only run the first time.
Might have to do InvalidateArrange as shown above, to force the Bindings to function the first time.
OR WORK-AROUND #2:
Each time tab changes, use shell route to go to MainPage again. Keep same view model, so knows which tab to show first (and remembers any other state you care about).
Both of these are ugly.
I recommend creating an issue at .Net Maui github, and providing link to your github sample.
This is still not works for me properly but after looking at below two links I found that it it not what we are looking for. The Isvisible : false first and then on switch or check box change you are trying to make it visible then it will not visible but the actual control visible. So on look after I have see this link but again the answer is not what I was looking for.
Step to resolve.
On View use the Parent as ScrollView or control belongs to IView,IElement.
<ScrollView x:Name "myScrollView">
.....
...
Add Action on ViewModel
public delegate void Action(T obj);
Invoke the Action
Note: Make sure you call this on require not all the time.
e.g. On Visibility set in ViewModel call after visibility update.
MeasureAction?.Invoke("reSetVisibility");
Now on View's Code File, use Viewmodel and accept the invoke
Here Call the below line will works perfectly.
(myScrollView as IView).InvalidateMeasure();
That's IT... Enjoy IsVisible now and make your layout as require.

c# Xamarin Android WebView fit to landscape screen

Following the answer in the following link
"
Xamarin WebView scale to fit "
I've managed to fit the html live stream to the phone screen while it is in portrait mode. The result in portrait is acceptable. But when the screen rotates to landscape mode it exceeds the limits of the screen and in order to view the whole stream it requires for the user to scroll. I've found the following topic but it does not seem to work
Xamarin webview does not fill screen
What needs to be done in order for the stream to fit without scrolling. I feel that its simply. Do I follow the correct path?
I am using a custom renderer for the WebView
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Settings.BuiltInZoomControls = true;
Control.Settings.DisplayZoomControls = false;
Control.Settings.SetSupportZoom(true);
Control.Settings.LoadWithOverviewMode = true;
Control.Settings.UseWideViewPort = true;
}
/*
* The code for the second link
*
* Control.Settings.JavaScriptEnabled = true;
Control.EvaluateJavascript("document.documentElement.style.height = screen.height + 'px';", null);
Control.EvaluateJavascript("document.documentElement.style.width = screen.width + 'px';", null);
*/
}
}
The xaml code of the page is
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
NavigationPage.HasNavigationBar="False"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:androidclient="clr-namespace:AndroidClient"
x:Class="AndroidClient.LiveViewPage">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Button x:Name="ExitFullScreenBtn" Clicked="ExitFullScreenBtn_Clicked"
HorizontalOptions="End" VerticalOptions="Start" WidthRequest="50"
Text="X"></Button>
<androidclient:CustomWebView x:Name="Browser" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
></androidclient:CustomWebView>
</StackLayout>
</ContentPage>
The c# for the same page
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LiveViewPage : ContentPage
{
public LiveViewPage()
{
InitializeComponent();
var url = "http://192.168.1.7:8080/";
Browser.Source = url; }
private void ExitFullScreenBtn_Clicked(object sender, EventArgs e)
{
Console.WriteLine("Closed Live");
Navigation.PopAsync();
}
}

How to select an image from gallery in xamarin forms?

I'm trying to pick an image from gallery and i've done everything that is in this doc, but i'm getting an error with the "image" object in my .cs implementation.
My .cs code:
public partial class MyProfilePage : ContentPage
{
public MyProfilePage()
{
InitializeComponent();
BindingContext = new MyProfileViewModel();
}
async void ImageButton_Clicked(object sender, EventArgs e)
{
//(sender as Button).IsEnabled = true;
Stream stream = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
if (stream != null)
{
image = ImageSource.FromStream(() => stream);
}
(sender as Button).IsEnabled = true;
}
}
Welcome to SO!
but i'm getting an error with the "image" object in my .cs implementation.
You can check the Xaml file that whether contain the x:Name="image".
Such as follows:
<ContentPage Title="Photo Picker"
Icon="monkeyicon.png">
<StackLayout Margin="20,35,20,20" >
<Label Text="Photo Picker"
FontAttributes="Bold"
HorizontalOptions="Center" />
<Button Text="Pick Photo"
Clicked="OnPickPhotoButtonClicked" />
<Image x:Name="image" />
</StackLayout>
</ContentPage>
If not works , you can refer to following steps:
First, you need to share the detail error logs here, and explain this occurs in iOS or
Android.
Second, you also can refer to this official sample Xamarin.Forms - Dependency Service to check where problem is.

Cannot see the Master Section in Masterdetail page of Xamarain

I am trying to get into Xamarain development and I am trying to do a small App which have the login screen and when the user press the screen it will be navigated to a Dashboard Which is actually a Master Details Page
My master details Page is as follows
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MobArt.View"
x:Class="MobArt.View.MasterFrontPage">
<MasterDetailPage.Master>
<ContentPage Tittle="Menu">
<StackLayout Orientation="Vertical">
<Button Text="WF"></Button>
<Button Text="FW"></Button>
<Button Text="RO Out"></Button>
<Button Text="RO IN"></Button>
<Button Text="Loan Out"></Button>
<Button Text="Loan IN"></Button>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<local:RollScan></local:RollScan>
</MasterDetailPage.Detail>
</MasterDetailPage>
And Codebehind is just constructor
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterFrontPage : MasterDetailPage
{
public MasterFrontPage()
{
InitializeComponent();
}
}
And in LoginButtonclick I tried to navigate using below cODE
async void OnLoginButtonClicked(object sender, EventArgs e)
{
var Username = usernameEntry.Text;
var Password = passwordEntry.Text;
UserServices usrserv = new UserServices();
User usr = await usrserv.GetSelecteduserdataASync(Username, Password);
if(usr.User_PK!=0&& usr.UserLoc_PK!=0)
{
MasterDetailPage fpm = new MasterFrontPage();
Application.Current.MainPage = fpm;
}
}
Iam able to See the Details Section in my Page.But cannot see the Menu(Master section) when I tried for Android. Can Anyone suggest what I am missing
I am able to See the Details Section in my Page.But cannot see the Menu(Master section) when I tried for Android. Can Anyone suggest what I am missing
The Menu(Master Section) is still there, but is hidden. You can get it by swipping from the left:
But it is highly recommended using NavigationPage as the details Page:enter code here
...
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:RollScan />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
...
Thus, you will have a toggle button for your menu:

How to access StackLayout from the backend?

I have been using Xamarin Forms to develop iOS and Android applications. I want to access a StackLayout that is within a TabbedPage so I can make it visible or hidden whenever the user changes tabs, but when I try to access the StackLayout I get "This does not exist in the current context". Here is my XAML code and my CS code.
CS
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace DebuggerTestAndroidIOS
{
public partial class PatientTabPage : TabbedPage
{
public PatientTabPage ()
{
InitializeComponent ();
ItemsSource = PatientDataModel.tabs;
//vitalSignsStack.IsVisible = true;
this.CurrentPageChanged += (object sender, EventArgs e) => {
var i = this.Children.IndexOf(this.CurrentPage);
System.Diagnostics.Debug.WriteLine("Page No:"+i);
if (i == 1){
vitalSignsStack.IsVisible = true;
}
};
}
}
}
XAML
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DebuggerTestAndroidIOS.PatientTabPage">
<TabbedPage.ItemTemplate>
<DataTemplate>
<ContentPage Title ="{Binding TabName}">
<!--Parent Wrapper layout-->
<StackLayout Orientation="Vertical" BackgroundColor="White">
<StackLayout x:Name="vitalSignsStack" Orientation="Horizontal" IsVisible="false">
<Image Source="VitalSigns.png" HorizontalOptions="Center"/>
</StackLayout>
</StackLayout><!--End parent wrapper-->
</ContentPage>
</DataTemplate>
</TabbedPage.ItemTemplate>
</TabbedPage>
An element is only going to be accesible within the context of the page that created it - in this case, the ContentPage.
If you want to reach it from outside of the ContentPage, you will need to add a public method or property to the ContentPage that exposes it.
You cannot access a control inside a DataTemplate with its name. The problem is, that it will be repeated and so this name would exist multiple times, what is not allowed.
But why don't you create a new Page like this:
public partial class PatientTabContentPage : TabbedPage
{
public PatientTabContentPage ()
{
InitializeComponent ();
}
public HideVitalSignsStack(bool true){
vitalSignsStack.IsVisible = true;
}
}
And change DataTemplate to
<DataTemplate>
<PatientTabContentPage Title ="{Binding TabName}">
</DataTemplate>
Then hide the stackpanel with
this.CurrentPageChanged += (object sender, EventArgs e) => {
var page = CurrentPage as PatientTabContentPage;
var i = this.Children.IndexOf(this.CurrentPage);
System.Diagnostics.Debug.WriteLine("Page No:"+i);
if (i == 1){
page.HideVvitalSignsStack(true);
}
};
Thanks for your efforts. I tried them, still was not able to access the StackLayouts. I modified a little bit my code, this helped a lot and made everything easier: Creating different layouts for each tab

Categories

Resources