We have upgraded from Xamarin Forms 2.5 to 3.4.
The ProgressBar on Android becomes thicker and not controllable by XAML's HeightRequest, while it was before.
It is within a grid layout.
Any ideas how to control the height?
You can modify the height of the progressbar by using Render in xamarin.
CustomProgressBarRenderer.cs
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(App69.CustomProgressBar), typeof(App69.Droid.CustomProgressBarRenderer))]
namespace App69.Droid
{
public class CustomProgressBarRenderer : ProgressBarRenderer
{
public CustomProgressBarRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ProgressBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.ScaleY = 10; //Changes the height
}
}
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App69"
x:Class="App69.MainPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Top Left" Grid.Row="0" Grid.Column="0" />
<Label Text="Top Right" Grid.Row="0" Grid.Column="1" />
<Label Text="Bottom Left" Grid.Row="0" Grid.Column="1" />
<local:CustomProgressBar Progress="0.5" Grid.Row="1" Grid.Column="1" />
</Grid>
</ContentPage>
class CustomProgressBarRenderer : ProgressBarRenderer
{
/// <summary>
/// Raises the <see cref="E:ElementChanged" /> event.
/// </summary>
/// <param name="e">The <see cref="ElementChangedEventArgs{Xamarin.Forms.ProgressBar}"/> instance containing the event data.</param>
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ProgressBar> e)
{
base.OnElementChanged(e);
var element = Element as CustomProgressBar;
Control.IndeterminateDrawable.SetColorFilter(element.BarColor.ToAndroid(), PorterDuff.Mode.SrcIn);
Control.ProgressDrawable.SetColorFilter(element.BarColor.ToAndroid(), PorterDuff.Mode.SrcIn);
Control.ScaleY = element.BarHeight;
}
/// <summary>
/// Called when [element property changed].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
var element = Element as CustomProgressBar;
Control.IndeterminateDrawable.SetColorFilter(element.BarColor.ToAndroid(), PorterDuff.Mode.SrcIn);
Control.ProgressDrawable.SetColorFilter(element.BarColor.ToAndroid(), PorterDuff.Mode.SrcIn);
Control.ScaleY = element.BarHeight;
}
}
Related
I was faced with some weird behavior of TapGestureRecognizer. I have some simple Xamarin page like below
<StackLayout>
<Path
ClassId="BottomCone"
Fill="{AppThemeBinding Dark=#333333, Light=#444444}"
Data="{x:Static local:MainPage.BottomConeGeometry}"
BackgroundColor="Red"
>
<Path.GestureRecognizers>
<TapGestureRecognizer Tapped="BottomConeTapped" />
</Path.GestureRecognizers>
</Path>
</StackLayout>
Though it's actually just some simple closed shape drown with Path element on which I have some handler of tap event. And the problem is that event is firing every time when I'm clicking on Path or its background. Is it possible to not fire the tap event when the tap happened in the background of the path? And fire the event just in case the tap event happened inside a Path element.
So the shapes have overlapping "bounds" (the rectangle surrounding each shape).
One technique is to have a set of rectangles that approximates each of the shapes. The concept is to make the shapes themselves InputTransparent, and have an overlapping set of invisible boxes that capture touch.
OverlappingShapeButtonsPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XFSOAnswers.OverlappingShapeButtonsPage">
<ContentPage.Resources>
<Style TargetType="BoxView">
<!-- Uncomment, to see where OnTapped1 boxes are. -->
<!--<Setter Property="BackgroundColor" Value="Red"/>-->
</Style>
</ContentPage.Resources>
<ContentPage.Content>
<Grid>
<AbsoluteLayout InputTransparent="True">
<Polygon Points="0,0 80,0 0,60" Fill="Green" />
<Polygon Points="80,0 80,60 0,60" Fill="Pink" />
</AbsoluteLayout>
<Grid RowDefinitions="20,20,20" ColumnDefinitions="20,20,20,20"
RowSpacing="0" ColumnSpacing="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped2" />
</Grid.GestureRecognizers>
<BoxView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
<BoxView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped1" />
</BoxView.GestureRecognizers>
</BoxView>
<BoxView Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<BoxView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped1" />
</BoxView.GestureRecognizers>
</BoxView>
<BoxView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="1">
<BoxView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapped1" />
</BoxView.GestureRecognizers>
</BoxView>
</Grid>
</Grid>
</ContentPage.Content>
</ContentPage>
OverlappingShapeButtonsPage.xaml.cs:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace XFSOAnswers
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class OverlappingShapeButtonsPage : ContentPage
{
public OverlappingShapeButtonsPage()
{
InitializeComponent();
}
/// <summary>
/// This is called when any of the BoxViews are touched.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnTapped1(object sender, EventArgs e)
{
}
/// <summary>
/// This is called when the grid is touched anywhere that is not inside one of the BoxViews.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnTapped2(object sender, EventArgs e)
{
}
}
}
What user sees:
Red showing boxes for OnTapped1, approximating Green triangle:
This works reasonably well, because user will tend to touch near the center of the shape they are interested in.
You could refine this by doing simple (x,y) math calculations that approximate the edge between two shapes.
In General, Make sure that you aren't expecting unrealistic precision from user's touch. Primarily, that means that the touch areas should not be too small (more than one in a small area).
Consider these (or similar) guidelines: Optimal Size and Spacing for Mobile Touch.
I want to take photo using Xamarin form custom renderer.
I use Custom Renderer Sample and I add a Button named 'btnTakePicture' in Xaml , but I don't have any idea how to take photo on Button click event.
I want to show camera in part of screen.
Also I checked Xam.Media.Plugin and I couldn't take photo.
Xaml Code
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
x:Class="CustomRenderer.frmCamera"
Title="Main Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="7*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Label Text="Camera Preview"/>
</Grid>
<Grid Grid.Row="1">
<local:CameraPreview Camera="Rear" x:Name="cmrPreview"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" Background="black"/>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button x:Name="btnChangeCamera" Text="Switch Camera" Clicked="btnChangeCamera_Clicked"/>
<Button x:Name="btnTakePicture" Text="Take" Grid.Column="1" Clicked="btnTakePicture_Clicked"/>
</Grid>
<Frame></Frame>
</Grid>
</ContentPage>
C# Code
using System.IO;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace CustomRenderer
{
public partial class frmCamera : ContentPage
{
public frmCamera()
{
InitializeComponent();
}
private void btnChangeCamera_Clicked(object sender, System.EventArgs e)
{
if (cmrPreview.Camera == CameraOptions.Rear)
cmrPreview.Camera = CameraOptions.Front;
else
cmrPreview.Camera = CameraOptions.Rear;
}
bool blnIsFlashLight = false;
private void btnFlashLight_Clicked(object sender, System.EventArgs e)
{
if (blnIsFlashLight)
{
Flashlight.TurnOffAsync();
blnIsFlashLight = false;
}
else
{
Flashlight.TurnOnAsync();
blnIsFlashLight = true;
}
}
private void btnTakePhoto_Clicked(object sender, System.EventArgs e)
{
}
}
}
here is my project Screenshot
i have a page with Cards and once i tap on one i need to get detailed data. I have Gesture recognizer so i am able to navigate to he second page but with no details.
I have tried to add TapGesture in xaml but it didnt work.
This is my xaml
<grial:Repeater
Padding="0"
ItemsSource="{ Binding ListOfBestSellers }"
Orientation="Horizontal"
HeightRequest="200"
Spacing="0"
VerticalOptions="End"
ScrollBarVisibility="Never">
<grial:Repeater.ItemTemplate>
<DataTemplate >
<local:AvatArticleBrowserCardItemTemplate
Padding="10,0,5,5"
WidthRequest="150" >
</local:AvatArticleBrowserCardItemTemplate>
</DataTemplate>
</grial:Repeater.ItemTemplate>
</grial:Repeater>
and Template
<ContentView.Content>
<grial:CardView
VerticalOptions="Fill"
CornerRadius="5" x:Name="articleCard">
<grial:CardView.RowDefinitions>
<RowDefinition
Height="*" />
<RowDefinition
Height="Auto" />
</grial:CardView.RowDefinitions>
<!-- ARTICLE IMAGE -->
<ffimageloading:CachedImage
Grid.Row="0"
Margin="-14,0,-14,10"
Source="{ Binding BackgroundImage }"
Aspect="AspectFill" />
<Grid
Grid.Row="1"
VerticalOptions="End"
RowSpacing="10"
Padding="14,10">
<!-- TITLE -->
<Label
Grid.Row="0"
Text="{ Binding Title }"
FontSize="15"
Style="{ StaticResource LabelBoldStyle }"
TextColor="{ DynamicResource AccentColor }" />
<!-- CATEGORY -->
<Label
Grid.Row="1"
Text="{ Binding Section }"
FontSize="12"
VerticalOptions="Start" />
<!-- DATE -->
<Label
Grid.Row="1"
Text="{ Binding When }"
FontSize="12"
VerticalOptions="Start"
HorizontalOptions="End" />
</Grid>
</grial:CardView>
</ContentView.Content>
This is what i have
async void GoToCardDetail(object sender, EventArgs e)
{
if (LangUpLoggedUser.LoggedIn)
{
await Navigation.PushAsync(new MyArticle());
}
else
{
await Navigation.PushAsync(new Login());
}
}
private void CardDetail()
{
articleCard.GestureRecognizers.Clear();
TapGestureRecognizer gestureRecognizerArticleDetail = new TapGestureRecognizer();
gestureRecognizerArticleDetail.Tapped += GoToCardDetail;
articleCard.GestureRecognizers.Add(gestureRecognizerArticleDetail);
}
But its not giving me detailed data, on my list view from other page i have this
private async void ListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
#if !NAVIGATION
var selectedItem = ((ListView) sender).SelectedItem;
var articlePage = new MyArticle(selectedItem as ArticleDetailData);
await Navigation.PushAsync(articlePage);
#endif
}
my MyArticle const
public MyArticles()
{
InitializeComponent();
BindingContext = new MyArticlesListViewModel(null);
}
/// <summary>
///
/// </summary>
/// <param name="articles"></param>
/// <param name="categoryName"></param>
public MyArticles(List<Article> articles, string categoryName)
{
_categoryName = categoryName;
InitializeComponent();
BindingContext = new MyArticlesListViewModel(articles);
}
And that works perfectly can zou please help me how to achieve the same result?
you need to pass the selected item to MyArticle's constructor
var card = (CardView) sender;
var item = (ArticleDetailData)card.BindingContext;
await Navigation.PushAsync(new MyArticle(item));
Hi tried to compile my app for the first time for IOS but I got this:
System.NullReferenceException: Object reference not set to an instance of an object
in the AppDelegate.
It stops in this line :return base.FinishedLaunching(app, options);
I'm relative new into xamarin so I am sorry for my unknowingness.
Here is my complete AppDelegate:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Globalization;
using System.IO;
using Flex;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.Xml.Linq;
namespace dpsApp.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Rg.Plugins.Popup.Popup.Init();
FlexButton.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}
}
So here is my MainPage XAML:
`
<StackLayout WidthRequest="10000">
<StackLayout x:Name="LogoStack" BackgroundColor="White">
<Image x:Name="Image"
Source="a.png"
HeightRequest="120"
WidthRequest="120"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="EasterCount"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
<StackLayout x:Name="StackList" IsVisible="True" HeightRequest="3000">
<ListView x:Name="PageList"
HasUnevenRows="True"
ItemTapped="Link_ItemTapped"
HeightRequest="25"
BackgroundColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Margin="0,0,0,0" BackgroundColor="#e7e7e7" HeightRequest="65" Padding="0">
<Grid x:Name="DeleteStack" VerticalOptions="CenterAndExpand" BackgroundColor="White" HorizontalOptions="FillAndExpand" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="22" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="{Binding Title}" LineBreakMode="TailTruncation" FontSize="25" Margin="20,0,0,0"/>
<Label Grid.Column="0" Grid.Row="1" Text="{Binding Link}" LineBreakMode="TailTruncation" FontSize="17" Margin="20,0,0,0"/>
<Image Margin="0,0,20,0"
IsVisible="{Binding IsVisible}"
Grid.Column="1"
Grid.Row="0"
Grid.RowSpan="2"
x:Name="DeleteButton"
Source="delete.png"
VerticalOptions="Center"
HeightRequest="20"
HorizontalOptions="Center"/>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout x:Name="FirstTimeOpenStack" HorizontalOptions="Center">
<Label Text="Please tap on the plus icon in the top right corner to add a website" />
</StackLayout>
</StackLayout>
Your App Delegate looks ok. You have an unhandled exception somewhere in the App() you are launching.
Here is one way to capture unhandled exceptions:
namespace WiFiVisualPager.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
global::Xamarin.Forms.Forms.Init();
DisplayCrashReport();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
#region [Error handling]
//Credit: Peter Norman.
//https://peterno.wordpress.com/2015/04/15/unhandled-exception-handling-in-ios-and-android-with-xamarin/
//Minor compile fixes by David McCurley.
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
LogUnhandledException(newExc);
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
LogUnhandledException(newExc);
}
internal static void LogUnhandledException(Exception exception)
{
try
{
const string errorFileName = "Fatal.log";
var libraryPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Resources); // iOS: Environment.SpecialFolder.Resources
var errorFilePath = Path.Combine(libraryPath, errorFileName);
var errorMessage = String.Format("Time: {0}\r\nError: Unhandled Exception\r\n{1}",
DateTime.Now, exception.ToString());
File.WriteAllText(errorFilePath, errorMessage);
// Log to Android Device Logging.
//Android.Util.Log.Error("Crash Report", errorMessage);
}
catch
{
// just suppress any error logging exceptions
}
}
/// <summary>
// If there is an unhandled exception, the exception information is diplayed
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private static void DisplayCrashReport()
{
const string errorFilename = "Fatal.log";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Resources);
var errorFilePath = Path.Combine(libraryPath, errorFilename);
if (!File.Exists(errorFilePath))
{
return;
}
var errorText = File.ReadAllText(errorFilePath);
var alertView = new UIAlertView("Crash Report", errorText, null, "Close", "Clear") { UserInteractionEnabled = true };
alertView.Clicked += (sender, args) =>
{
if (args.ButtonIndex != 0)
{
File.Delete(errorFilePath);
}
};
alertView.Show();
}
#endregion
}
}
I have this (copied from the examples)...
<?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:local="clr-namespace:MyApp" x:Class="MyApp.MainPage" xmlns:telerikInput="clr-namespace:Telerik.XamarinForms.Input;assembly=Telerik.XamarinForms.Input">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout Padding="5">
<Label Text="Select a view mode" />
<Picker x:Name="viewModePicker" />
</StackLayout>
<telerikInput:RadCalendar x:Name="calendar" NativeControlLoaded="CalendarLoaded" Grid.Row="1"/>
</Grid>
</ContentPage>
And...
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
calendar.DisplayDate = new DateTime(2017, 4, 12);
viewModePicker.ItemsSource = Enum.GetValues(typeof(CalendarViewMode));
viewModePicker.SelectedItem = CalendarViewMode.Day;
viewModePicker.SelectedIndexChanged += ViewModeChanged;
}
private void ViewModeChanged(object sender, EventArgs e)
{
calendar.TrySetViewMode((CalendarViewMode)viewModePicker.SelectedItem);
}
private void CalendarLoaded(object sender, System.EventArgs e)
{
calendar.TrySetViewMode((CalendarViewMode)viewModePicker.SelectedItem);
}
}
Everything works, including the calendar instance existing, except I can't see it! I'm using an iOS app.
This rendered fine for me:
StartPage.xaml.cs
using System;
using Telerik.XamarinForms.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TelerikXamarinApp1
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StartPage : ContentPage
{
public StartPage()
{
InitializeComponent();
calendar.DisplayDate = new DateTime(2017, 4, 12);
viewModePicker.ItemsSource = Enum.GetValues(typeof(CalendarViewMode));
viewModePicker.SelectedItem = CalendarViewMode.Day;
viewModePicker.SelectedIndexChanged += ViewModeChanged;
}
private void ViewModeChanged(object sender, EventArgs e)
{
calendar.TrySetViewMode((CalendarViewMode)viewModePicker.SelectedItem);
}
private void CalendarLoaded(object sender, EventArgs e)
{
calendar.TrySetViewMode((CalendarViewMode)viewModePicker.SelectedItem);
}
}
}
StartPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:telerikInput="clr-namespace:Telerik.XamarinForms.Input;assembly=Telerik.XamarinForms.Input"
x:Class="TelerikXamarinApp1.StartPage">
<ContentView.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout Padding="5">
<Label Text="Select a view mode" />
<Picker x:Name="viewModePicker" />
</StackLayout>
<telerikInput:RadCalendar x:Name="calendar" NativeControlLoaded="CalendarLoaded" Grid.Row="1" />
</Grid>
</ContentView.Content>
</ContentPage>
I reinstalled the DLLs from the Telerik Nuget server. Must have missed something, and I didn't realise that one can log on to their server using one's regular Telerik website credentials.
This resulted in the calendar appearing on my iPhone but not in the simulator. I copied my solution over to Mac OS and used Visual Studio Mac to build and deploy and now the calendar appears in the simulator too.
Also, the build and deploy time seems about twice as fast. Was using Windows on Parallels before, though.