reusability user controls of Xaml in xamarin.forms - xamarin.forms

I am trying a new approach i.e. XAML to make application in xamarin.forms. At this time i am facing an issue to reuse my stack layout which is having a image and label. How i can reuse my layout in different pages using XAML.

You can actually define your custom component in a separate XAML file and then just link the component wherever you need it.
For example a label with image can be grouped together in a dedicated XAML file:
<?xml version="1.0" encoding="utf-8" ?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="UserControls.ImageWithTitle"
VerticalOptions="Center" HorizontalOptions="Center" >
<Label HorizontalOptions="Center"
x:Name="TitleLabel" />
<Image Source="noimage.png" />
</StackLayout>
On the .cs file I've defined a binding for the TitleLabel
public string TitleName
{
get { return TitleLabel.Text; }
set { TitleLabel.Text = value; }
}
So when you include the component on another layout, you can assign the label value directly (or via a binding):
<usercontrols:ImageWithTitle TitleName="Home"/>

Related

Change font size of tabs in a TabbedPage

Is there anyway to changed the font size of the ContentPage? enter image description here
Example is that, the last Tab. SUMMARY is cut and I want to set the font size to small.
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Test.ViewModels"
xmlns:model="clr-namespace:Test.Models"
Shell.TabBarBackgroundColor="Transparent"
x:Class="Test.Views.MainPage">
<ContentPage Title="Delivery">
</ContentPage>
<ContentPage Title="Pending">
</ContentPage>
<ContentPage Title="Success">
</ContentPage>
<ContentPage Title="Failed">
</ContentPage>
<ContentPage Title="Summary">
</ContentPage>
</TabbedPage>
I tried adding this:
<NavigationPage.TitleView>
<Label Text="This is my Title" FontSize="12" TextColor="White" />
</NavigationPage.TitleView>
But doesn't do anything.
I was able to reproduce the tab title as the screenshot you privided. You could try the code below to set the TabMode, TabGravity in custom renderer.
[assembly: ExportRenderer(typeof(TabbedPage),
typeof(CustomTabbedPageRenderer))]
namespace App15.Droid
{
class CustomTabbedPageRenderer : TabbedPageRenderer
{
private TabLayout tabLayout = null;
public CustomTabbedPageRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.TabbedPage> e)
{
base.OnElementChanged(e);
this.tabLayout = (TabLayout)this.GetChildAt(1);
tabLayout.TabMode = TabLayout.ModeScrollable;
tabLayout.TabGravity = TabLayout.GravityFill;
}
}
}
Normally, we could set the TextSize in tabbed page custom renderer. But sometimes we would miss the different types if TextView. So i think the way above would be better. You could also check the way to set the TextSize in custom renderer.
Tabbed page custom renderer on Xamarin.Forms
When TabbedPage use android:TabbedPage.ToolbarPlacement="Bottom" to set the tabs in the bottom, the renderer above does not work. You could refer to teh solution in the link below.
How to change Tabbar text size when using TabbedPage.ToolbarPlacement="Bottom" in Xamarin Android?

Images not shown in CarouselView on first load

The scenario is, a content page is opened with a carousel displaying multiple images.
The images are bound as an observable collection of ImageSource and they're fine
When the page is displayed, I can see the carousels empty.
I have tried with two different CarouselView. The one that xamarin brings by default and the one from CardsView nuget library. With both of them the result is the same.
The page shows the carousel views without the image, but with the right amount of items (i.e: I can see that the observable collection has 3 items).
I suspect it must be some kind of racing condition because as soon as I force a hot reload of the view while debuggin (ie: I save the XAML and the view reloads in the device) I can see the carousels with the images displayed properly.
To reproduce it I have also added the images separately outside the carousel to prove that they're there.
I've also added both carousels to the view. This is the result on the first load:
and after I save the xaml (without doing anything on it at all) and the view refreshes, this is what I see, and it's correct.
Notice how in the first load, the 3 images are displayed correctly outside the carousel, but not inside any of the carousels, although there is no problem if what I add in the carousel is text.
Any idea where may be the issue? Is it a racing condition? Or is it a problem with both different carousels?
This is my code
<?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:viewModels="clr-namespace:Sasw.AforoPass.ViewModels;assembly=Sasw.AforoPass"
xmlns:panCardView="clr-namespace:PanCardView;assembly=PanCardView"
xmlns:controls="clr-namespace:PanCardView.Controls;assembly=PanCardView"
mc:Ignorable="d"
x:Class="Sasw.AforoPass.Views.MainPage"
x:DataType="viewModels:MainViewModel">
<ContentPage.Content>
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</StackLayout.Resources>
<CarouselView BackgroundColor="White"
x:Name="Carousel"
ItemsSource="{Binding Images}">
<CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Image Source="{Binding .}"></Image>
<Label Text="image should be here"></Label>
</StackLayout>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<panCardView:CarouselView
ItemsSource="{Binding Images}"
BackgroundColor="{StaticResource ColorPrimary}"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<panCardView:CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand">
<Image Source="{Binding .}"></Image>
<Label Text="image should be here"></Label>
</StackLayout>
</DataTemplate>
</panCardView:CarouselView.ItemTemplate>
<controls:LeftArrowControl />
<controls:RightArrowControl />
<controls:IndicatorsControl ToFadeDuration="1500"/>
</panCardView:CarouselView>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[0]}"></Image>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[1]}"></Image>
<Image WidthRequest="20" HeightRequest="20" Source ="{Binding Images[2]}"></Image>
<Button Text="Close"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
and the collection bound is:
public ObservableCollection<ImageSource> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
}
UPDATE 1:
I've just edited the whole question because at first I though the problem could be related with Rg.Plugins.Popup because it was happening within a popup. But I've tried the same with a normal content page and the problem remains, so it's definitely something to do with the carousel.
UPDATE 2
I keep investigating. So far, the problem seems to be with image streams in carousels, either the Xamarin carousel or the CardView's library carousel (which have different mechanisms).
I've tried adding options and height and width request to the image that accesses the stream without luck
<!--in DataTemplate within the carousel-->
<Image Source="{Binding .}" HeightRequest="100"
WidthRequest="100"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"></Image>
<Label Text="image should be here"></Label>
UPDATE 3:
Thanks to Jack Hua for his comment and repo, I updated it to reflect the issue I have and managed to reproduce it at this specific commit here
Basically I have taken the same repo, added a QRCoder library to quickly generate images as byte[] at runtime and added the image sources from stream.
public Model() {
Images = new ObservableCollection<ImageSource>();
var imageOne = GetQrImageAsBytes();
var imageTwo = GetQrImageAsBytes();
var imageThree = GetQrImageAsBytes();
// This works
//Images.Add(ImageSource.FromFile("logo.jpg"));
//Images.Add(ImageSource.FromFile("sample.jpg"));
//Images.Add(ImageSource.FromFile("ttt.png"));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageOne)));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageTwo)));
Images.Add(ImageSource.FromStream(() => new MemoryStream(imageThree)));
}
private byte[] GetQrImageAsBytes()
{
var randomText = Guid.NewGuid().ToString();
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode(randomText, QRCodeGenerator.ECCLevel.L);
var qRCode = new PngByteQRCode(qrCodeData);
var qrCodeBytes = qRCode.GetGraphic(20);
return qrCodeBytes;
}
UPDATE 4:
I found the issue, I had to try a few times to make sure this really was the problem, but it certainly is.
Re-Sharper suggests me to make a change in ItemsSource="{Binding Images}" as it does not recognize Images. So I follow Re-Sharper recommendation and let it add a x:DataType="app265:Model" in xaml's ContentPage tag. That causes the problem. I don't really know why, but I'll sleep better tonight :)
<?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:app265="clr-namespace:App265;assembly=App265"
mc:Ignorable="d"
x:Class="App265.MainPage"
x:DataType="app265:Model"><!--this line that Re-Sharper adds causes the issue-->
Is this a Xamarin bug? or does it make sense to you? An explanation to why this x:DataType breaks things but only on the first load would be chosen as the right answer.
x:DataType="ImageSource"
Add this to your carousel view DataTemplate. It should solve your problem.
It seems there some issues with BindignContext detecting, so you should "help" to detect it yourself.
Same problem here with xamarin.forms 5.0.0.2012 but in my case my image sources are urls. It worked with x:DataType="x:String" in the carousel view DataTemplate
<CarouselView
ItemsSource="{Binding ImageSources}"
>
<CarouselView.ItemTemplate>
<DataTemplate
x:DataType="x:String">
<forms:CachedImage
Aspect="AspectFit"
ErrorPlaceholder="Placeholder"
LoadingPlaceholder="Placeholder"
DownsampleWidth="300"
DownsampleToViewSize="True"
Source="{Binding .}">

Master Detail breaks parameters?

I don't need a MasterDetail until I've navigated through two regular ContentPages first, where I collect information that the MasterDetail will need. The ContentPages make use of the INavigationParameters sent to OnNavigatedTo and OnNavigatedFrom for that collected information.
The the SecondPage viewmodel handles a button command wherein the handler calls the MasterDetail:
NavigationService.NavigateAsync("CompareSelectionsMasterDetail/CompareSelectionsNavigationPage/RangeCircle",
parameters);
Sure enough, the RangeCircleViewModel.OnNavigatedTo receives the parameters. However, when I go back (like with the Android back button), its OnNavigatedFrom is not called, thereby sending null parameters back leaving my ContentPages with no idea what their state was.
The viewmodel for CompareSelectionMasterDetail doesn't do anything and the viewmodel for CompareSelectionsNavigationPage just does this:
public class CompareSelectionsNavigationPage : NavigationPage, INavigationPageOptions
{
public bool ClearNavigationStackOnNavigation => false;
}
I'm not sure what it means to have a NavigationPage in the MasterDetailPage XAML and the separate CompareSelectionsNavigationPage that I call with Prism but if I remove the XAML one, the RangeCircle page doesn't render. If I only have the XAML one and change the Prism navigation to CompareSelectionsMasterDetail/NavigationPage/RangeCircle then I get the same behavior as using both NavigationPages.
My MasterDetail XAML is simple.
> <MasterDetailPage.Master>
> <NavigationPage Title="Required Foo" Icon="hamburger_icon.png">
> <x:Arguments>
> <ContentPage Title="Menu">
> <StackLayout Padding="40">
> <!-- TODO: // Update the Layout and add some real menu items -->
> <Label Text="Hello John"/>
> <Button Text="Range Circle" Command="{Binding NavigateToRangeCircleCommand}" CommandParameter="ViewA" />
> </StackLayout>
> </ContentPage>
> </x:Arguments>
> </NavigationPage> </MasterDetailPage.Master>
Every example I can find of Master Detail (especially with Prism) starts off the app with Master Detail page. I'm not sure if my usage is what breaks OnNavigatedFrom? (Basically ContentPage(Page1)->ContentPage(Page2)->MasterDetail->NavigationPage->NavigationPage->ContentPage(RangeCircle))
The individual ContentPages don't have a problem calling OnNavigatedFrom.

Tab icons in a Xamarin.Forms UWP TabbedPage?

When putting together a TabbedPage in Xamarin.Forms, how do I get UWP to use the page's Icon property?
It looks like UWP could support this just fine, if I configure my Forms attributes/files correctly.
Here's my TabbedPage XAML. The icons are all set up and working for iOS and Android, and even the on-page Image in UWP renders fine (meaning the files are likely in the project correctly).
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:tabbed"
x:Class="tabbed.MainPage">
<TabbedPage.Children>
<local:InitialPage Title="Tab1" Icon="star.png" />
<ContentPage Title="Tab2" Icon="gear.png">
<ContentPage.Content>
<StackLayout>
<Label Text="A nice label." />
<Image Source="star.png" /><!-- works here -->
</StackLayout>
</ContentPage.Content>
</ContentPage>
</TabbedPage.Children>
</TabbedPage>
I outlined how this is possible here http://depblog.weblogs.us/2017/07/12/xamarin-forms-tabbed-page-uwp-with-images/
In short, you need to change the default HeaderTemplate that is being used by UWP. But due to the way Xamarin forms is started, this is not straightforward.
So you need to inject a custom template into the resource dictionary.
Example project is up on Github here https://github.com/Depechie/XamarinFormsTabbedPageUWPWithIcons
Longer detail:
You need to supply your own TabbedPageStyle and switch out the one that Xamarin is using for their UWP rendering.
So the new style contains an Image where we data bind the Source to the Xamarin Icon property.
<Style x:Key="TabbedPageStyle2" TargetType="uwp:FormsPivot">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Icon, Converter={StaticResource IconConverter}}" Width="15" Height="15" />
<TextBlock Name="TabbedPageHeaderTextBlock" Text="{Binding Title}"
Style="{ThemeResource BodyTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The actual style switching is done in the App.Xaml.cs like this
((Style)this.Resources["TabbedPageStyle"]).Setters[0] = ((Style)this.Resources["TabbedPageStyle2"]).Setters[0];
You'll also need a converter to be sure the Image control understands the Icon source giving by Xamarin
public class IconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value != null && value is Xamarin.Forms.FileImageSource)
return ((Xamarin.Forms.FileImageSource)value).File;
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
As it is currently, the UWP TabbedPage renderer does not use the Icon property at all, so getting tab icons will require a custom renderer. Even the official UWP samples don't actually seem to have this baked-in, requiring a custom UserControl.
The Android TabbedPageRenderer and iOS TabbedRenderer, and even the macOS TabbedPageRenderer, use the Icon property to adjust the tab UI, but the UWP renderer would need updating to make this work.

Back button missing on NavigationPage

I'm using Prism.Forms 6.3 in a Xamarin.Forms 2.3.4.247 project. I'm having a hard time tracking why the back arrow button isn't visible when I navigate to a details page within a Master/Detail setup.
I can navigate to the Pages just fine, but the back-button never shows up. Instead, the hamburger menu icon is always visible. This is my "Master" page.
<?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:local="clr-namespace:FloatSink.Apps.Mobile.Views.ValueConverters"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="FloatSink.Apps.Mobile.Views.MainPage"
BackgroundColor="Blue">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<StackLayout Padding="40">
<Label Text="Hello" />
<Button Text="Feed" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/FeedPage" />
<Button Text="Options" Command="{Binding NavigateCommand}" CommandParameter="NavigationPage/OptionsPage" />
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
</MasterDetailPage>
This is two of my Detail pages.
<?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="FloatSink.Apps.Mobile.Views.FeedPage">
<Label Text="Hello from Feed Page!" />
</ContentPage>
<?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="FloatSink.Apps.Mobile.Views.OptionsPage">
<Label Text="Hello from Options Page!" />
</ContentPage>
I navigate to it using the CommandParameter specified in my Master page, using the navigation service in the MainPageViewModel.
private void NavigateToPage(string uri)
{
this.navigationService.NavigateAsync(uri);
}
I've setup my NavigationPage like this during the startup of the app so I land on the FeedPage first, then I can navigate to the OptionsPage.
public class App : PrismApplication
{
public App(IPlatformInitializer dependencyRegister) : base(dependencyRegister) { }
protected override void OnInitialized()
{
base.NavigationService.NavigateAsync("MainPage/NavigationPage");
}
protected override void RegisterTypes()
{
var builder = new ContainerBuilder();
builder.RegisterModule<NavigationRegistration>();
builder.RegisterModule<ServicesRegistration>();
base.Container.RegisterTypeForNavigation<NavigationPage>();
// This is deprecated but we have to wait for Prism.Autofac to update itself
builder.Update(base.Container);
}
}
The DI registrations associated with navigation are done in this module:
internal class NavigationRegistration : Module
{
/// <summary>
/// Load the navigation related types into the given builder.
/// </summary>
/// <param name="builder">Container Builder that will be turned into the final IOC Container</param>
protected override void Load(ContainerBuilder builder)
{
// Register the NavigationPage in Xamarin with the Prism Navigation system.
//builder.RegisterType<NavigationPage>().AsSelf();
//PageNavigationRegistry.Register(nameof(NavigationPage), typeof(NavigationPage));
// Get all of the Types that represent a View in our assembly for DI and navigation registration
// If start-up time ever becomes an issue, we can replace this assembly scan with explicit registrations.
Type[] viewTypes = base.ThisAssembly.GetTypes().Where(type => type.IsAssignableTo<Page>()).ToArray();
// Iterate over each discovered View Type and register it with the navigation system and DI container.
for(int index = 0; index < viewTypes.Length; index++)
{
Type viewType = viewTypes[index];
builder.RegisterType(viewType).Named<Page>(viewType.Name);
// If we ever need to disconnect a view name from its Type name, we can do so here.
// We would build a custom attribute to decorate the view with, pull the attribute from the Type
// and register the Type with the attribute value.
PageNavigationRegistry.Register(viewType.Name, viewType);
}
}
}
Again I can each one of my detail pages without problem, the hamburger menu exists and I can open the Master page to view my buttons used for navigating. I just can't navigate backwards once I'm there. Is that something I'm supposed to wire up myself?
I'm not sure I'm reading your question right but it sounds like this is normal behavior. In my (short) experience with XF/Prism, every navigation from the master is a beginning of the stack. If you were to add another page, e.g. from Master->PageA->PageB, I would expect Page A to have the hamburger menu but going to PageB would give you the back arrow.
For using NavigationPage inside uri you should register it for navigation in the App.xaml.cs:
protected override void RegisterTypes()
{
// your registrations
Container.RegisterTypeForNavigation<NavigationPage>();
}
I most cases it is the reason.
To Navigate To Master Page
"/MasterPage/NavigationPage/ViewA"
To Navigate out of Master Page from ViewA and with back button
"ViewB"
You need to start your app with MainPage = new NavigationPage(new StartPage()); That is how it is solved in normal situation. So maybe try to register your MainPage wrapped in a NavigationPage.

Resources