Animate label on focus and unfocus of entry xamarin.forms - xaml

I'm trying to create something like this, so I wrote this code:
This is the 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="alphatrial.MainPage"
BackgroundColor="#ffc40c"
xmlns:local="clr-namespace:alphatrial.Effects">
<ContentPage.Effects>
<local:StatusBarEffect
BackgroundColor="#ffc40c"/>
</ContentPage.Effects>
<StackLayout >
<Label Text="ISC - Alpha" TextColor="Black" FontSize="Large" FontAttributes="Bold" FontFamily="MyAwesomeCustomFont" HorizontalOptions="End" Margin="0,50,50,-14" />
<Line Stroke="black" X1="0" X2="340" StrokeThickness="3" />
<Label Text="App" TextColor="White" FontSize="Large" FontAttributes="Bold" FontFamily="MyAwesomeCustomFont" HorizontalOptions="End" Margin="0,-12,15,0"/>
<Frame Margin="0,80,0,0" BackgroundColor="Transparent">
<StackLayout >
<Label Text="Email" TextColor="Black" FontSize="Small" Margin="25,0,20,0" x:Name="emailAnimate" BindingContext="email" />
<Entry Margin="20,-40,20,0" x:Name="email"/>
</StackLayout>
</Frame>
<Frame Margin="0,-10,0,0" BackgroundColor="Transparent">
<StackLayout >
<Label Text="Password" TextColor="Black" FontSize="Small" Margin="25,0,20,0" x:Name="PassAnimate" />
<Entry Margin="20,-40,20,0" x:Name="password" IsPassword="True"/>
</StackLayout>
</Frame>
</StackLayout>
</ContentPage>
And this is the xaml.cs file:
public MainPage()
{
InitializeComponent();
email.Focused += Email_Focused;
email.Unfocused += Email_Unfocused;
password.Focused += Password_Focused;
password.Unfocused += Password_Unfocused;
}
private void Password_Unfocused(object sender, FocusEventArgs e)
{
if (password.Text == "")
{
PassAnimate.TranslateTo(0, 0, 100);
PassAnimate.ScaleTo(1, 100);
}
}
private void Password_Focused(object sender, FocusEventArgs e)
{
if (password.IsFocused)
{
PassAnimate.ScaleTo(0.8, 150);
PassAnimate.TranslateTo(-25, -30, 150);
}
}
private void Email_Unfocused(object sender, FocusEventArgs e)
{
if(email.Text=="")
{
emailAnimate.TranslateTo(0, 0, 100);
emailAnimate.ScaleTo(1, 100);
}
}
private void Email_Focused(object sender, FocusEventArgs e)
{
if (email.IsFocused)
{
emailAnimate.ScaleTo(0.8,150);
emailAnimate.TranslateTo(-25, -30,150);
}
}
}
}
But I obtained this.
The unfocus function doesn't work when I remove the focus from the entry. However, when I write something in the entry, then clear it and remove the focus, the animation works fine. What is the problem?
Update:
when i removed the code that checks if entry is empty:
if(email.Text=="")
the animation worked. it seems that this is not the correct way to check if entry is empty. would you please give me an idea about how to know of entry is empty?

okay i figured it out, i have to check if the entry is empty using the following:
if (string.IsNullOrEmpty(email.Text) )

Related

Code Behind Xamarin forms launch a function in another page

I have 2 ContentView in MyWordPage.Xaml which are MyWordListView and AddWordsView
My MyWordPage.Xaml looks like this :
<ContentView x:Name="MyWordListView" >
<CollectionView x:Name="ListOfWords" IsVisible="False"
SelectionMode="Single" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout >
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentView>
<ContentView IsVisible="False" x:Name="AddWordsView" />
<pv:PancakeView HorizontalOptions="End" VerticalOptions="End" Margin="0,0,10,150" Padding="10" CornerRadius="10">
<Image HeightRequest="30" WidthRequest="30" Aspect="AspectFit" />
<pv:PancakeView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChangeViewButton" />
</pv:PancakeView.GestureRecognizers>
</pv:PancakeView>
<Grid/>
If MyWordListView is visible, AddWordsView is not visible and vise versa.
The App starts with MyWordListPage visible and MyWordPage.xaml.cs looks like this:
public MyWordPage()
{
InitializeComponent();
// My second content view AddWordsView take content form a content page
// this content page name is MyAddWordPage
AddWordsView.Content = new MyAddWordPage().Content;
//My list of words
ListOfWords.ItemsSource = new List<string>()
{
"New York",
"London",
"Mumbai",
"Chicago"
};
}
//I navigate between the 2 View With an Overlay Button that make
//each one of View visible thanks to a boolean
bool ViewChange=false;
void OnChangeViewButton(System.Object sender, System.EventArgs e)
{
if (ViewChange==false)
{
AddWordsView.IsVisible=true;
MyWordListView.IsVisible=false;
ViewChange=!ViewChange;
}
else
{
AddWordsView.IsVisible=false;
MyWordListView.IsVisible=true;
ViewChange=!ViewChange;
}
}
OnUpdateMyList()
{
// Here I do things to refresh my list
}
MyAddWordPage.xaml.cs looks like this :
public MyWordPage()
{
InitializeComponent();
}
void OnInsertWord(System.Object sender, System.EventArgs e)
{
}
What I would like to do :
In MyAddWordPage.xaml.cs when clicking on a button to launch the function OnInsertWord() I would like to launch the function OnUpdateMyList() in MyWordPage.xaml.cs in order to refresh My collectionView in MyWordListView
Thanks for your help
According to your requirement, you can achieve this by overriding OnAppearing method.
The OnAppearing method is executed after the ContentPage is laid out, but just before it becomes visible.So, you can rebind the list to the collectionview in this method. Therefore, this is a good place to set the content of Xamarin.Forms views.
Here is the code in MyWordPage.xaml.cs:
public MainPage()
{
InitializeComponent();
// My second content view AddWordsView take content form a content page
// this content page name is MyAddWordPage
AddWordsView.Content = new MyAddWordPage().Content;
//My list of words
}
//Create a list
List<string> list = new List<string>()
{
"New York",
"London",
"Mumbai",
"Chicago"
};
protected override void OnAppearing()
{
base.OnAppearing();
ListOfWords.ItemsSource = list;
}
//I navigate between the 2 View With an Overlay Button that make
//each one of View visible thanks to a boolean
bool ViewChange = false;
void OnChangeViewButton(System.Object sender, System.EventArgs e)
{
if (ViewChange == false)
{
AddWordsView.IsVisible = true;
MyWordListView.IsVisible = false;
ViewChange = !ViewChange;
}
else
{
AddWordsView.IsVisible = false;
MyWordListView.IsVisible = true;
ViewChange = !ViewChange;
}
}
}
Here is the code in MyWordPage.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="App5.MainPage">
<StackLayout>
<StackLayout x:Name="MyWordListView">
<CollectionView x:Name="ListOfWords" SelectionMode="Single" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout >
<Label Text="{Binding .}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
<ContentView IsVisible="False" x:Name="AddWordsView" >
<Label Text="Here is the text."></Label>
</ContentView>
<StackLayout>
<Button Clicked="OnChangeViewButton"></Button>
</StackLayout>
</StackLayout>
</ContentPage>

adjust and move the content of a page up slightly when the keyboard appears in an Entry control Xamarin Forms

I have a registration form, when the user is entering data such as Email, below that Entry control there is a Label that appears if there is an error in the input. So my problem is that the virtual keyboard hides the Label showing input errors and I don't want that to happen.
With keyboard.jpg without keyboard.jpg
It will be that there will be some way to move the content of the form a little higher so that the Control Entry can be seen along with the Error Label
<StackLayout>
<Entry
Keyboard="Email"
MaxLength="30"
Placeholder="Enter Email"
ReturnType="Next"
Style="{StaticResource BorderlessEntryStyle}"
Text="{Binding Email.Value}">
<Entry.Behaviors>
<behaviorsValidate:EventToCommandBehavior Command="{Binding ValidateEmailCommand}" EventName="TextChanged" />
</Entry.Behaviors>
</Entry>
<Label
Margin="4,-4,0,0"
FontSize="12"
IsVisible="{Binding Email.IsValid, Converter={StaticResource InverseBoolConverter}}"
Style="{StaticResource SimpleLabelStyle}"
Text="{Binding Email.Errors, Converter={StaticResource FirstValidationErrorConverter}}"
TextColor="{DynamicResource Red}"
VerticalOptions="FillAndExpand" />
</StackLayout>
About adjusting elements when keyboard shows in Xamarin Forms, find one way to do this.
On android you just need to add your elements inside a Grid and use the platform specific UseWindowSoftInputModeAdjust Resize in the Application XAML.
firstly, create a new class that extend from Grid in Shared code.
public class KeyboardView: Grid
{
}
Then adding your control inside it.
<views:KeyboardView Padding="0,60,0,0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="60" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Source="ic_test"
HeightRequest="80"
WidthRequest="80"
HorizontalOptions="CenterAndExpand"
Grid.Row="0"/>
<Label Text="Login"
FontAttributes="Bold"
TextColor="CornflowerBlue"
HorizontalOptions="CenterAndExpand"
FontSize="25"
VerticalOptions="Center"
Margin="0,20,0,0"
Grid.Row="1"
x:Name="welcomeText"/>
<Entry Placeholder="Email"
Grid.Row="2"
Margin="20,0"
x:Name="email"
ReturnType="Done"
Keyboard="Email"/>
<Entry Placeholder="Password"
Margin="20,0"
Grid.Row="3"
HeightRequest="50"
x:Name="password"
ReturnType="Done"
IsPassword="true"/>
<Button VerticalOptions="EndAndExpand"
BackgroundColor="CornflowerBlue"
HeightRequest="60"
TextColor="White"
CornerRadius="0"
Grid.Row="4"
Text="Login"/>
</views:KeyboardView>
Thirdly, add platform specific UseWindowSoftInputModeAdjust with Resize value on the Application XAML
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="KeyboardSample.App"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:Application.WindowSoftInputModeAdjust="Resize">
On iOS we have to create a custom renderer to do the resize. Don't test on ios device.
[assembly: ExportRenderer(typeof(KeyboardView), typeof(KeyboardViewRenderer))]
namespace KeyboardSample.iOS.Renderers
{
public class KeyboardViewRenderer : ViewRenderer
{
NSObject _keyboardShowObserver;
NSObject _keyboardHideObserver;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
RegisterForKeyboardNotifications();
}
if (e.OldElement != null)
{
UnregisterForKeyboardNotifications();
}
}
void RegisterForKeyboardNotifications()
{
if (_keyboardShowObserver == null)
_keyboardShowObserver = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
if (_keyboardHideObserver == null)
_keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
}
void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
{
NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
CGSize keyboardSize = result.RectangleFValue.Size;
if (Element != null)
{
Element.Margin = new Thickness(0, 0, 0, keyboardSize.Height); //push the entry up to keyboard height when keyboard is activated
}
}
void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
{
if (Element != null)
{
Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
}
}
void UnregisterForKeyboardNotifications()
{
if (_keyboardShowObserver != null)
{
_keyboardShowObserver.Dispose();
_keyboardShowObserver = null;
}
if (_keyboardHideObserver != null)
{
_keyboardHideObserver.Dispose();
_keyboardHideObserver = null;
}
}
}
}

Xamarin forms Add button in TabbedPage

I have a question. I created the following TabbedPage:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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:views="clr-namespace:MyApp.Views"
mc:Ignorable="d"
x:Class="MyApp.Views.MainPage"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
BarBackgroundColor="White"
BarTextColor="Black"
android:TabbedPage.BarItemColor="#B2B2B2"
android:TabbedPage.BarSelectedItemColor="#56D7A5"
android:TabbedPage.IsSwipePagingEnabled="False">
<TabbedPage.Children>
<NavigationPage Title="page1" IconImageSource="navbar_page1">
<x:Arguments>
<views:page1 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="page2" IconImageSource="navbar_page2">
<x:Arguments>
<views:page2 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="page3" IconImageSource="navbar_page3">
<x:Arguments>
<views:page3 NavigationPage.HasNavigationBar="False" />
</x:Arguments>
</NavigationPage>
</TabbedPage>
Now on every page I have added this custom FabMenu like this:
<c:FloatingMenu Margin="0, 0, 10, 10" BGColor="#56D7A5" OpenIcon="openFab_icon" CloseIcon="closeFab_icon"
AbsoluteLayout.LayoutBounds=".95,.95" AbsoluteLayout.LayoutFlags="PositionProportional">
<c:FloatingButton x:Name="btnAddHomework" BGColor="#59E1FF" IconSrc="add_homework_icon" OnClickCommand="{Binding btnAddHomeworkCommand}" />
<c:FloatingButton x:Name="btnAddDeadline" BGColor="#0FF1A0" IconSrc="add_deadline_icon"/>
<c:FloatingButton x:Name="btnAddTest" BGColor="#5988FF" IconSrc="add_test_icon"/>
</c:FloatingMenu>
The problem is that every page has his own FabMenu, so you see it dissapear and reappear on every page, so my question is: Is there some kind of root view that overlays all the tabs in the TabbedPage?
Please let me know how I do that!
Disclaimer
I came up with a way to create the effect wanted using only pure Xamarin.Forms. Read along and pay attention to the tricky parts of the solution.
Abstract
This solution is achieved implementing AbsoluteLayout, CarouselView, IndicatorView and DataTemplateSelector. Xamarin.Forms 4.8 is supposed in what follows. If a lower version is used, please take into account that features like CarouselView or IndicatorView could be in Preview status.
DataTemplateSelector, CarouselView and IndicatorView are used to simulate a TabbedPage, and AbsoluteLayout is used to provide the Overlay.
So, now with the solution:
Create your Views
Here you create a view for each of the pages you want. In this example i want my application to consist of two pages, so i create two views (code behind remains untouched):
View1.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="overlayTest.View1"
BackgroundColor="Black">
<ContentView.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms 1!"
TextColor="White"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentView.Content>
</ContentView>
View2.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"
x:Class="overlayTest.View2">
<ContentView.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms 2!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentView.Content>
</ContentView>
Create a DataTemplateSelector
This will be used by the CarouselView in order to select one view or the other depending on the current Position.
using System;
using Xamarin.Forms;
namespace overlayTest
{
class MyTemplateSelector : DataTemplateSelector
{
readonly DataTemplate view1, view2;
public MyTemplateSelector()
{
view1 = new DataTemplate(typeof(View1));
view2 = new DataTemplate(typeof(View2));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
String s = item.ToString();
if(s == "1")
{
return view1;
}
return view2;
}
}
}
Create your Main Page
Page1.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:t="clr-namespace:overlayTest"
x:Class="overlayTest.Page1">
<ContentPage.Resources>
<ResourceDictionary>
<t:MyTemplateSelector x:Key="templateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<AbsoluteLayout>
<StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
Padding="0"
Spacing="0">
<CarouselView ItemTemplate="{StaticResource templateSelector}"
IndicatorView="indicatorView">
<CarouselView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>1</x:String>
<x:String>2</x:String>
</x:Array>
</CarouselView.ItemsSource>
</CarouselView>
<IndicatorView x:Name="indicatorView">
<IndicatorView.IndicatorTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand">
<Frame Margin="10">
<Label/>
</Frame>
</StackLayout>
</DataTemplate>
</IndicatorView.IndicatorTemplate>
</IndicatorView>
</StackLayout>
<ContentView
IsVisible="True" VerticalOptions="Start"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
BackgroundColor="Transparent">
<Frame CornerRadius="10"
Margin="20"
VerticalOptions="StartAndExpand"
HorizontalOptions="CenterAndExpand" InputTransparent="False">
<StackLayout Padding="0">
<Label
FontSize="Medium"
TextColor="Black"/>
<StackLayout Orientation="Horizontal"
HorizontalOptions="CenterAndExpand">
<Label Text="I am floating here"/>
<Switch IsToggled="True" />
</StackLayout>
<Button Text="Save"
BackgroundColor="Accent"/>
</StackLayout>
</Frame>
</ContentView>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
And in the code behind we set the name of the tabs. Here please put attention in the fact that i am supposing an element tree of a StackLayout -> Frame -> Label. If you change the IndicatorTemplate, you will have to also modify this part of the code!
Page1.xaml.cs
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace overlayTest
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
indicatorView.PropertyChanged += (s, a) =>
{
if (a.PropertyName == IndicatorView.HeightProperty.PropertyName)
{
var indicators = indicatorView.IndicatorLayout.Children.ToList();
int counter = 0;
foreach(var indicator in indicators)
{
var indicatorBaseStack = (StackLayout)indicator;
var indicatorFrame = (Frame)indicatorBaseStack.Children[0];
var indicatorFrameLabel = (Label)indicatorFrame.Content;
indicatorFrameLabel.Text = counter == 0 ? "View1" : "View2";
counter++;
}
}
};
}
}
}
Finally set that Page to the MainPage property of App:
public App()
{
InitializeComponent();
MainPage = new Page1();
}
The final result looks like this:
As a workaround, you could set ToolbarItem of each ContentPage (or you can define a base ContentPage).
<ContentPage.ToolbarItems>
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Clicked="{Binding xx}"
Priority="0" />
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Priority="1" />
<ToolbarItem Text="Example Item"
IconImageSource="xxx.png"
Order="Secondary"
Priority="2" />
</ContentPage.ToolbarItems>
I recommend creating a BaseContentPage that includes a static FloatingButton. This allows every page to inherit from BaseContentPage and use the same FloatingButton.
Code
BaseContentPage
abstract class BaseContentPage : ContentPage
{
protected static Button Button { get; } = new Button { Text = $"This button was created at {DateTimeOffset.UtcNow}" }.Invoke(button => button.Clicked += HandleButtonClicked);
static async void HandleButtonClicked(object sender, EventArgs e) =>
await Application.Current.MainPage.DisplayAlert("Button Clicked", "This is the same button on both pages", "OK");
}
Example LabelPage
class LabelPage : BaseButtonPage
{
public LabelPage()
{
Title = "LabelPage";
Content = new StackLayout
{
Children =
{
new Label { Text = "Label Page" }.TextCenter().Center(),
Button
}
}
}
}
Example ButtonPage
class ButtonPage : BaseButtonPage
{
public ButtonPage()
{
Title = "ButtonPage";
Content = Button;
}
}
Example App
public class App : Application
{
public App()
{
Device.SetFlags(new[] { "Markup_Experimental" });
MainPage = new TabbedPage
{
Children =
{
new ButtonPage(),
new LabelPage()
}
};
}
}
Sample App
Here is the sample app used to create the attached GIF:
https://github.com/brminnick/TabbedPageButton/

Why are the borders on my BoxViews round?

I´m trying to create a separator between StackLayouts in a XAML page in Xamarin.Forms and I can do that easily with BoxView.
But when I try to add a border to the BoxView (by adding a Frame) I can´t get it to not curve at the ends.
I have tried all kinds of ways to make this work without luck. I even tried to stretch the BoxView out of the screen (take a look at the latter image) to have the curve off the screen (that would just do fine at this moment even)
Here is just few of of the things I tried out without any luck.
<!--Test 1. -->
<Frame OutlineColor="{DynamicResource CardOutlineColor}" HasShadow="False" Padding="0" VerticalOptions="FillAndExpand">
<BoxView x:Name="boxViewSeparator" HeightRequest="15" WidthRequest="10000" BackgroundColor="{DynamicResource WindowsBackgroundColor}" />
</Frame>
<!--Test 2. -->
<AbsoluteLayout VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Frame OutlineColor="{DynamicResource CardOutlineColor}" HasShadow="False" Padding="0" VerticalOptions="FillAndExpand">
<BoxView AbsoluteLayout.LayoutBounds="1.0, 1.0, 1.0, 1.0"
Color="#f2f3f3"
AbsoluteLayout.LayoutFlags="All" BackgroundColor="{DynamicResource WindowsBackgroundColor}" />
</Frame>
</AbsoluteLayout>
<!--Test3. -->
<Frame OutlineColor="{DynamicResource CardOutlineColor}" HasShadow="False" Padding="0" VerticalOptions="FillAndExpand">
<ContentView HeightRequest="15" BackgroundColor="{DynamicResource WindowsBackgroundColor}" />
</Frame>
<!--Test 4. -->
<Frame OutlineColor="{DynamicResource CardOutlineColor}" HasShadow="False" Padding="0" VerticalOptions="FillAndExpand">
<ContentView HeightRequest="15" BackgroundColor="{DynamicResource WindowsBackgroundColor}" MinimumWidthRequest="500"/>
</Frame>
I came the closest with this this code
<AbsoluteLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Frame OutlineColor="{DynamicResource CardOutlineColor}" HasShadow="False"
Padding="0" VerticalOptions="FillAndExpand"
AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="1.5, 1.5, 1.2, 1.0">
<BoxView HeightRequest="15" BackgroundColor="{DynamicResource WindowsBackgroundColor}" />
</Frame>
</AbsoluteLayout>
But the result is that I only get the other part of the ViewBox out of the window
Does anybody have any other idea?
Ok I found out how to do this and like everything you know, it´s easy :-)
Just use 3 BoxViews in a StackLayout without padding/spacing.
<StackLayout Orientation ="Vertical" Padding="0" Spacing="0">
<BoxView BackgroundColor="#d6dbdb" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
<BoxView HeightRequest="15" BackgroundColor="#f2f3f3"/>
<BoxView BackgroundColor="#d6dbdb" HeightRequest="1" VerticalOptions="End" HorizontalOptions="FillAndExpand"/>
</StackLayout>
And the result is the following.
p.s
I would like to see how you would off set the frame off the screen...
I've found more elegant solution(IMHO):
Create custom box view:
public class Border : BoxView
{
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(nameof(BorderColor),
typeof(Color),
typeof(Border),
Color.Transparent);
public static readonly BindableProperty BorderWidthProperty =
BindableProperty.Create(nameof(BorderWidth),
typeof(double),
typeof(Border),
0d);
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public double BorderWidth
{
get { return (double)GetValue(BorderWidthProperty); }
set { SetValue(BorderWidthProperty, value); }
}
}
Implement renderers:
ANDROID
public class BorderRenderer : VisualElementRenderer<BoxView>
{
public BorderRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
public override void Draw(Canvas canvas)
{
var border = Element as Border;
base.Draw(canvas);
var paint = new Paint();
paint.StrokeWidth = (float)border.BorderWidth;
paint.SetStyle(Paint.Style.Stroke);
paint.SetARGB(ConvertTo255ScaleColor(border.BorderColor.A), ConvertTo255ScaleColor(border.BorderColor.R), ConvertTo255ScaleColor(border.BorderColor.G), ConvertTo255ScaleColor(border.BorderColor.B));
SetLayerType(Android.Views.LayerType.Software, paint);
var number = (float)border.BorderWidth / 2;
var rectF = new RectF(
number, // left
number, // top
canvas.Width - number, // right
canvas.Height - number // bottom
);
canvas.DrawRoundRect(rectF, 0, 0, paint);
}
private int ConvertTo255ScaleColor(double color)
{
return (int)Math.Ceiling(color * 255);
}
}
iOS
public class BorderRenderer : VisualElementRenderer<BoxView>
{
public BorderRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
Layer.MasksToBounds = true;
Layer.CornerRadius = 0f;
}
public override void Draw(CGRect rect)
{
var border = (Border)Element;
using (var context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(border.Color.ToCGColor());
context.SetStrokeColor(border.BorderColor.ToCGColor());
context.SetLineWidth((float)border.BorderWidth);
var rCorner = Bounds.Inset((int)border.BorderWidth / 2, (int)border.BorderWidth / 2);
var radius = (nfloat)border.CornerRadius;
radius = (nfloat)Math.Max(0, Math.Min(radius, Math.Max(rCorner.Height / 2, rCorner.Width / 2)));
var path = CGPath.FromRoundedRect(rCorner, radius, radius);
context.AddPath(path);
context.DrawPath(CGPathDrawingMode.FillStroke);
}
}
}
Don't forget to add attribute above namespace on each renderer:
[assembly: ExportRenderer(typeof(Border), typeof(BorderRenderer))]

Xamrin Forms : Swipe to delete(gesture) in ListView

I want to implement the swipe to delete functionality in Xamrin Forms, for which i have tried the following.
Wrote a custom renderer for the list view and in the "OnElementChanged" of the renderer am able to access the binded command to the "CustomListView" and am able to add this command to the Swipe Gesture as added below.
swipeGestureRecognizer = new UISwipeGestureRecognizer (() => {
if (command == null) {
Console.WriteLine ("No command set");
return;}
command.Execute (null);
});
However i am having trouble in accessing the specific row(swiped row), so that i could make a button visible/hidden on the swiped row in the list view. Please could you recommend a way to implement the same?
Swipe to delete is now built into Xamarin Froms ListViews using a ContextAction. Here is the most basic tutorial of how to do it. It is very easy to implement.
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/listview/
You could do something like this:
protected override void OnElementChanged (ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged (e);
var swipeDelegate = new SwipeRecogniserDelegate ();
swipeGestureRecognizer = new UISwipeGestureRecognizer {
Direction = UISwipeGestureRecognizerDirection.Left,
Delegate = swipeDelegate
};
swipeGestureRecognizer.AddTarget (o => {
var startPoint = swipeDelegate.GetStartPoint ();
Console.WriteLine (startPoint);
var indexPath = this.Control.IndexPathForRowAtPoint(startPoint);
if(listView.SwipeCommand != null) {
listView.SwipeCommand.Execute(indexPath.Row);
}
});
this.Control.AddGestureRecognizer (swipeGestureRecognizer);
this.listView = (SwipableListView)this.Element;
}
The key is SwipeRecogniserDelegate. its implemented like so:
public class SwipeRecogniserDelegate : UIGestureRecognizerDelegate
{
PointF startPoint;
public override bool ShouldReceiveTouch (UIGestureRecognizer recognizer, UITouch touch)
{
return true;
}
public override bool ShouldBegin (UIGestureRecognizer recognizer)
{
var swipeGesture = ((UISwipeGestureRecognizer)recognizer);
this.startPoint = swipeGesture.LocationOfTouch (0, swipeGesture.View);
return true;
}
public PointF GetStartPoint ()
{
return startPoint;
}
}
I was able to accomplish this with the new Xamarin.Forms
SwipeView
Pass the current row into the CommandParameter, and use it in the event handler.
FYI: For some reason the SwipeView has a default BackgroundColor of white, which you can override with something else to match your theme.
Xaml:
<ListView Margin="-20,0,0,0" x:Name="photosListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" SeparatorColor="Gray" VerticalScrollBarVisibility="Default" HasUnevenRows="true" SeparatorVisibility="Default" Background="{StaticResource PrimaryDark}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<SwipeView BackgroundColor="{StaticResource PrimaryDark}" >
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Delete" BackgroundColor="LightPink" Clicked="OnDeleteRow" CommandParameter="{Binding .}" />
</SwipeItems>
</SwipeView.RightItems>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal">
<CheckBox IsVisible="{Binding SelectEnabled}" Color="{StaticResource White}" IsChecked="{Binding Selected}" Margin="20,0,-15,0" CheckedChanged="OnItemCheckedChanged" />
<Grid WidthRequest="70" HeightRequest="50">
<Grid.Margin>
<OnPlatform x:TypeArguments="Thickness" Android="15,0,0,0" iOS="10,0,0,0" />
</Grid.Margin>
<Image Aspect="AspectFill" Source="{Binding ThumbImageSource}" HorizontalOptions="Fill" />
</Grid>
</StackLayout>
<StackLayout Grid.Column="1" Spacing="0" Padding="0" Margin="0,5,0,0">
<Label Text="{Binding Photo.Description}" TextColor="{StaticResource TextColour}" FontSize="16" FontAttributes="Bold" />
<Label Text="{Binding DateTakenString}" TextColor="{StaticResource TextColour}" FontSize="14" />
</StackLayout>
</Grid>
</SwipeView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
cs:
public async void OnDeleteRow(object sender, EventArgs e)
{
if (await GetDeleteRowConfirmationFromUser())
{
SwipeItem si = sender as SwipeItem;
PhotoListItem itemToDelete = si.CommandParameter as PhotoListItem;
LocalDatabaseService db = new LocalDatabaseService();
db.DeletePhoto(itemToDelete.Photo);
_listItems.Remove(itemToDelete);
}
}