Adding content on a custom content page - xamarin

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>

Related

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,
});
}

Xamarin ItemTemplate with a WebView control

I am trying to add a WebView control inside a ItemTemplate and set the height of the row. I know that I can't have the webview control scroll so I need to setting the height to the correct size to display the full html content. I have created an IValueConverter class that I was thinking can return the correct height needed but what height value to return depending on how long the content is?
Anyway I can load the webview and get the height needed to display the full content I get -1 for height in my writeline?
XAML Code
<telerikListView:ListViewTemplateCell>
<Grid BackgroundColor="{StaticResource LightBlueColor}"
Padding="10">
<telerikPrimitives:RadBorder Padding="10"
HorizontalOptions="Fill"
BorderThickness="2"
BorderColor="{StaticResource DarkBlueColor}"
BackgroundColor="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!--<RowDefinition Height="*"/>-->
<RowDefinition Height="{Binding AssetItem.Description, Converter={StaticResource DescriptionToHeightConverter}}" />
</Grid.RowDefinitions>
<!--<Grid Grid.Row="0" Grid.Column="0">
<HtmlLabelControl:HtmlLabel
Text="{Binding AssetItem.Description}"
HeightRequest="100"/>-->
<WebView HeightRequest="800" MinimumHeightRequest="300" HorizontalOptions="FillAndExpand">
<WebView.Source>
<HtmlWebViewSource Html="{Binding AssetItem.Description}"/>
</WebView.Source>
</WebView>
<!--</Grid>-->
<!--<WebView Grid.Column="0" Grid.Row="0" HeightRequest="200" HorizontalOptions="FillAndExpand">
<WebView.Source>
<HtmlWebViewSource Html="{Binding AssetItem.Description}"/>
</WebView.Source>
</WebView>-->
<!--<Label Text="{Binding AssetItem.Description}"
TextColor="{StaticResource GrayTextColor}"
Grid.Row="0"
Grid.Column="0"/>-->
<!--Star-->
<telerikPrimitives:RadPath
x:Name="path"
Grid.Row="0"
Grid.Column="1"
WidthRequest="40"
HeightRequest="35"
StrokeThickness="2"
VerticalOptions="Start"
Fill="{Binding AssetItem.IsBookmark, Converter={StaticResource FavFillColorConverter}}"
Stroke="#3e7dc5"
Geometry="{x:Static telerikInput:Geometries.Star}">
<telerikPrimitives:RadPath.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="BookmarkCommand" CommandParameter="{Binding AssetItem.AssetId}" />
</telerikPrimitives:RadPath.GestureRecognizers>
</telerikPrimitives:RadPath>
</Grid>
<!--</Grid>-->
</telerikPrimitives:RadBorder>
</Grid>
</telerikListView:ListViewTemplateCell>
CS Converter Logic
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var description = value as string;
//WebView wv = new WebView();
//wv.Source = description;
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = description;
WebView webView = new WebView()
{
Source = HtmlSource
};
Debug.WriteLine($"Web View Height: {webView.Height}");
if (!string.IsNullOrEmpty(description))
{
if (description.Length == 300)
{
return 50;
}
}
return 300;
}
Test code
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = "<html><body><div><h1>MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM</h1></div></body></html>";
WebView webView = new WebView()
{
Source = HtmlSource
};
string htmlheight = "";
Task.Run(async () => {
try
{
htmlheight = await webView.EvaluateJavaScriptAsync("document.body.scrollHeight");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
//WebView_NavigatedAsync(webView);
Debug.WriteLine($"Web View Height: {htmlheight}");
If you want to get the height of the html .You can implement by using Custom Renderer
in Forms
public MainPage()
{
InitializeComponent();
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = #"<html><body>
<h1>Xamarin.Forms</h1>
<p>Welcome to WebView.</p>
</body></html>";
Webview webView = new Webview()
{
WidthRequest = 100,
HeightRequest = 20,
Source =HtmlSource
};
MessagingCenter.Subscribe<Object, float>(this,"webview_loaded",(sender,value)=>{
Console.WriteLine(value); //value is the height of html
});
Content = new StackLayout
{
Children =
{
webView,
},
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions=LayoutOptions.FillAndExpand
};
}
in iOS project
using Foundation;
using UIKit;
using CoreGraphics;
using xxx;
using xxx.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:ExportRenderer(typeof(WebView),typeof(MyWebViewRenderer))]
namespace App7.iOS
{
public class MyWebViewRenderer:WebViewRenderer,IUIWebViewDelegate
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if(NativeView!=null)
{
// WeakDelegate = this;
}
}
[Export("webViewDidFinishLoad:")]
public void LoadingFinished(UIWebView webView)
{
string htmlHeight = webView.EvaluateJavascript("document.body.scrollHeight");
float height = float.Parse(htmlHeight);
MessagingCenter.Send<System.Object, float>(this, "webview_loaded", height);
}
}
}
in Android
using Android.Content;
using Android.Webkit;
using Android.Widget;
using xxx;
using xxx.Droid;
using Java.Lang;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(MyWebViewRenderer))]
namespace xxx.Droid
{
public class MyWebViewRenderer:WebViewRenderer
{
public MyWebViewRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Android.Webkit.WebView webview =(Android.Webkit.WebView) Control;
WebSettings settings = webview.Settings;
settings.JavaScriptEnabled = true;
webview.SetWebViewClient(new JavascriptWebViewClient());
}
}
}
public class JavascriptWebViewClient : WebViewClient
{
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript("javascript:document.body.scrollHeight;", new EvaluateBack() );
}
}
class EvaluateBack : Java.Lang.Object, IValueCallback
{
public void OnReceiveValue(Java.Lang.Object value)
{
string htmlHeight = value.ToString();
float height = float.Parse(htmlHeight);
MessagingCenter.Send<System.Object, float>(this,"webview_loaded",height);
}
}
}
Notes: in your test code ,you get call the method when the html didn't finish loading ,so the result is -1.

How to show Navigationbar 'TitleView' Common for all pages

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

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>

Xamarin button visible in iOS simulator but not on device

I'm new to Xamarin but know some iOS, got someone else's code dumped in my lap to fix some bugs. I've googled quite a bit, but I can't solve this one.
Everything works fine deployed on Android, deployed on iPhone 4/iOS 7.1.2, and simulated as iPhone 6/iOS 9.3 in the iOS simulator.
Problem
The btnNews button is visible in the iOS simulator (iPhone 6/iOS 9.3) but not when the code is deployed to an iPhone 6/iOS 9.3.1.
MainPage.xaml in a portable project
<?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:c="clr-namespace:Cookies;assembly=Cookies"
x:Class="MyCompany.MainPage" Title="MyCompany">
<Grid ColumnSpacing="1" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" x:Name ="buttonsRow"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<c:CookieWebView x:Name="webView" Grid.Row="0" Grid.ColumnSpan="2">
</c:CookieWebView>
<ActivityIndicator x:Name="activityIndicator"
VerticalOptions="FillAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.ColumnSpan="2" />
<Button x:Name="btnNews" Text="back to news list" BackgroundColor="#E15F59" BorderRadius="0" BorderColor="#E15F59"
TextColor="White" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BorderWidth="0" IsVisible="False"
HorizontalOptions="FillAndExpand"/>
</Grid>
</ContentPage>
MainPage.xaml.cs MainPage()
namespace MyCompany
{
public partial class MainPage : ContentPage
{
private Button _btnNews;
private ActivityIndicator Loader { get; set; }
private RowDefinition _row;
public const string URL_NEWS = "http://www.mycompany.com/";
public const string QUERY = "master=app";
public static CookieWebView WebView;
public static string CurrentUrl;
bool externalUrlClicked = false;
public MainPage(string url = null)
{
InitializeComponent();
// cookies common : https://github.com/seansparkman/CookiesWebView + nuget package CookiesWebView nuget : https://www.nuget.org/packages/CookieWebView/1.0.0
// ios: http://stackoverflow.com/questions/29768019/cookies-in-web-view-xamarin-ios
NavigationPage.SetHasNavigationBar(this, false);
WebView = this.FindByName<CookieWebView>("webView");
_row = this.FindByName<RowDefinition>("buttonsRow");
Loader = this.FindByName<ActivityIndicator>("activityIndicator");
WebView.Navigated += _webView_Navigated;
WebView.Navigating += WebView_Navigating;
var source = new UrlWebViewSource();
source.Url = GetUrl(!string.IsNullOrEmpty(url) ? url : URL_NEWS);
WebView.Source = source;
ReadCookiesFromSettings();
_btnNews = this.FindByName<Button>("btnNews");
_btnNews.Clicked += _btnNews_Clicked;
// Keep free from iPhone status bar
this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
}
}
}
MainPage.xaml.cs _webView_Navigated()
This method runs whenever a link is clicked in the web view. If the url is a "news item page" then btnNews ("Back to news list") should be shown, else btnNews should be hidden. I have set breakpoints to validate that the correct if/else blocks run at appropriate times. The deployed app uses the same urls/web content as the simulated app.
public void _webView_Navigated(object sender, CookieNavigatedEventArgs args)
{
if (externalUrlClicked)
{
args.Url = CurrentUrl;
externalUrlClicked = false;
return;
}
else
CurrentUrl = args.Url;
if (args.Url != GetUrl(URL_NEWS))
{
_row.Height = 50;
_btnNews.IsVisible = true;
}
else
{
_row.Height = 0;
_btnNews.IsVisible = false;
}
try
{
var cookie = WebView.Cookies[Settings.CookiesSettingsKey];
Settings.CookiesSettings = cookie.Value;
}
catch (Exception)
{
}
Loader.IsVisible = Loader.IsRunning = false;
}
In a native iOS app I would have started investigating layout and things like setNeedsDisplay, but I haven't yet found out how to do that with Xamarin.
Thanks for helping out!

Resources