I have a xamarin.forms app which uses Rg.Plugins.Popup. I have a list view with an "edit" icon on each view cell. I am opening the popup when we click on this icon. The problem is the popup will open at the center of screen no matter what where the listview cells position. How can we open the popup exactly at the position of listview cell which being clicked? Is it possible? Any help is appreciated.
For now, Rg.Plugins.Popup support the Animations for Right, Left, Center, Top, Bottom.
For more details of Animations, you could check the link below. https://github.com/rotorgames/Rg.Plugins.Popup/wiki/Animations#custom-animations
Applying a custom animations in a xaml file:
class UserAnimation : MoveAnimation
{
private double _defaultTranslationY;
public UserAnimation()
{
DurationIn = DurationOut = 300;
EasingIn = Easing.SinOut;
EasingOut = Easing.SinIn;
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;
}
public override void Preparing(View content, PopupPage page)
{
base.Preparing(content, page);
page.IsVisible = false;
if (content == null) return;
_defaultTranslationY = content.TranslationY;
}
public override void Disposing(View content, PopupPage page)
{
base.Disposing(content, page);
page.IsVisible = true;
if (content == null) return;
content.TranslationY = _defaultTranslationY;
}
public async override Task Appearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Appearing(content, page));
if (content != null)
{
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(content.Width, _defaultTranslationY, DurationIn, EasingIn));
};
page.IsVisible = true;
await Task.WhenAll(taskList);
}
public async override Task Disappearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Disappearing(content, page));
if (content != null)
{
_defaultTranslationY = content.TranslationX - content.Width;
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(leftOffset, _defaultTranslationY, DurationOut, EasingOut));
};
await Task.WhenAll(taskList);
}
}
Usage:
<pages:PopupPage.Animation>
<animations:UserAnimation />
</pages:PopupPage.Animation>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Frame BackgroundColor="Silver">
<StackLayout Spacing="20">
<Label
FontSize="16"
HorizontalOptions="Center"
Text="User Animation" />
<Button Clicked="OnClose" Text="Close" />
</StackLayout>
</Frame>
</StackLayout>
OnClose event:
private void OnClose(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
}
If you wannna Left, Top, Botton, Center for the Popup, you could change the MoveAnimationOptions of your custom animations.
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;
Related
Xamarin Form View Model can trigger the onTextChange Event for Searchbar but there is no Event handler for OnCancelButtonClicked.
What I want:
An Event should be Triggered whenever Cancel/Close Button is clicked as below.
You can get Searchbar CloseButton event in SearchBar custom render, but I think it is not useful for your goal.
I suggest you can click search icon to refresh data source. I don one sample using Searchbar and ListView, you can take a look:
[assembly: ExportRenderer(typeof(SearchBar), typeof(CustomSearchBarRenderer))]
namespace FormsSample.Droid
{
public class CustomSearchBarRenderer: SearchBarRenderer
{
public CustomSearchBarRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var searchView = Control;
searchView.Iconified = true;
searchView.SetIconifiedByDefault(false);
int searchCloseButtonId = Context.Resources.GetIdentifier("android:id/search_close_btn", null, null);
// search close button icon, you can add event for closeIcon.click.
var closeIcon = searchView.FindViewById(searchCloseButtonId);
int searchViewSearchButtonId = Control.Resources.GetIdentifier("android:id/search_mag_icon", null, null);
var searchIcon = searchView.FindViewById(searchViewSearchButtonId);
searchIcon.Click += SearchIcon_Click;
}
}
private void SearchIcon_Click(object sender, EventArgs e)
{
Element.OnSearchButtonPressed();
}
}
}
<StackLayout>
<SearchBar
x:Name="searchBar"
HorizontalOptions="Fill"
Placeholder="Search fruits..."
SearchButtonPressed="OnSearchButtonPressed"
VerticalOptions="CenterAndExpand" />
<Label
HorizontalOptions="Fill"
Text="Enter a search term and press enter or click the magnifying glass to perform a search."
VerticalOptions="CenterAndExpand" />
<ListView
x:Name="searchResults"
HorizontalOptions="Fill"
VerticalOptions="CenterAndExpand" />
</StackLayout>
public partial class Page23 : ContentPage
{
public Page23()
{
InitializeComponent();
searchResults.ItemsSource = DataService.Fruits;
}
private void OnSearchButtonPressed(object sender, EventArgs e)
{
if(string.IsNullOrEmpty(searchBar.Text))
{
searchResults.ItemsSource = DataService.Fruits;
}
else
{
searchResults.ItemsSource = DataService.GetSearchResults(searchBar.Text);
}
}
}
You can click search closebutton to move text in SearchBar firstly, then click SearchButton to refresh data for listView.
I have spent my recent hours on researching on how to make this possible
I have a bottom tabbed page with a home button. What I want to achieve is, when button is pressed for x seconds, a new page shall be opened. How should the custom renderer for the tabbed page look like?
I already have one custom renderer for the tabbed page that makes sure that the home page button is a floating action button. The custom renderer for the home page button:
[assembly: ExportRenderer(typeof(NavigationBar), typeof(CustomTabbedRenderer))]
namespace MysteryLocation.Droid
{
public class CustomTabbedRenderer : TabbedPageRenderer
{
Context context;
public CustomTabbedRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
if (e.OldElement == null && e.NewElement != null)
{
for (int i = 0; i <= this.ViewGroup.ChildCount - 1; i++)
{
var childView = this.ViewGroup.GetChildAt(i);
if (childView is ViewGroup viewGroup)
{
((ViewGroup)childView).SetClipChildren(false);
for (int j = 0; j <= viewGroup.ChildCount - 1; j++)
{
var childRelativeLayoutView = viewGroup.GetChildAt(j);
if (childRelativeLayoutView is BottomNavigationView bottomView)
{
FloatingActionButton button = new FloatingActionButton(context);
BottomNavigationView.LayoutParams parameters = new BottomNavigationView.LayoutParams(150, 150);
parameters.Gravity = GravityFlags.CenterHorizontal;
Drawable d = Resources.GetDrawable(Resource.Drawable.image);
button.SetScaleType(Android.Widget.ImageView.ScaleType.Center);
button.SetImageDrawable(d);
button.LayoutParameters = parameters;
bottomView.AddView(button);
}
}
}
}
}
}
}
}
==========
EDIT
==========
In my customtabbedrenderer I've added button.LongClick += Button_LongClick and button.Click += Button_Click with the methods
private void Button_LongClick(object sender, EventArgs e)
{
//open a page through pushasync
}
private void Button_Click(object sender, EventArgs e)
{
//navigate to the page as usual, as it does when there's no
"button.Click" specified in the custom renderer
}
Any ideas on how the code should look like in the given methods?
=========
EDIT 2
=========
I have solved on long click, a new page is being opened through
button.LongClick += (object sender, LongClickEventArgs args) =>
{
Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(new Pass()));
};
The app works both on click and on long click now. The remaining problem is, how can I allow the same button to navigate to the page that it is supposed to when only doing a simple click? It navigates to that page when there's no "button.Click += Button_Click" and "button.LongClick += Button_Click1". But as soon as I add a longclick event, the button stops working on click.
==========================
SOLVED
==========================
Added Element.CurrentPage = Element.Children[2] in the method Button_Click method, simple as that.
Longpress may be problematic in XF. For one of my applications I used a double tap:
<?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:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
xmlns:CustomCtrls ="clr-namespace:D4503.CustomControls"
xmlns:Views="clr-namespace:D4503.Views"
x:Class="D4503.Pages.MainPage"
BackgroundColor="{StaticResource ScreenBackColor}"
ios:Page.UseSafeArea="true">
<ContentPage.Content>
<StackLayout>
<Image Grid.Row="1" Grid.Column="0" x:Name="btnRed" Style="{StaticResource ColorButtonImage}" Source="stoplight_red.png"
Aspect="AspectFit">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="Red_Button_Tapped"/>
<TapGestureRecognizer Tapped="Red_Button_Tapped_Double" NumberOfTapsRequired="2" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Context of the problem:
I do have a StackLayout with a lot of entries. When the user taps on an entry I do want to show below the tapped entry an info box. This info box should visually be above the next entry (kind of like a tooltip). The entry can have a dynamic height.
What is my approach:
Using a RelativeLayout it should be possible to position views outside the bounds of the RelativeLayout which represents the entry.
Something like this:
<StackLayout>
<BoxView BackgroundColor="Green" HeightRequest="150" ></BoxView>
<RelativeLayout BackgroundColor="Yellow" x:Name="container">
<Label Text="This is the entry"></Label>
<BoxView BackgroundColor="Aqua"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=container, Property=Y, Factor=1, Constant=100}"></BoxView>
</RelativeLayout>
<BoxView BackgroundColor="Green" HeightRequest="150" ></BoxView>
</StackLayout>
In this sample code the green BoxView's are kind of the entries before and after the one I do want to show. This is the result:
This makes actually sense, as I've linked to the Y-Property of the container and added 100 using "Constant".
And this is what I do want to archive:
I want to have a StackLayout with multiple entries. Whenever I click on one of this entries (yellow) right below an info should appear (blue).
How do I have to specify the YConstraint on the BoxView (which should illustrate the info window) to archive my goal? Or am I on a wrong path and another solution fits better?
I write a demo about your needs, here is running GIF.
First of all, I create content view.
<ContentView.Content>
<RelativeLayout x:Name="container" BackgroundColor="Yellow">
<Entry Text="This is the entry" x:Name="MyEntry" Focused="MyEntry_Focused" Unfocused="MyEntry_Unfocused">
</Entry>
</RelativeLayout>
</ContentView.Content>
Here is background code about content view.
public partial class FloatEntry : ContentView
{
BoxView boxView;
public FloatEntry()
{
InitializeComponent();
boxView = new BoxView();
boxView.BackgroundColor = Color.Red;
boxView.WidthRequest = 200;
}
private void MyEntry_Focused(object sender, FocusEventArgs e)
{
container.Children.Add(boxView,Constraint.RelativeToView(MyEntry, (Parent, sibling) =>
{
return sibling.X + 100;
}), Constraint.RelativeToView(MyEntry, (parent, sibling) =>
{
return sibling.Y + 50;
}));
container.RaiseChild(boxView);
}
private void MyEntry_Unfocused(object sender, FocusEventArgs e)
{
container.Children.Remove(boxView);
}
}
}
But If you used this way to achieve it, you want to BoxView to cover the below Entry. You have to put the content view to a RelativeLayout as well.
<RelativeLayout x:Name="myRl">
<myentry:FloatEntry x:Name="myfloat" HorizontalOptions="StartAndExpand" HeightRequest="50" >
<myentry:FloatEntry.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</myentry:FloatEntry.GestureRecognizers>
</myentry:FloatEntry>
<myentry:FloatEntry HorizontalOptions="StartAndExpand" HeightRequest="50"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=myfloat, Property=Y, Factor=1, Constant=50}"
>
</myentry:FloatEntry>
</RelativeLayout>
Here is layout background code.
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
}
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
// I need to use following method to move the Boxview cover the blew Entry
myRl.RaiseChild(myfloat);
}
}
A more generic approach would be to write your own control which could be named as InfoBoxPopup (bascially a ContentPage) which you open manually once the Entry gets Focused and Close it on Unfocus.
Just be sure that you have on top of every page a grid panel defined.
In the InfoBox.xaml you define your custom style (panel, label, margins, IsInputTransparent?, etc. to show the custom text or other stuff)
public partial class InfoBoxPopup : ContentView
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(InfoBoxPopup));
public InfoBoxPopup()
{
InitializeComponent();
}
public string? Text
{
get => (string?)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public void Show()
{
var rootGrid = GetCurrentPageGrid();
var rowsCount = rootGrid.RowDefinitions.Count;
if (rowsCount > 1)
{
Grid.SetRowSpan(this, rowsCount);
}
rootGrid.Children.Add(this);
}
public void Close()
{
var rootGrid = (Grid)Parent;
rootGrid.Children.Remove(this);
}
private static Grid GetCurrentPageGrid()
{
var shellView = (ShellView)Application.Current.MainPage;
var contentPage = (ContentPage)shellView.CurrentPage;
if (contentPage.Content is Grid grid) { return grid; }
var actualPanel = contentPage.Content;
for (int i = 0; i < 10; i++)
{
var children = actualPanel.LogicalChildren;
var childGrid = children.OfType<Grid>().FirstOrDefault();
if (childGrid != null) { return childGrid; }
actualPanel = children.OfType<View>().FirstOrDefault();
}
throw new ArgumentException("No Grid panel could identified to place the info box!");
}
}
I installed the Nuget package Rg.plugins.popup.
Tried to set a popup page that should appear on right.
Tried different ways but not able find a solution
<pages:PopupPage.Animation>
<animations:MoveAnimation
PositionIn="Right"
PositionOut="Right"
DurationIn="300"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
Any solution with RG plugin?
It's not about the animation, that will only control the appearance. You should make sure the content of the popup page is properly arranged. For instance, here is the XAML for a popup page that will display a square popup in the top right corner.
<?xml version="1.0" encoding="UTF-8"?>
<pages:PopupPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
x:Class="MyApp.Views.RandomPopupPage">
<StackLayout
BackgroundColor="White"
HorizontalOptions="End"
VerticalOptions="Start"
Margin="20"
WidthRequest="100"
HeightRequest="100"
Spacing="0">
<Label
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
Text="Some text here" />
</StackLayout>
</pages:PopupPage>
It has a StackLayout vertically aligned with start (TOP) and horizontally aligned with end (right). It also has a fixed width and height. You should arrange its elements as you do with a normal page keeping in mind it has a transparent background.
It looks like this: See image
Tried to set a popup page that should appear on right.
From Rg.plugins.popup document , can custom animations as follow:
Creat UserAnimation class
class UserAnimation : MoveAnimation
{
private double _defaultTranslationY;
public UserAnimation()
{
DurationIn = DurationOut = 300;
EasingIn = Easing.SinOut;
EasingOut = Easing.SinIn;
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;
}
//1
public override void Preparing(View content, PopupPage page)
{
base.Preparing(content, page);
page.IsVisible = false;
if (content == null) return;
_defaultTranslationY = content.TranslationY;
}
//3
public override void Disposing(View content, PopupPage page)
{
base.Disposing(content, page);
page.IsVisible = true;
if (content == null) return;
content.TranslationY = _defaultTranslationY;
}
//2
public async override Task Appearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Appearing(content, page));
if (content != null)
{
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(content.Width, _defaultTranslationY, DurationIn, EasingIn));
};
page.IsVisible = true;
await Task.WhenAll(taskList);
}
//4
public async override Task Disappearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Disappearing(content, page));
if (content != null)
{
_defaultTranslationY = content.TranslationX - content.Width;
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(leftOffset, _defaultTranslationY, DurationOut, EasingOut));
};
await Task.WhenAll(taskList);
}
}
Use it in Xaml:
<pages:PopupPage.Animation>
<animations:UserAnimation/>
</pages:PopupPage.Animation>
Not sure if the effect below is what you want, but you can use this method to customize the animation.
I have implemented Radio button using custom rendering in Xamarin. The radio button is aligned left and text is aligned right by default. How can we change the position of radio button to the right?
Xaml Code is below
<StackLayout HorizontalOptions="Start" VerticalOptions="CenterAndExpand">
<control:BindableRadioGroup x:Name="SortPicker"
TextColor="Gray"
CheckedChanged="OnCheckedChanged"
Padding="50,0,0,10"
WidthRequest="1100"
Spacing="20">
</control:BindableRadioGroup>
</StackLayout>
Render Code:
protected override void OnElementChanged(ElementChangedEventArgs<CustomRadioButton> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var radButton = new RadioButton(Context);
colorStateList = radButton.TextColors;
radButton.CheckedChange += this.RadButtonCheckedChange;
SetNativeControl(radButton);
}
Control?.SetPadding(30, Control.PaddingTop, 0, Control.PaddingBottom);
Control.Text = e.NewElement.Text;
Control.Checked = e.NewElement.Checked;
UpdateTextColor();
if (e.NewElement.FontSize > 0)
{
Control.TextSize = (float)e.NewElement.FontSize;
}
if (!string.IsNullOrEmpty(e.NewElement.FontName))
{
Control.Typeface = TrySetFont(e.NewElement.FontName);
}
}
Try enabling supportsRtl property to true in your application manifest:
<application android:label="app label" android:supportsRtl="true"/>
and setting the Control's LayoutDirectionProperty like so:
Control.LayoutDirection = LayoutDirection.Rtl;