Why are the borders on my BoxViews round? - xaml

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))]

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

Problems with the interface when rotating the device horizontally

The interface of my app is divided into a header (it is a webview) and absolute content, which among other things contains a second webview.
In vertical orientation it works and looks perfect.
But when I turn the device horizontally the header stops measuring 70 and measures something like 30 ....
The header reduces the height although it has a fixed height.
Why this happen? Any solution?
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:PruebaHeight"
x:Class="PruebaHeight.MainPage">
<ContentPage.Content>
<StackLayout Spacing="0">
<WebView x:Name="webview_header" HorizontalOptions="FillAndExpand" HeightRequest="70" BackgroundColor="Red"/>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Black">
<!-- other things -->
<ScrollView AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Green">
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="0" BackgroundColor="Purple">
<WebView x:Name="webview" AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Blue" />
</AbsoluteLayout>
</ScrollView>
</AbsoluteLayout>
</StackLayout>
</ContentPage.Content>
You could try to custom a view like following code.
XamWebView.cs
public class XamWebView: WebView
{
}
Then use it in your code.
var webview= new XamWebView();
webview.HorizontalOptions = LayoutOptions.Fill;
webview.VerticalOptions = LayoutOptions.StartAndExpand;
webview.WidthRequest = 70;
webview.HeightRequest = 70;
webview.Source = "https://www.google.com/";
Creating the Custom Renderer on Android(you could get the value of heightrequest when you running)
using WebView = Android.Webkit.WebView;
[assembly: ExportRenderer (typeof(XamWebView), typeof(XamWebViewRenderer))]
namespace Core.Droid
{
public class XamWebViewRenderer : WebViewRenderer
{
static XamWebView _xwebView = null;
WebView _webView;
public XamWebViewRenderer(Context context) : base(context)
{
}
class XamWebViewClient : Android.Webkit.WebViewClient
{
public override async void OnPageFinished(WebView view, string url)
{
if (_xwebView != null)
{
int i = 10;
while (view.ContentHeight == 0 && i-- > 0)
await System.Threading.Tasks.Task.Delay(100);
_xwebView.HeightRequest = view.ContentHeight;
}
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
_xwebView = e.NewElement as XamWebView;
_webView = Control;
if (e.OldElement == null)
{
_webView.SetWebViewClient(new XamWebViewClient());
}
}
}
}

MVVM binding cutom property to view model

I am new in MVVVM so please forgive me if it is stupid question. I am using this example http://www.codeproject.com/Articles/36848/WPF-Image-Pixel-Color-Picker-Element and included there library to get color of indicated by user pixel of image. it looks nice and dipsalys in rectangle selected color but i neeed to bind the selecteed value to viewmodel.
here is my xaml code:
<Window x:Class="MovieEditor.View.PixelSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrls="clr-namespace:ColorPickerControls;assembly=ColorPickerControls"
xmlns:local="clr-namespace:MovieEditor.MVVMCommon"
Title="FilterDesigner" Height="550" Width="550"
Icon="..\Resources\Images\icon.ico"
xmlns:VM="clr-namespace:MovieEditor.ViewModel">
<Window.DataContext>
<VM:PixelSelectorVM/>
</Window.DataContext>
<Window.Resources>
<local:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
</Window.Resources>
<Grid Background="#FF191919" >
<DockPanel>
<Grid Margin="10,10,10,1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto" MinHeight="38"/>
</Grid.RowDefinitions>
<Border BorderBrush="White" BorderThickness="5" Margin="0,39,0,11">
<ctrls:ImageColorPicker Binding.XmlNamespaceManager="{Binding p PixelSelectorVM.MyImageColorPicker, UpdateSourceTrigger=PropertyChanged}"
x:Name="image" Source ="{Binding Frame}" Margin="0,36,0,0"
/>
</Border>
<Border Width="77"
HorizontalAlignment="Center"
BorderBrush="White" BorderThickness="1" Margin="263,2,182,435">
<Rectangle Fill="{Binding ElementName=image, Path=SelectedColor,
Converter={StaticResource ColorToBrushConverter}}" RenderTransformOrigin="0.549,0.429" Margin="1"/>
</Border>
<Button Content="Save" Command="{Binding Save}" Margin="165,0,0,4" Grid.Row="1" HorizontalAlignment="Left" Width="60"/>
<Label Content="Selected pixel color:" HorizontalAlignment="Left" Height="18" Margin="140,11,0,0" VerticalAlignment="Top" Width="110"/>
<Button Content="Cancel" Command="{Binding Cancel}" Margin="0,1,165,4" HorizontalAlignment="Right" Width="60" RenderTransformOrigin="0.5,0.5" Grid.Row="1">
</Button>
</Grid>
</DockPanel>
</Grid>
</Window>
</code>
And here is my view model:
public class PixelSelectorVM : ViewModelBase
{
private BitmapImage frame;
public MainWindowVM parentMainWindowVM;
private ImageColorPicker imageColorPicker;
public ImageColorPicker MyImageColorPicker
{
get
{
return this.imageColorPicker;
}
set
{
this.imageColorPicker = value;
OnPropertyChanged("MyImageColorPicker");
}
}
public BitmapImage Frame
{
get
{
return this.frame;
}
set
{
this.frame = value;
OnPropertyChanged("Frame");
}
}
public PixelSelectorVM(BitmapImage image, MainWindowVM mainWindowVM)
{
this.frame = image;
this.parentMainWindowVM = mainWindowVM;
this.imageColorPicker = new ImageColorPicker();
this.imageColorPicker.Source = image;
}
public PixelSelectorVM() { }
public ICommand Save
{
get
{
return new RelayCommand(SaveExecute);
}
}
public ICommand Cancel
{
get
{
return new RelayCommand(CancelExecute);
}
}
private void SaveExecute()
{
}
private void CancelExecute()
{
}
}
Please suggest me solution how can i pass the selected color to view model
You should be able to bind ImageColorPicker's SelectedColor to ViewModel's Property.
So in XAML add the binding:
SelectedColor="{Binding MySelectedColor, Mode=TwoWay}"
And in VM add the MySelectedColor property:
private Color selectedColor;
public Color MySelectedColor
{
get
{
return this.selectedColor;
}
set
{
this.selectedColor = value;
OnPropertyChanged("MySelectedColor");
}
}
When control's SelectedColor changes, it should automatically update the MySelectedColor in your VM.

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