How to show Navigationbar 'TitleView' Common for all pages - xamarin

I have added TitleView in MainPage to show on Navigationbar but it shows only for MainPage when I navigate to some other page Navigationbar displaying empty.
Below code I have in MainPage.xaml file
<NavigationPage.TitleView>
<RelativeLayout HorizontalOptions="Fill"
<Image Source="bell.png" HeightRequest="25" WidthRequest="25" x:Name="imgBell"
RelativeLayout.YConstraint="{ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=0.018,Constant=10}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding GetStaffAnnouncementCommand}"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
<Label FontSize="10" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" BackgroundColor="Transparent" Text="2" TextColor="Red"
HeightRequest="22" WidthRequest="23" x:Name="labelText">
</Frame>
</RelativeLayout>
</NavigationPage.TitleView>
When I click on bell icon and move to second page TitleView not displaying at all
How can I display TitleView common for all pages?

I wrote a demo about your needs.
There is GIF.
You could Write a custom page inherits from 'ContentPage' and add toolbar item to it.
Update
I achieve it with DependencyService, If you want to know more details about how to achieve DependencyService, you could refer to this blog and my code.
https://www.xamboy.com/2018/03/08/adding-badge-to-toolbaritem-in-xamarin-forms/
There is code that used DependencyService.
Custom ToolbarPage
public class ToolbarPage : ContentPage
{
public ToolbarItem toolbarItem;
public static int item;
public ToolbarPage()
{
// public ToolbarItem(string name, string icon, Action activated, ToolbarItemOrder order = ToolbarItemOrder.Default, int priority = 0);
toolbarItem =new ToolbarItem();
toolbarItem.Icon = "ring2.png";
toolbarItem.Order = ToolbarItemOrder.Primary;
// toolbarItem.Text = item+"";
toolbarItem.Priority = 0;
toolbarItem.Clicked += ToolbarItem_Clicked;
ToolbarItems.Add(toolbarItem);
if (item >= 1)
{
DependencyService.Get<IToolbarItemBadgeService>().SetBadge(this, toolbarItem, $"{item}", Color.Red, Color.White);
}
}
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
item = item + 1;
DependencyService.Get<IToolbarItemBadgeService>().SetBadge(this, toolbarItem, $"{item}", Color.Red, Color.White);
}
}
Main.cs
public partial class MainPage : ToolbarPage
{
public MainPage()
{
InitializeComponent();
bt1.Text = ToolbarPage.item.ToString();
bt1.Clicked += async (o, e) =>
{
await Navigation.PushAsync(new HelloToolbarInher());
};
}
protected override async void OnAppearing()
{
//You must make a delay,
await Task.Delay(100);
bt1.Text = ToolbarPage.item.ToString();
DependencyService.Get<IToolbarItemBadgeService>().SetBadge(this, toolbarItem, $"{ToolbarPage.item}", Color.Red, Color.White);
}
}
Do not forget to change MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<local:ToolbarPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NaviagationViewDemo"
x:Class="NaviagationViewDemo.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Button
x:Name="bt1"
Text="click"
></Button>
</StackLayout>
There is my new demo.
https://github.com/851265601/NewNaviagationViewDemo

Related

Override navbar back button click on Xamarin.Forms

Is there any way to show an alert msg before clicking navbar back icon & exiting the page.
I've tried this solution but it's not working.
If you want to capture the back button click the Action Bar in android platform, you can try to override the OnOptionsItemSelected() in the MainActivity class, which allows us to capture the navigation bar’s back button click.
Please follow up the following steps:
1.create a base contentpage CoolContentPage.cs
public class CoolContentPage: ContentPage
{
/// <summary>
/// Gets or Sets the Back button click overriden custom action
/// </summary>
public Action CustomBackButtonAction { get; set; }
public static readonly BindableProperty EnableBackButtonOverrideProperty =
BindableProperty.Create(
nameof(EnableBackButtonOverride),
typeof(bool),
typeof(CoolContentPage),
false);
/// <summary>
/// Gets or Sets Custom Back button overriding state
/// </summary>
public bool EnableBackButtonOverride
{
get
{
return (bool)GetValue(EnableBackButtonOverrideProperty);
}
set
{
SetValue(EnableBackButtonOverrideProperty, value);
}
}
}
2.override the OnOptionsItemSelected() event in MainActivity class in order to capture the nav bar back button click in Android for Xamarin Forms.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
//TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
AndroidX.AppCompat.Widget.Toolbar toolbar
= this.FindViewById<AndroidX.AppCompat.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
// check if the current item id
// is equals to the back button id
if (item.ItemId == 16908332) // xam forms nav bar back button id
{
// retrieve the current xamarin
// forms page instance
var currentpage = (CoolContentPage)Xamarin.Forms.Application.Current.
MainPage.Navigation.NavigationStack.LastOrDefault();
// check if the page has subscribed to the custom back button event
if (currentpage?.CustomBackButtonAction != null)
{
// invoke the Custom back button action
currentpage?.CustomBackButtonAction.Invoke();
// and disable the default back button action
return false;
}
// if its not subscribed then go ahead
// with the default back button action
return base.OnOptionsItemSelected(item);
}
else
{
// since its not the back button
//click, pass the event to the base
return base.OnOptionsItemSelected(item);
}
}
public override void OnBackPressed()
{
// this is really not necessary, but in Android user has both Nav bar back button
// and physical back button, so its safe to cover the both events
var currentpage = (CoolContentPage)Xamarin.Forms.Application.Current.
MainPage.Navigation.NavigationStack.LastOrDefault();
if (currentpage?.CustomBackButtonAction != null)
{
currentpage?.CustomBackButtonAction.Invoke();
}
else
{
base.OnBackPressed();
}
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Here, we need to create Toolbar.xml in folder layout
Toolbar.xml
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
3.In xamarin forms,we can use CoolContentPage we created above as a XAML page in Xamarin Forms solution:
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<xfbackbtnapp:CoolContentPage xmlns:xfbackbtnapp="clr-namespace:XFBackBtnApp"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XFBackBtnApp.MainPage"
EnableBackButtonOverride="False"
Title="Home Page"
BackgroundColor="#00bfff"
>
<StackLayout
Spacing="20"
Padding="20,10,20,10"
VerticalOptions="Center"
HorizontalOptions="Center" >
<Label Text="Welcome to Navigation Bar Back button Click overriding in Xamarin Forms!"
FontSize="20"
HorizontalTextAlignment="Center"
TextColor="White"/>
<Button Text="Open Next Page" FontSize="15" BackgroundColor="White" Clicked="OpenPageButton_OnClicked"></Button>
</StackLayout>
</xfbackbtnapp:CoolContentPage>
MainPage.xaml.cs
public partial class MainPage : CoolContentPage
{
public MainPage()
{
InitializeComponent();
}
private void OpenPageButton_OnClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Page1());
}
}
Page1.xaml
<?xml version="1.0" encoding="utf-8" ?>
<xfbackbtnapp:CoolContentPage xmlns:xfbackbtnapp="clr-namespace:XFBackBtnApp"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
EnableBackButtonOverride="False"
BackgroundColor="#00bfff"
Title="Page 1"
x:Class="XFBackBtnApp.Page1">
<ContentPage.Content>
<StackLayout
Spacing="20"
Padding="20,10,20,10"
VerticalOptions="Center"
HorizontalOptions="Center" >
<Label Text="Ok, this is just a normal Page! Click next Page to see the Navigation Bar Back button click overridden behavior..."
FontSize="20"
HorizontalTextAlignment="Center"
TextColor="White"/>
<Button Text="Open Next Page" FontSize="15" BackgroundColor="White" Clicked="OpenPageButton_OnClicked"></Button>
</StackLayout>
</ContentPage.Content>
</xfbackbtnapp:CoolContentPage>
Page1.xaml.cs
public partial class Page1 : CoolContentPage
{
public Page1()
{
InitializeComponent();
}
private void OpenPageButton_OnClicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Page2());
}
}
Page2.xaml
<?xml version="1.0" encoding="utf-8" ?>
<xfbackbtnapp:CoolContentPage xmlns:xfbackbtnapp="clr-namespace:XFBackBtnApp"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XFBackBtnApp.Page2"
EnableBackButtonOverride="True"
BackgroundColor="#00bfff"
Title="Page 2"
>
<ContentPage.Content>
<StackLayout
Spacing="20"
Padding="20,10,20,10"
VerticalOptions="Center"
HorizontalOptions="Center" >
<Label Text="This is the cool page, which has the Navigation Bar Back button click overriden. How go ahead and click that Back button! ;)"
FontSize="20"
HorizontalTextAlignment="Center"
TextColor="White"/>
</StackLayout>
</ContentPage.Content>
</xfbackbtnapp:CoolContentPage>
Page2.xaml.cs
public partial class Page2 : CoolContentPage
{
public Page2()
{
InitializeComponent();
if (EnableBackButtonOverride)
{
this.CustomBackButtonAction = async () =>
{
var result = await this.DisplayAlert(null,
"Hey wait now! are you sure " +
"you want to go back?",
"Yes go back", "Nope");
if (result)
{
await Navigation.PopAsync(true);
}
};
}
}
}
Note:
1.Here,I added property EnableBackButtonOverride="True" to the root of Page2.xaml, then if we can press the back button of the Toolbar on the top of the page or press the soft back button of our Phone, a DisplayAlert will pop up.
2.If you want to achieve this function in Ios,you can check article Override Navigation Bar back button click in Xamarin Forms.

Position View below RelativeLayout

Context of the problem:
I do have a StackLayout with a lot of entries. When the user taps on an entry I do want to show below the tapped entry an info box. This info box should visually be above the next entry (kind of like a tooltip). The entry can have a dynamic height.
What is my approach:
Using a RelativeLayout it should be possible to position views outside the bounds of the RelativeLayout which represents the entry.
Something like this:
<StackLayout>
<BoxView BackgroundColor="Green" HeightRequest="150" ></BoxView>
<RelativeLayout BackgroundColor="Yellow" x:Name="container">
<Label Text="This is the entry"></Label>
<BoxView BackgroundColor="Aqua"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=container, Property=Y, Factor=1, Constant=100}"></BoxView>
</RelativeLayout>
<BoxView BackgroundColor="Green" HeightRequest="150" ></BoxView>
</StackLayout>
In this sample code the green BoxView's are kind of the entries before and after the one I do want to show. This is the result:
This makes actually sense, as I've linked to the Y-Property of the container and added 100 using "Constant".
And this is what I do want to archive:
I want to have a StackLayout with multiple entries. Whenever I click on one of this entries (yellow) right below an info should appear (blue).
How do I have to specify the YConstraint on the BoxView (which should illustrate the info window) to archive my goal? Or am I on a wrong path and another solution fits better?
I write a demo about your needs, here is running GIF.
First of all, I create content view.
<ContentView.Content>
<RelativeLayout x:Name="container" BackgroundColor="Yellow">
<Entry Text="This is the entry" x:Name="MyEntry" Focused="MyEntry_Focused" Unfocused="MyEntry_Unfocused">
</Entry>
</RelativeLayout>
</ContentView.Content>
Here is background code about content view.
public partial class FloatEntry : ContentView
{
BoxView boxView;
public FloatEntry()
{
InitializeComponent();
boxView = new BoxView();
boxView.BackgroundColor = Color.Red;
boxView.WidthRequest = 200;
}
private void MyEntry_Focused(object sender, FocusEventArgs e)
{
container.Children.Add(boxView,Constraint.RelativeToView(MyEntry, (Parent, sibling) =>
{
return sibling.X + 100;
}), Constraint.RelativeToView(MyEntry, (parent, sibling) =>
{
return sibling.Y + 50;
}));
container.RaiseChild(boxView);
}
private void MyEntry_Unfocused(object sender, FocusEventArgs e)
{
container.Children.Remove(boxView);
}
}
}
But If you used this way to achieve it, you want to BoxView to cover the below Entry. You have to put the content view to a RelativeLayout as well.
<RelativeLayout x:Name="myRl">
<myentry:FloatEntry x:Name="myfloat" HorizontalOptions="StartAndExpand" HeightRequest="50" >
<myentry:FloatEntry.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</myentry:FloatEntry.GestureRecognizers>
</myentry:FloatEntry>
<myentry:FloatEntry HorizontalOptions="StartAndExpand" HeightRequest="50"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=myfloat, Property=Y, Factor=1, Constant=50}"
>
</myentry:FloatEntry>
</RelativeLayout>
Here is layout background code.
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
// I need to use following method to move the Boxview cover the blew Entry
myRl.RaiseChild(myfloat);
}
}
A more generic approach would be to write your own control which could be named as InfoBoxPopup (bascially a ContentPage) which you open manually once the Entry gets Focused and Close it on Unfocus.
Just be sure that you have on top of every page a grid panel defined.
In the InfoBox.xaml you define your custom style (panel, label, margins, IsInputTransparent?, etc. to show the custom text or other stuff)
public partial class InfoBoxPopup : ContentView
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(InfoBoxPopup));
public InfoBoxPopup()
{
InitializeComponent();
}
public string? Text
{
get => (string?)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public void Show()
{
var rootGrid = GetCurrentPageGrid();
var rowsCount = rootGrid.RowDefinitions.Count;
if (rowsCount > 1)
{
Grid.SetRowSpan(this, rowsCount);
}
rootGrid.Children.Add(this);
}
public void Close()
{
var rootGrid = (Grid)Parent;
rootGrid.Children.Remove(this);
}
private static Grid GetCurrentPageGrid()
{
var shellView = (ShellView)Application.Current.MainPage;
var contentPage = (ContentPage)shellView.CurrentPage;
if (contentPage.Content is Grid grid) { return grid; }
var actualPanel = contentPage.Content;
for (int i = 0; i < 10; i++)
{
var children = actualPanel.LogicalChildren;
var childGrid = children.OfType<Grid>().FirstOrDefault();
if (childGrid != null) { return childGrid; }
actualPanel = children.OfType<View>().FirstOrDefault();
}
throw new ArgumentException("No Grid panel could identified to place the info box!");
}
}

Adding content on a custom content page

Ok, so I am trying to add a searchbar in the toolbar of my page.
The Search bar appears correctly in the toolbar and I can catch the on text changed event.
I created a new Xaml and cs page and changed content page to 'MySearchContentPage'
I Tried to add a grid and label on my new page created but nothing will show except for the searchbar. I added this just to see if I can get anything to display.
Am I adding it in the right place ? Or how do you add content to this page ?
I have done this by doing the following:
MySearchContentPage Class:
public class MySearchContentPage : ContentPage, ISearchPage
{
public MySearchContentPage()
{
SearchBarTextChanged += HandleSearchBarTextChanged;
}
public event EventHandler<string> SearchBarTextChanged;
public void OnSearchBarTextChanged(string text) => SearchBarTextChanged?.Invoke(this, text);
void HandleSearchBarTextChanged(object sender, string searchBarText)
{
//Logic to handle updated search bar text
}
}
ISearchPage:
public interface ISearchPage
{
void OnSearchBarTextChanged(string text);
event EventHandler<string> SearchBarTextChanged;
}
iOS renderer page:
public class MySearchContentPageRenderer : PageRenderer, IUISearchResultsUpdating
{
readonly UISearchController searchController;
bool _isFirstAppearing = true;
public override void WillMoveToParentViewController(UIViewController parent)
{
base.WillMoveToParentViewController(parent);
var searchController = new UISearchController(searchResultsController: null)
{
SearchResultsUpdater = this,
DimsBackgroundDuringPresentation = false,
HidesNavigationBarDuringPresentation = true,
HidesBottomBarWhenPushed = true
};
searchController.SearchBar.Placeholder = "Search Symptoms";
parent.NavigationItem.SearchController = searchController;
DefinesPresentationContext = true;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
//Work-around to ensure the SearchController appears when the page first appears https://stackoverflow.com/a/46313164/5953643
if (_isFirstAppearing)
{
ParentViewController.NavigationItem.SearchController.Active = true;
ParentViewController.NavigationItem.SearchController.Active = false;
_isFirstAppearing = false;
}
}
public void UpdateSearchResultsForSearchController(UISearchController searchController)
{
if (Element is ISearchPage searchPage)
searchPage.OnSearchBarTextChanged(searchController.SearchBar.Text);
}
public MySearchContentPageRenderer()
{
var searchControllerr = new UISearchController(searchResultsController: null)
{
SearchResultsUpdater = this,
DimsBackgroundDuringPresentation = false,
HidesNavigationBarDuringPresentation = false,
HidesBottomBarWhenPushed = true
};
searchControllerr.SearchBar.Placeholder = string.Empty;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
UINavigationBar.Appearance.TitleTextAttributes = new UIStringAttributes
{
ForegroundColor = UIColor.Red
};
}
public override void ViewDidLoad()
{
// base.ViewDidLoad();
// NavigationController.NavigationBar.PrefersLargeTitles = true;
// NavigationController.NavigationBar.BackgroundColor = UIColor.Red;
// var searchController = new UISearchController(searchResultsController: null);
// searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Default;
// searchController.SearchBar.BackgroundColor = UIColor.Green;
// NavigationItem.SearchController = searchController;
// NavigationItem.HidesSearchBarWhenScrolling = false;
//searchController.SearchBar.SizeToFit();
//searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Prominent;
////NavigationController.TabBarController
//this.sea
//NavigationController.TabBarController.NavigationItem.HidesSearchBarWhenScrolling = true;
//NavigationController.TabBarController.NavigationItem.SearchController = searchController;
//this.Title = "Search";
}
}
So far the outcome is this :
I can't seem to get anything else to add to this page. Can anyone explain why?
AddSymptomNew.xaml page:
<?xml version="1.0" encoding="UTF-8"?>
<visiblegyapp:MySearchContentPage
xmlns:visiblegyapp="clr-namespace:VisiblegyApp"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VisiblegyApp.AddSymptomNew"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.LargeTitleDisplay="Always"
Title="Search Symptoms"
BackgroundColor="{DynamicResource BasePageColor}"
>
<ScrollView
x:Name="outerScrollView"
Padding="0"
>
<Grid
x:Name="layeringGrid"
RowSpacing="0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test label" TextColor="Red" Grid.Row="1"/>
</Grid>
</ScrollView>
The cause is ContentPage is inheritable while XAML is not inheritable.
I would recommend you to use a custom contentview and add this contentView to MySearchContentPage .
For example, create a custom contentView here:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
And in Xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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"
x:Class="App132.AddSymptomNewView">
<ContentView.Content>
<ScrollView
x:Name="outerScrollView"
Padding="0">
<Grid
x:Name="layeringGrid"
RowSpacing="0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test label" TextColor="Red" Grid.Row="1"/>
</Grid>
</ScrollView>
</ContentView.Content>
</ContentView>
And use it in the MySearchContentPage :
<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:app132="clr-namespace:App132"
mc:Ignorable="d"
x:Class="App132.MainPage">
<app132:AddSymptomNewView/>
</ContentPage>

Cannot display ListView data when creating PDF using PdfSharp.Xamarin.Forms

I am new to xamarin and I am using PdfSharp.Xamarin.Forms nuget to create a PDF in Xamarin forms for both Android and iOS. Problem is I cannot render ListView. They have mentioned about it, and need to write a renderer for it. But I have no idea how to create and bind it.
This is how I did it.
<Grid x:Name="mainGrid">
<ScrollView>
<StackLayout Padding="4" Orientation="Vertical">
<!--<Image HeightRequest="80" Source="logojpeg.jpg" Margin="0,0,0,5"/>-->
<Label FontSize="18" TextColor="Black" FontFamily="{StaticResource timesNewRomanBold}" HorizontalOptions="CenterAndExpand" Text="Monthly Motor Renew List of Jayasekara (900585) as at January, 2020"/>
<Label FontSize="18" TextColor="Black" FontFamily="{StaticResource timesNewRomanBold}" HorizontalOptions="CenterAndExpand" Text="Report generated on 27 December, 2019" Margin="0,0,0,5"/>
<ListView x:Name="renewListView"
Footer=""
pdf:PdfRendererAttributes.ListRendererDelegate="{StaticResource PDFSampleListRendererDelegate}"
BackgroundColor="White"
SeparatorVisibility="None"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="false">
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ScrollView>
</Grid>
In code behind.
public partial class MotorRenewalFinalPrint : ContentPage
{
public MotorRenewalFinalPrint()
{
InitializeComponent();
}
public MotorRenewalFinalPrint (List<MotorRenewalPrintData> newdd)
{
InitializeComponent ();
Title = "Save as PDF";
renewListView.ItemsSource = newdd;
}
private void pdf_Clicked(object sender, EventArgs e)
{
var pdf = PDFManager.GeneratePDFFromView(mainGrid);
var fileManager = DependencyService.Get<IFileIO>();
string filePath = Path.Combine(fileManager.GetMyDocumentsPath(), "formpdf.pdf");
DependencyService.Get<IPdfSave>().Save(pdf, filePath);
DependencyService.Get<IPDFPreviewProvider>().TriggerPreview(filePath);
}
}
Updated...
MainClass
public partial class MainPage : ContentPage
{
private List<Customer> Cus = new List<Customer>();
public MainPage()
{
InitializeComponent();
Customer ss1 = new Customer { Names = "test1", Ages = "10"};
Customer ss2 = new Customer { Names = "test2", Ages = "30" };
Cus.Add(ss1);
Cus.Add(ss2);
//rListView.ItemsSource = Cus;
}
private void Button_Clicked(object sender, System.EventArgs e)
{
var pdf = PDFManager.GeneratePDFFromView(mainGrid);
var fileManager = DependencyService.Get<IFileIO>();
string filePath = Path.Combine(fileManager.GetMyDocumentsPath(), "testpdf.pdf");
DependencyService.Get<IPdfSave>().Save(pdf, filePath);
DependencyService.Get<IPDFPreviewProvider>().TriggerPreview(filePath);
}
}
view
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestPDF"
xmlns:pdf="clr-namespace:PdfSharp.Xamarin.Forms;assembly=PdfSharp.Xamarin.Forms"
x:Class="TestPDF.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:PDFSampleListRendererDelegate x:Key="PDFSampleListRendererDelegate" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Grid x:Name="mainGrid">
<ScrollView>
<StackLayout Margin="0,0,0,5">
<Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="CenterAndExpand" TextColor="Black" FontSize="18" VerticalOptions="Center" />
<ListView pdf:PdfRendererAttributes.ListRendererDelegate="{DynamicResource PDFSampleListRendererDelegate}" HeightRequest="150"/>
<Button Text="click" Clicked="Button_Clicked"/>
</StackLayout>
</ScrollView>
</Grid>
</ContentPage.Content>
</ContentPage>
PDFSampleListRendererDelegate
public class PDFSampleListRendererDelegate : PdfListViewRendererDelegate
{
public override void DrawCell(ListView listView, int section, int row, XGraphics page, XRect bounds, double scaleFactor)
{
XFont font = new XFont("times" ?? GlobalFontSettings.FontResolver.DefaultFontName, 15);
var yourObject = (listView.ItemsSource as List<Customer>).ElementAt(row);
page.DrawString(yourObject.Names, font, XBrushes.Black, bounds,
new XStringFormat
{
LineAlignment = XLineAlignment.Center,
Alignment = XStringAlignment.Center,
});
}
public override void DrawFooter(ListView listView, int section, XGraphics page, XRect bounds, double scaleFactor)
{
base.DrawFooter(listView, section, page, bounds, scaleFactor);
}
public override double GetFooterHeight(ListView listView, int section)
{
return base.GetFooterHeight(listView, section);
}
}
you should override DrawCell method
i.e:
public override void DrawCell(ListView listView, int section, int row, XGraphics page, XRect bounds, double scaleFactor)
{
XFont font = new XFont(yourCustomFont ?? GlobalFontSettings.FontResolver.DefaultFontName, label.FontSize * scaleFactor);
var yourObject = (listView.ItemSource as List<YourObjType>).ElementAt(row);
page.DrawString(yourObject.Text, font, XColors.Black, bounds,
new XStringFormat {
LineAlignment = XLineAlignment.Center,
Alignment = XStringAlignment.Center,
});
}

Binding CarouselPage content pages to view model

I am trying to use a CarouselPage in Xamarin Forms.
<?xml version="1.0" encoding="utf-8" ?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:views="clr-namespace:TestForms.Views;assembly=TestForms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="TestForms.Views.Photos" ItemsSource="{Binding Pages}">
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage >
<StackLayout VerticalOptions="StartAndExpand" Padding="50">
<Label Text="ContentPage"></Label>
<Label Text="{Binding Title}"></Label>
<Label Text="{Binding Description}"></Label>
</StackLayout>
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
</CarouselPage>
In the view model I have
List<ContentPage> ContentPages = new List<ContentPage>();
foreach (var photo in Photos)
{
var page = new ContentPage();
page.BindingContext = new PhotoDetailViewModel(photo);
ContentPages.Add(page);
}
Pages = new ObservableCollection<ContentPage>(ContentPages);
When I render this, I get a list of pages for all the photos. but I can't seem to bind the title or description in the individual page.
What am I missing here?
You have your CarouselPage wired up correctly
Just need to change your view model slightly.
I'm assuming your Title and Description Properties are in your PhotoDetailViewModel?
if so the binding you are creating in your CarouselPage is not working because it is binded to the List of ContentPage, which wouldn't have the properties "Title" and "Description"
in your CarouselPage your have already set up an ItemsSource binding which should automatically set the BindingContext of your child pages in your CarouselPage. So you dont need to do it manually.
So instead create an ObservableCollection of PhotoDetailViewModel in your ViewModel and bind the ItemsSource of your CarouselPage to that.
So Remove:
List<ContentPage> ContentPages = new List<ContentPage>();
foreach (var photo in Photos)
{
var page = new ContentPage();
page.BindingContext = new PhotoDetailViewModel(photo);
ContentPages.Add(page);
}
Pages = new ObservableCollection<ContentPage>(ContentPages);
And add:
Pages = new ObservableCollection<PhotoDetailViewModel>(Photos.Select(p => new PhotoDetailViewModel(p));
Make sure to change the Property Type of Pages to ObservableCollection<PhotoDetailViewModel>
And that should be all you need to change
As I said you should use ContentView instead of ContentPage. Below is working example
public partial class AnotherCarouselPage : ContentPage
{
public class Zoo
{
public string ImageUrl { get; set; }
public string Name { get; set; }
}
public ObservableCollection<Zoo> Zoos { get; set; }
public AnotherCarouselPage()
{
Zoos = new ObservableCollection<Zoo>
{
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/23c1dd13-333a-459e-9e23-c3784e7cb434/2016-06-02_1049.png",
Name = "Woodland Park Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/6b60d27e-c1ec-4fe6-bebe-7386d545bb62/2016-06-02_1051.png",
Name = "Cleveland Zoo"
},
new Zoo
{
ImageUrl = "http://content.screencast.com/users/JamesMontemagno/folders/Jing/media/e8179889-8189-4acb-bac5-812611199a03/2016-06-02_1053.png",
Name = "Phoenix Zoo"
}
};
InitializeComponent();
carousel.ItemsSource = Zoos;
carousel.PositionSelected += Carousel_PositionSelected;
carousel.ItemSelected += Carousel_ItemSelected;
}
private void Carousel_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
}
private void Carousel_PositionSelected(object sender, SelectedPositionChangedEventArgs e)
{
}
}
page xml
<?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:control="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
x:Class="ButtonRendererDemo.AnotherCarouselPage"
x:Name="devicePage"
BackgroundColor="Gray">
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<control:CarouselView x:Name="carousel" >
<control:CarouselView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}"/>
<Image Source="{Binding ImageUrl}"/>
</StackLayout>
</DataTemplate>
</control:CarouselView.ItemTemplate>
</control:CarouselView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

Resources