Xamarin Effect affecting other controls - c#

I am experiencing a weird bug where a Custom effect would affect other Controls as well while i am only targeting one specific control. I was able to reproduce the behavior in a small test project. What i am trying to achieve is to change the color of a specific entry control. It does work but when navigating back to the previous page the effect affects other controls as well while i only want the specific effect to affect a specific control. I also only add the effect to the specific Entry by using its Effects list from xaml.
(note i am using the standard forms navigation page for navigating defined in my app.xaml like this.
MainPage = new NavigationPage(new MainPage());
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App2"
x:Class="App2.MainPage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="Welcome to Xamarin.Forms!"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Entry></Entry>
<Button Clicked="Button_Clicked" Text="navigate"></Button>
<Button Clicked="Button_Clicked2" Text="navigate to other"></Button>
</StackLayout>
</ContentPage>
CodeBehind of MainPage.xaml
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
Application.Current.MainPage.Navigation.PushAsync(new Test());
}
private void Button_Clicked2(object sender, EventArgs e)
{
Application.Current.MainPage.Navigation.PushAsync(new Test2());
}
}
Test.Xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App2"
x:Class="App2.Test">
<ContentPage.Content>
<Entry>
<Entry.Effects>
<local:EntryLineColorEffect></local:EntryLineColorEffect>
</Entry.Effects>
</Entry>
</ContentPage.Content>
</ContentPage>
Test2.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App2.Test2">
<ContentPage.Content>
<StackLayout>
<Label Text="test 2" />
<Entry>
</Entry>
</StackLayout>
</ContentPage.Content>
</ContentPage>
EntryLineColorEffect (forms project)
public class EntryLineColorEffect : RoutingEffect
{
public EntryLineColorEffect() : base("Prolang.EntryLineColorEffect")
{
}
}
EntryLineColorEffect (Android project)
[assembly: ResolutionGroupName("Prolang")]
[assembly: ExportEffect(typeof(EntryLineColorEffect), "EntryLineColorEffect")]
namespace App2.Droid.Effects
{
class EntryLineColorEffect : PlatformEffect
{
EditText control;
protected override void OnAttached()
{
control = Control as EditText;
UpdateLineColor();
}
protected override void OnDetached()
{
control = null;
}
private void UpdateLineColor()
{
if (control != null)
{
control.Background.SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
}
}
}
}
some screenshots that make it more clear
The entry does get the Magenta color which is good
But when navigating back and then navigating to the Test2 page that entry is also getting the Magenta color which for me is undesired behavior (and i do not know why it is hapenning). So what should i do to avoid this behavior?

In the end someone on the Xamarin comunity forum was able to answer my question.
=>
Effect won't affect other controls which haven't attached that effect. It seems Background.SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop); changes the global Entry's underline's color. The entry displayed later after this setting will apply this behavior. So your Test2's Entry still has an attacted style. Use Background.Mutate().SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop); instead to correct that:
EditText control;
protected override void OnAttached()
{
control = Control as EditText;
UpdateLineColor();
}
protected override void OnDetached()
{
control = null;
}
private void UpdateLineColor()
{
if (control != null)
{
control.Background.Mutate().SetColorFilter(Color.DarkMagenta.ToAndroid(), Android.Graphics.PorterDuff.Mode.SrcAtop);
}
}

Related

How to improve loading performance in Tabbed Page xamarin forms

I am trying to create bottom tabbed page in Xamarin forms and i am doing this for Android.
here i am using latest version of Xamarin Forms.
My sample Bottom tabbed page like below.
<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:SampleTabbedPage.Views"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
android:TabbedPage.ToolbarPlacement="Bottom"
android:TabbedPage.BarItemColor="Gray"
android:TabbedPage.BarSelectedItemColor="Blue"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.SampleTabbed">
<!--Pages can be added as references or inline-->
<NavigationPage
Title="Main"
NavigationPage.HasNavigationBar="False">
<x:Arguments>
<views:SampleDetailsPage/>
</x:Arguments>
</NavigationPage>
<ContentPage Title="Tab 1" />
<ContentPage Title="Tab 2" />
</TabbedPage>
My sample details page like
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.SampleDetailsPage"
BackgroundColor="Gray">
<ContentPage.Content>
<StackLayout>
<ListView BackgroundColor="White" ItemTapped="ListView_ItemTapped">
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Mango</x:String>
<x:String>Banana</x:String>
<x:String>Pinaple</x:String>
<x:String>Apple</x:String>
<x:String>Avacado</x:String>
<x:String>Coconut</x:String>
<x:String>Dragan Fruit</x:String>
<x:String>Pomaganate</x:String>
<x:String>Wood Apple</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
And the navigation i am doing as follows
in App.xamal.cs
MainPage = new NavigationPage(new FirstPage());
I have simple first page
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SampleTabbedPage.Views.FirstPage">
<ContentPage.Content>
<StackLayout>
<Button Text="Click Me!" Clicked="Button_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
here in button click event i do the navigation to "Sample Tabbed Page"
await Navigation.PushAsync(new SampleTabbed());
This is working fine.
but there is a performance issue.
if i run this and click on button in First page it is taking 3 seconds to load tabbed page.
if i remove list view in sample detail page it take 2 seconds to load.
but if i add just a content page to click event it take only milliseconds to load.
Am i doing some thing wrong with implementing tabbed page?
or
is there any way to improve the loading performance with tabbed pages.
A solution is to make the heavy pages load their content in a lazy manner, only when their tab becomes selected. This way, since these pages are now empty when TabbedPage is created, navigating to the TabbedPage suddenly becomes very fast!
1.create a behavior for the TabbedPage page, called ActivePageTabbedPageBehavior.
class ActivePageTabbedPageBehavior : Behavior<TabbedPage>
{
protected override void OnAttachedTo(TabbedPage tabbedPage)
{
base.OnAttachedTo(tabbedPage);
tabbedPage.CurrentPageChanged += OnTabbedPageCurrentPageChanged;
}
protected override void OnDetachingFrom(TabbedPage tabbedPage)
{
base.OnDetachingFrom(tabbedPage);
tabbedPage.CurrentPageChanged -= OnTabbedPageCurrentPageChanged;
}
private void OnTabbedPageCurrentPageChanged(object sender, EventArgs e)
{
var tabbedPage = (TabbedPage)sender;
// Deactivate previously selected page
IActiveAware prevActiveAwarePage = tabbedPage.Children.OfType<IActiveAware>()
.FirstOrDefault(c => c.IsActive && tabbedPage.CurrentPage != c);
if (prevActiveAwarePage != null)
{
prevActiveAwarePage.IsActive = false;
}
// Activate selected page
if (tabbedPage.CurrentPage is IActiveAware activeAwarePage)
{
activeAwarePage.IsActive = true;
}
}
}
2.define IActiveAware interface
interface IActiveAware
{
bool IsActive { get; set; }
event EventHandler IsActiveChanged;
}
3.create a base generic abstract class called LoadContentOnActivateBehavior
abstract class LoadContentOnActivateBehavior<TActivateAwareElement> : Behavior<TActivateAwareElement>
where TActivateAwareElement : VisualElement
{
public DataTemplate ContentTemplate { get; set; }
protected override void OnAttachedTo(TActivateAwareElement element)
{
base.OnAttachedTo(element);
(element as IActiveAware).IsActiveChanged += OnIsActiveChanged;
}
protected override void OnDetachingFrom(TActivateAwareElement element)
{
(element as IActiveAware).IsActiveChanged -= OnIsActiveChanged;
base.OnDetachingFrom(element);
}
void OnIsActiveChanged(object sender, EventArgs e)
{
var element = (TActivateAwareElement)sender;
element.Behaviors.Remove(this);
SetContent(element, (View)ContentTemplate.CreateContent());
}
protected abstract void SetContent(TActivateAwareElement element, View contentView);
}
4.the specialized LazyContentPageBehavior
class LazyContentPageBehavior : LoadContentOnActivateBehavior<ContentView>
{
protected override void SetContent(ContentView element, View contentView)
{
element.Content = contentView;
}
}
then we can use in xaml like this:
<TabbedPage>
<TabbedPage.Behaviors>
<local:ActivePageTabbedPageBehavior />
</TabbedPage.Behaviors>
<ContentPage Title="First tab">
<Label Text="First tab layout" />
</ContentPage>
<local:LazyLoadedContentPage Title="Second tab">
<ContentPage.Behaviors>
<local:LazyContentPageBehavior ContentTemplate="{StaticResource ContentTemplate}" />
</ContentPage.Behaviors>
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ContentTemplate">
<!-- Complex and slow to render layout -->
<local:SlowContentView />
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
</local:LazyLoadedContentPage>
</TabbedPage>
we moved the ContentPage complex layout to become a DataTemplate.
Here's the custom LazyLoadedContentPage page which is activation aware:
class LazyLoadedContentPage : ContentPage, IActiveAware
{
public event EventHandler IsActiveChanged;
bool _isActive;
public bool IsActive
{
get => _isActive;
set
{
if (_isActive != value)
{
_isActive = value;
IsActiveChanged?.Invoke(this, EventArgs.Empty);
}
}
}
}
SlowContentView do some complex things
public partial class SlowContentView : ContentView
{
public SlowContentView()
{
InitializeComponent();
// Simulating a complex view
...
}
}

Xamarin: Method does not have correct signature

I am trying to create simple multi-screen App.
I added Content Page and button to it.
MapPage.xaml.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MapPage : ContentPage
{
public MapPage ()
{
InitializeComponent ();
}
void ShowMap(object s, EventArgs e)
{
}
}
MapPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App14"
x:Class="App14.MapPage">
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin Forms!" />
<Button Text="show map"
Clicked="ShowMap"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
I am getting error: ShowMap Method does not have correct signature
What I am doing wrong?
change the event handler from private (the default if not specified) to protected
protected void ShowMap(object s, EventArgs e)
{
}

How to get an image to pop up when a button is clicked in a xamarin forms app?

Hello I am working on a xamarin forms app the displays a map and when the user clicks a button the maps legend image pops up on top of the map. I am using this nuget package to get the popup effect: Rtorgames Popup package
the issue is that when I click the button the popup works but no legend image is showing up and the images are in the correct directories for both iOS and android.
heres my xaml code for the MapPage.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="AppName.Forms.Pages.Map">
<ContentView x:Name="contentView" ControlTemplate="{StaticResource MapNavBar}">
<StackLayout>
<Button Text = "Legand" HorizontalOptions = "End" VerticalOptions="End" Clicked="legend_OnClicked"></Button>
<Grid HorizontalOptions="FillAndExpand" >
<Image Source="Map.png" Scale="1.0" Aspect="AspectFit" />
</Grid>
</StackLayout>
</ContentView>
</ContentPage>
here's the code behind for MapPage.xaml:
using System;
using Rg.Plugins.Popup.Extensions;
using Rg.Plugins.Popup.Services;
using Xamarin.Forms;
namespace AppName.Forms.Pages
{
public partial class Map : ContentPage
{
public Map()
{
InitializeComponent();
}
private async void legend_OnClicked(object sender, EventArgs e)
{
var page = new LegendPage();
await Navigation.PushPopupAsync(page);
}
}
}
here's the legendPage xaml code:
<?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="AppName.Forms.Pages.LegendPage">
<StackLayout>
<Grid HorizontalOptions="FillAndExpand" >
<Image Source="legend.png" Scale="1.0" Aspect="AspectFit" />
</Grid>
</StackLayout>
</pages:PopupPage>
and finally here's the code behind to the legendPage.xaml:
using Xamarin.Forms;
using System;
using Rg.Plugins.Popup.Pages;
using System.Threading.Tasks;
namespace AppName.Forms.Pages
{
public partial class LegendPage : PopupPage
{
public LegendPage()
{
}
protected override void OnAppearing()
{
base.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
// Method for animation child in PopupPage
// Invoced after custom animation end
protected override bool OnBackButtonPressed()
{
// Prevent hide popup
//return base.OnBackButtonPressed();
return true;
}
// Invoced when background is clicked
protected override bool OnBackgroundClicked()
{
// Return default value - CloseWhenBackgroundIsClicked
return base.OnBackgroundClicked();
}
}
}
any help would be amazing!
Thanks in advance! :)

Xamarin MasterDetailPage button

How would I reference the Master Detail Page (MenuNavigation) by a clickable button inside of Homepage.xaml.cs and Homepage.xaml?
I want the side menu to appear whenever I click the "menu" button. (see below)
I have tried MasterDetail.isPresentedProperty inside the clicked button event handler in Homepage.xaml.cs and cannot figure it out.
Please help!
MenuNavigation.xaml:
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App2.MenuNavigation"
xmlns:local="clr-namespace:App2">
<!--this is the Menu Hamburger sidebar -->
<MasterDetailPage.Master>
<ContentPage Title="Menu" Padding="20" BackgroundColor="#4B1388" >
<StackLayout Orientation="Vertical">
<Label Text="Menu" TextColor="White" FontSize="Large" FontAttributes="Bold" HorizontalOptions="Center"/>
<Button Text="Home"
TextColor="White" BackgroundColor="#4B1388"
Clicked="Button_Clicked_Home"/> <!-- event handler points to MenuNavigation C# file when button is clicked-->
<Button Text="About"
TextColor="White" BackgroundColor="#4B1388"
Clicked="Button_Clicked_About"/>
<Button Text="Help"
TextColor="White" BackgroundColor="#4B1388"
Clicked="Button_Clicked_Help"/>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
<!--links to the detail pages from the hamburger sidebar -->
<MasterDetailPage.Detail>
</MasterDetailPage.Detail>
MenuNavigation.xaml.cs:
namespace App2
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MenuNavigation : MasterDetailPage
{
public MenuNavigation()
{
InitializeComponent();
//this is for no nav bar at top
var Homepage = new Homepage();
NavigationPage.SetHasNavigationBar(Homepage, false);
Detail = new NavigationPage(Homepage);
IsPresented = false; //this will make the side menu disappear when you select a page
}
private void Button_Clicked_Home(object sender, EventArgs e)
{
var Homepage = new Homepage();
NavigationPage.SetHasNavigationBar(Homepage, false);
Detail = new NavigationPage(Homepage);
IsPresented = false; //this will make the side menu disappear when you select a page
}
private void Button_Clicked_About(object sender, EventArgs e)
{
Detail = new NavigationPage(new About());
IsPresented = false;
}
private void Button_Clicked_Help(object sender, EventArgs e)
{
Detail = new NavigationPage(new Help());
IsPresented = false;
}
}
}
Homepage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App2.Homepage"
Title="Homepage">
<Button Text="Menu" Clicked="MasterDetailButton_Pressed"/>
</ContentPage>
Hompage.xaml.cs:
namespace App2
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Homepage : ContentPage
{
public Homepage()
{
InitializeComponent();
VideoListView.ItemsSource = Videos; //allows for binding
}
private void MasterDetailButton_Pressed(object sender, EventArgs e)
{
MasterDetailPage.IsPresentedProperty.Equals(true);
//open the master detail page when button is clicked.
//MasterDetailPage.IsPresentedProperty.Equals(true);
//MenuNavigation.IsPresentedProperty.Equals(true);
}
}
}
You should get a reference of your MasterDetailPage and change the IsPresented property of the reference. As the MasterDetailPage has to be the root of the app, something like this should work: (App.Current.MainPage as MasterDetailPage).IsPresented = true;.
Hope it helps! :)

Xamarin.Forms accessing controls written in markup from Code

Im trying to add some items to a Listview which i added using Xamarin.Forms markup in an xaml file.
The button can be accessed by hooking with the click event.But since the listview is empty i need the event like ondraw like in winforms, so that i can hook to it when it is drawn.
In the XAML file I have :
<?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="ButtonXaml.ButtonXamlPage">
<StackLayout>
<Button Text="Tap for click count!"
BorderWidth="10"
TextColor="Red"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />
<ListView
HorizontalOptions="Center"
/>
</StackLayout>
</ContentPage>
In the .cs file i have
using System;
using Xamarin.Forms;
namespace ButtonXaml
{
public partial class ButtonXamlPage
{
int count = 0;
public ButtonXamlPage()
{
InitializeComponent();
}
public void OnButtonClicked(object sender, EventArgs args)
{
((Button)sender).Text = "You clicked me";
}
}
}
So should i hook to events in Listview or can i do something like Resource.getElementbyID like we do in android
To access a Forms control in the code-behind, you need to assign it a name, using the x:Name attribute
in XAML:
<ListView HorizontalOptions="Center" x:Name="MyList" />
in code:
MyList.ItemsSource = myData;
There is a bug in Xamarin where VS doesn't see the defined x:Name
http://forums.xamarin.com/discussion/25409/problem-with-xaml-x-name-and-access-from-code-behind
Say you've defined an image in XAML:
<Image x:Name="myImageXName" />
Then this should work in code behind:
this.FindByName<Image>("myImageXName");
In my case the problem was lack of line XamlCompilation(XamlCompilationOptions.Compile)] in .xaml.cs file.
Example:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
...
}

Categories