Can someone provide an example of how to use the IconTabbedPage in Iconize, preferably in Xaml? I have an IconTabbedPage with IconNavigation pages as children, all defined in Xaml. I then set the Icon property of the subpages by specifiying the font awesome name (“fa-home”). I tried to set the title as well, but neither of these will render the icon. I have search (a lot) for examples of the IconTabbedPage but couldn’t find any in Xaml. Additional bonus if you can provide an example of how to use the icons in a list cell context action.
Looking into #Niklas Code, you can create a tabbed page with a base class that inherits from IconTabbedPage , then your xaml will look like this.
<icon:IconTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:icon="clr-namespace:Plugin.Iconize;assembly=Plugin.Iconize"
....
>
<icon:IconTabbedPage.Children>
<ContentPage Title="Build" Icon="md-build">
</ContentPage>
</icon:IconTabbedPage.Children>
I hope it will help somebody
I think you can take a look on Sample on GitHub
[\[assembly: XamlCompilation(XamlCompilationOptions.Compile)\]
namespace Iconize.FormsSample
{
public class App : Application
{
public App()
{
// The root page of your application
var tabbedPage = new IconTabbedPage { Title = "Iconize" };
foreach (var module in Plugin.Iconize.Iconize.Modules)
{
tabbedPage.Children.Add(new Page1
{
BindingContext = new ModuleWrapper(module),
Icon = module.Keys.FirstOrDefault()
});
}
MainPage = new IconNavigationPage(tabbedPage);
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}][1]
Related
I'm writing tests in Xamarin UI Test for a tab-based Xamarin Forms app. I'd like to set the automation Ids on each tab item so that my UI Test can click a specific tab, without referring to the tab's Text label, which is localized.
I imagine you need to use a custom renderer and set ContentDescription (Android) and AccessibilityIdentifier (iOS), and I've been trying to do that, with mixed results. What is the correct way to do this? If I'm on the right track with custom renderer, which renderer method(s) should I override in IOS/Android to achieve this?
UPDATE:
iOS:
Answer was provided by #apineda. See his solution below the question.
Android: Seems to required a custom renderer. It's a little yucky but it works. We have to recursively search the view hierarchy for the tab bar items and set "ContentDescription" for each. Since we are using a bottom-navigation bar, we search backwards for better performance. For topside navigation bar, you'll need to search for "TabLayout" instead of "BottomNavigationItemView".
[assembly: ExportRenderer(typeof(MainPage), typeof(CustomTabbedPageRenderer))]
namespace Company.Project.Droid.CustomRenderers
{
public class CustomTabbedPageRenderer : TabbedRenderer
{
private bool tabsSet = false;
public CustomTabbedPageRenderer(Context context)
: base(context)
{
}
protected override void DispatchDraw(Canvas canvas)
{
if (!tabsSet)
{
SetTabsContentDescription(this);
}
base.DispatchDraw(canvas);
}
private void SetTabsContentDescription(Android.Views.ViewGroup viewGroup)
{
if (tabsSet)
{
return;
}
// loop through the view hierarchy backwards. this will work faster since the tab bar
// is at the bottom of the page
for (int i = viewGroup.ChildCount -1; i >= 0; i--)
{
var menuItem = viewGroup.GetChildAt(i) as BottomNavigationItemView;
if (menuItem != null)
{
menuItem.ContentDescription = "TabBarItem" + i.ToString();
// mark the tabs as set, so we don't do this loop again
tabsSet = true;
}
else
{
var viewGroupChild = viewGroup.GetChildAt(i) as Android.Views.ViewGroup;
if (viewGroupChild != null && viewGroupChild.ChildCount > 0)
{
SetTabsContentDescription(viewGroupChild);
}
}
}
}
}
}
You don't need CustomRenderer for this. You just need to set the AutomationId to the children Pages of the TabPage and this is assigned to the bar Item.
Let's say you have this TabPage as below
<?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:local="clr-namespace:MyGreatNamespace"
x:Class="MyGreatNamespace.MyTabPage">
<TabbedPage.Children>
<local:MainPage AutomationId="MainTab" Title="Main Page" />
<local:PageOne AutomationId="TabOne" Title="Page One" />
<local:PageTwo AutomationId="TabTwo" Title="Page Two" />
</TabbedPage.Children>
</TabbedPage>
With this configuration you will be able to do:
app.Tap("TabTwo");
And you won't need to use the Text property.
Hope this helps.-
UPDATE:
Just confirmed the above does not work with Android (noticed your original question is for Android) but only with iOS. For some reason the behavior is different.
You can still use the Localized version of the Text to "Tap it" as explained below.
A trick you can use when dealing with Localized Text is that you set the right Culture then use the same resource set in the XAML as part of the Test.
i.e
app.Tap(AppResources.MyMainTabText);
I want to hide navigation bar button in xamarin. how can i do that using binding. Toolbar item doesn't have "IsVisible" property.
Following is my xaml code
please help me to sort out this issue.
I would suggest to build a bindable ToolBoxItem. That way you can control the visibility through a view model property.
An implementation could look like that:
public class BindableToolbarItem : ToolbarItem
{
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(BindableToolbarItem), true, BindingMode.TwoWay, propertyChanged: OnIsVisibleChanged);
public bool IsVisible
{
get => (bool)GetValue(IsVisibleProperty);
set => SetValue(IsVisibleProperty, value);
}
private static void OnIsVisibleChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var item = bindable as BindableToolbarItem;
if (item == null || item.Parent == null)
return;
var toolbarItems = ((ContentPage)item.Parent).ToolbarItems;
if ((bool)newvalue && !toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Add(item); });
}
else if (!(bool)newvalue && toolbarItems.Contains(item))
{
Device.BeginInvokeOnMainThread(() => { toolbarItems.Remove(item); });
}
}
}
As you have discovered yourself there is not IsVisible. So you will have to implement functionality like that yourself if you still want it.
Another way would be to handle it in the pages' code-behind and remove or add the toolbar item whenever needed.
Adding and removing is simple, just add and remove items to the ToolbarItems collection: ToolbarItems.RemoveAt(0); for instance will remove the first toolbar item.
Putting #Gerald answer in action, it would be done this way:
void Done_Clicked(System.Object sender, System.EventArgs e)
{
//Do somthing and hide the done item
ShowDoneToolbarItem(false, (ToolbarItem)sender);
}
void Entry_Focused(System.Object sender, Xamarin.Forms.FocusEventArgs e)
{
//Show the done item
ShowDoneToolbarItem(true);
}
void ShowDoneToolbarItem(bool show, ToolbarItem item = null)
{
if(show)
{
ToolbarItem done = new ToolbarItem();
done.Text = "Done";
done.Clicked += Done_Clicked;
ToolbarItems.Add(done);
}
else if(item != null)
{
ToolbarItems.Remove(item);
}
}
This is cleaner and works from the code behind.
Well we need the IsVisible property for the front end, as xamarin doesn't have it, you can use Device.RuntimePlatform to check in real time which device the application is running. Since my code is in .cs of the XAML file, we can use xaml .cs to insert items into the screen.I put if () to do the logic and check if my device is on which platform, because I don't want it to display in UWP a toolbar.
The code is in .cs of the XAML file:
public kingTest()
{
InitializeComponent();
if((Device.RuntimePlatform == "Android")||(Device.RuntimePlatform == "iOS"))
{
ToolbarItem toolbar = new ToolbarItem();
toolbar.IconImageSource = "ic_ToolBar.png";
this.ToolbarItems.Add(toolbar);
}
};
I've achieved this easily using overloaded constructors. Here's an example:
View (add the name property):
<ContentPage x:Name="ContentPage"
<!-- rest of the tag -->
/>
Code-behind (add the toolbar items):
public partial class ExamplePage : ContentPage
{
public ExamplePage()
{
InitializeComponent();
BindingContext = this;
var saveToolbarItem = new ToolbarItem { Text = "Save" };
saveToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(saveToolbarItem);
}
public ExamplePage(Object object)
{
InitializeComponent();
BindingContext = this;
var updateToolbarItem = new ToolbarItem { Text = "Update" };
updateToolbarItem.Clicked += YourMethodToBeRan;
var deleteToolbarItem = new ToolbarItem { Text = "Delete" };
deleteToolbarItem.Clicked += YourMethodToBeRan;
ContentPage.ToolbarItems.Add(updateToolbarItem);
ContentPage.ToolbarItems.Add(deleteToolbarItem);
}
// rest of the class
}
The above pseudocode will add the "Save" toolbar item when the class is instantiated with no parameter, or the "Update" and "Delete" when a parameter is provided.
This isn't as elegant as IsEnabled / IsVisible booleans but it's a step in the right direction. Following this train of thought, you could modify the children of your toolbar during runtime to "show" and "hide" by adding and removing them as children.
Good luck!
I don't know if #tequila slammer's solution fully worked on Xamarin, but for us it only kind of works in .Net Maui (the evolution of Xamarin) and binding the IsVisible property to a variable.
Once the BindableToolbarItem is removed from the ContentPage's list of ToolbarItems, it is disconnected from the object that IsVisible is bound to forever.
For example: We want to use this control to hide or show a ToolbarItem that navigates to the admin screen, if I log in as the administrator on app launch, the item is there...great. If I then log out and log in as a non-admin, the item is not there...perfect. If I then log out and log in as an admin, the item is not there (the propertyChanged: OnIsVisibleChanged never fired)...:-(.
Not a big deal for us, if you want admin access then stopping the app and starting the app to log in as the admin is not a big ask.
In the newest release with .Net 7 the workaround works never more !
The reason is because the toolbar item which revomed will destoyed !
In Xamarin Forms for iOS, I have a custom renderer for a ContentPage that displays a video control. In my Xamarin Forms app, this custom ContentPage is displayed inside a NavigationPage.
I would like to have the video screen open when a specific message comes in via MQTT.
When I open the video page by clicking a link on the main screen, it opens as expected. I know I am receiving the message via MQTT and calling Navigation.PushModalAsync() because of console statements and breakpoints. However, the custom rendered page is not displayed and the UI of my app freezes each time after calling PushModalAsync.
Is there something else I need to do to trigger Navigation.PushModalAsync() based on receiving an MQTT notification in the background of my app?
ViewRoomsPage.axml.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ViewRoomsPage : ContentPage
{
public ViewRoomsPage()
{
InitializeComponent();
}
public string StreamUri { get; set; }
}
ViewRoomsPage.axml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyForms.Pages.ViewRoomsPage">
<ContentPage.Content>
</ContentPage.Content>
VideoViewerRenderer.cs (video code removed; this should display a blank red screen. It also works when launched from a button on the main screen)
[assembly: ExportRenderer(typeof(ViewRoomsPage), typeof(ViewRoomsRenderer))]
namespace MyForms.IOS.NativeImplementations
{
public class ViewRoomsRenderer : PageRenderer
{
private IJKFFMoviePlayerController _playerController;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
e.NewElement.BackgroundColor = Color.Red;
}
}
}
Method triggered from receiving an MQTT message
public void PushViewRooms()
{
Device.BeginInvokeOnMainThread(async () =>
{
await Application.Current.MainPage.Navigation.PushModalAsync(new ViewRoomsPage());
});
}
In App.xaml.cs:
public partial class App : Application
{
public App()
{
SetupDependencies(); // using StructureMap
Manager = DependencyContainer.Resolve<IMqttManager>();
Manager.Connect();
InitializeComponent();
var mainPage = new MainPage();
MainPage = new NavigationPage(mainPage);
}
}
The problem was a deadlock caused by a Task.WaitAll() being triggered in another section of code running in the background.
Thanks all who helped sanity check that it wasn't something in the way the renderer was set up.
I'm working on an android app with xamarin. I made tabbed pages. For the second page, i want to show the camerastream from my android camera. For that, some sample code said me I need to use a textureView inside the android part of the app, but that textureview needs to be putted on that second page. Whenever I try to reach a Stacklayout inside that Page, the following error shows up: 'Page1.camera' is inaccessible due to its protection level.
Using x:FieldModifier="public" inside that stacklayout doesn't work either.
Here is the structure of my code to make it more clear
Here I make the tabbed pages:
MainPage = new TabbedPage
{
Children = {
new MainPage(),
new Page1(),
new Page2()
}
};
Inside that Page1 i have this:
<?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="App4.Page1"
Title="Licht">
<StackLayout x:Name="camera" x:FieldModifier="public" Orientation="Vertical">
</StackLayout>
</ContentPage>
And inside the MainActivity.cs i have this where i have to access the camera.
_textureView = new TextureView(Page1.camera);
And this is the structure of my app
And this is the structure of my app
Using x:FieldModifier="public" inside that stacklayout doesn't work either.
I have tried x:FieldModifier="public"in xamarin form, even though I use x:FieldModifier="public", the "camera" property is still private. This feature is not useful.
As far as I know there is no way to access "camera" inside MainActivity.cs in Xamarin form.
As a workaround you can design a page render for android platform and create a TextureView in your page render code.
how to create a page render
_textureView = new TextureView(Page1.camera);
BTW to initialize TextureView you need the object that implements the ISurfaceTextureListener interface at android platform.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity,ISurfaceTextureListener
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
TextureView textureView = new TextureView(this);
textureView.SurfaceTextureListener = this;
LoadApplication(new App());
}
public void OnSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
//start
}
public bool OnSurfaceTextureDestroyed(SurfaceTexture surface)
{
//stop
}
public void OnSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
{
throw new NotImplementedException();
}
public void OnSurfaceTextureUpdated(SurfaceTexture surface)
{
throw new NotImplementedException();
}
}
Please follow the TextureView guide for Android.
I'd like to have a Xamarin PCL ContentPage that displays a welcome message and then loads data from a web service. I have the web service working but the problem is that in the following example nothing is displayed while it takes time to work.
What is the general strategy for: displaying a wait-type Splash page, waiting for a long-running task to complete, and then continuing with the rest of the application (say a Main Menu page)?
Hopefully the strategy will work in the PCL project so that I don't have to try and write iOS and Android versions? I've tried to put code in various override methods, but nothing seems to work. Thanks in advance for any advice.
// The following is called from the "App : Application" class.
public class SplashScreen : ContentPage
{
public SplashScreen()
{
Label lblWelcome = new Label { Text = "Hello. Please wait..." };
Content = new StackLayout
{
Children = { lblWelcome }
}
//TODO: Have the device display the above content before the following continues...
CallWebServiceToLoadParameters();
//TODO: Move on to display the Main Menu
}
}
This may do the trick:
public class SplashScreen : ContentPage
{
public SplashScreen()
{
Label lblWelcome = new Label { Text = "Hello. Please wait..." };
Content = new StackLayout
{
Children = { lblWelcome }
}
Device.BeginInvokeOnMainThread(async () => {
//If awaitable
var response = await CallWebServiceToLoadParameters();
if(response{
App.Current.MainPage = //Your main page.
}
});
}
}
A little bit ugly, the correct way will be with a ViewModel for each page.