XamRight v1.597, features and fixes, oh my!

Shout out to all of the people who have been kicking the tires of XamRight!  Despite the all the testing we were able to do prior to our first release, several of our early users ran into some problems.  We’re pleased to announce that the current version of XamRight fixes numerous crashes and annoyances that people have been running into, as well as a few new features.

Previously, we had released an early version of autocompletion support for binding expressions in Xaml files.  While the analysis under the covers was sound, it turns out we had a few lessons to learn about how autocomplete works – the sample code to show how to start writing autocomplete providers doesn’t really do it justice.  But that’s a post for another day.

This release makes autocomplete a much better experience, and includes a (stable!) preview of support for referencing nested properties, as seen below using the Xamarin CRM sample app to demonstrate (we’ve used this app before for demo purposes):

Continue reading “XamRight v1.597, features and fixes, oh my!”

Analyzing the Xamarin CRM app with XamRight

The Xamarin CRM demo app is meant to display best practices and how to effectively use some features in using Xamarin Forms in the context of a complete app.

We cloned the repo and ran XamRight on the source code (without any modifications) to see what we could find and to demo some XamRight features.  The CRM app has five warnings detected by XamRight, listed below (we disabled some inefficient layout warning detection for this exercise). None of these are catastrophic, but should be resolved to demonstrate truly clean, maintainable code.  Keep reading to get to a quick video showing how XamRight helps find and fix XAML bugs and navigate around your Xamarin Forms source code.

Continue reading “Analyzing the Xamarin CRM app with XamRight”

XamRight: Usable Analysis for Xamarin.Forms

As we posted above previously, we’ve been building a tool called XamRight to make us more efficient at developing Xamarin apps.  The entire motivation was based on the fact that we loved a lot about Xamarin development, but ran into some pain points, sometimes over and over…

XamRight logo

It’s finally here — you can download the Visual Studio extension directly from Visual Studio!

This is the most advanced XAML analysis tool around, doing a cross-language, global, incremental analysis of your entire app to give you fast, accurate results.

Find bugs that matter

We focused on the most painful bugs to debug in Xamarin, those that either silently misbehave, or those that crash your app with little or none of your own code in the call stack, for example:

  • Incorrect Binding expressions
  • Invalid references to CLR namespaces
  • Malformed Resource definitions
  • Incompatible Resource use
  • And more!

Continue reading “XamRight: Usable Analysis for Xamarin.Forms”

LayoutOptions in Xamarin.Forms

From my experience working with Xamarin.Forms layouts, I found using LayoutOptions to be rather confusing. I would throw them around if and when I thought they were appropriate. And more often than not, they produced inaccurate results. While the documentation provided on the Xamarin.Forms website was somewhat helpful, I still made mistakes and used a lot of guess work and reran my code multiple times to come up with the layouts I desired.

This post aims at clearing up some of the confusion when dealing with LayoutOptions. If you have been using Xamarin.Forms for sometime, the main takeaways are

  • Default LayoutOptions values on Xamarin.Forms Layouts, Views and Controls are Fill except for RelativeLayout where the values are FillAndExpand
  • StackLayouts position their child views according to the order in which they are added to the StackLayout, hence, LayoutOptions values without the -AndExpand suffix are ignored when used for setting LayoutOptions properties in the direction of the containing StackLayout
  • The -AndExpand suffix is redundant when used for setting LayoutOptions properties in the opposite direction of the containing StackLayout. “AndExpand” functions to allocate extra space in the StackLayout among its children and since there’s only one child in the opposite direction of the StackLayout, the suffix serves no purpose

Continue reading “LayoutOptions in Xamarin.Forms”

Disappearing SearchBar for Xamarin.Forms

Many apps with lists now allow the search bar to disappear while scrolling down the list, then reappear whenever the user starts to scroll back up.  That isn’t possible out of the box with Xamarin.Forms.  Instead we can either:

  • Insert the SearchBar in the ListView.Header so it disappears when scrolling down the list, but it won’t reappear until the user scrolls back to the start of the list.
  • Put the SearchBar above the ListView so the user doesn’t have to scroll back to the top to see it, but that means it’s always taking up space on the screen.

Neither is exactly what we want.

Supporting that in Xamarin.Forms requires a bit of work, but luckily we build most of what we need in a post on Observing ListView Scrolling.

With that as a starting point, there are two steps:

Continue reading “Disappearing SearchBar for Xamarin.Forms”

Observing ListView Scrolling in Xamarin.Forms

Xamarin.Forms ListView gives some nice flexibility in how lists are displayed, but has some limitations in observing user interaction. In a recent project, I had a couple of things I wanted to know:

  1. Is the user scrolling up or down?
  2. Is the user at the start of the list?

What I needed was more or less this on the PCL side:

    public class MyListView : ListView
    {
        public static readonly BindableProperty
            LastScrollDirectionProperty =
                BindableProperty.Create(nameof(LastScrollDirection),
                typeof(string), typeof(MyListView), null);
        public static readonly BindableProperty
            AtStartOfListProperty =
                BindableProperty.Create(nameof(AtStartOfList),
                typeof(bool), typeof(MyListView), false);

        public string LastScrollDirection
        {
            get { return (string)GetValue(LastScrollDirectionProperty); }
            set { SetValue(LastScrollDirectionProperty, value); }
        }

        public bool AtStartOfList
        {
            get { return (bool)GetValue(AtStartOfListProperty); }
            set { SetValue(AtStartOfListProperty, value); }
        }
    }

This will walk through the tricky bits of the implementation. Some initialization and cleanup is omitted. You can see the code on GitHub for full details.

Continue reading “Observing ListView Scrolling in Xamarin.Forms”

Key Value Observing (KVO) on iOS for Xamarin

For developers coming to Xamarin from the native iOS world, this is probably old news. But for those of us coming from a different direction, read on for a handy way to work with native iOS controls.

The iOS (and macOS) runtimes have a built-in property notification system, somewhat analogous to INotifyPropertyChanged in .NET, Key Value Observing. While Xamarin has some nice documentation on using the Key-Value Coding system for macOS, there is little about using it on iOS, or how to use it to hook into platform events.  There is the basic API documentation, for instance on AddObserver, which we’ll be using.  Note that there is some misleading information on the page, which we’ll explain below.  That being said, AddObserver is a nice, easy-to-use .NET-feeling wrapper around the underlying functionality.

I’ve found KVO programming to be an important tool for developing custom renderers for Xamarin.Forms, where I want to extend the functionality of an existing renderer rather than completely write a new one.
Continue reading “Key Value Observing (KVO) on iOS for Xamarin”

Program analysis for XAML

Xamarin.Forms is a great platform for building a wide range of apps. XAML and data binding make layouts a lot clearer than writing it in C#, and it saves a ton of code. But the development process has some gotchas that wouldn’t be there in a pure C# app.

When working in C#, you get a lot of help from the language and IDEs keeping you from making a bunch of kinds of mistakes. Type safety stops a lot of potential bugs from making it past the Compile step. The refactoring support for C# is pretty slick as well. Move code around, rename stuff, change namespaces, and it all gets sorted out quickly.

But when working in XAML, that’s mostly gone. There isn’t a lot of checking that happens during the build, even with XAML compilation turned on. Bugs in XAML end up getting debugged at runtime. This means a much slower bug fix process than an equivalent bug in C# would have. Some of these bugs end up causing crashes, so they’re pretty easy to spot once you run your app and get to the right page. Others, like mistyping identifier names in Bindings, can just silently fail.

As a simple example, let’s compare the mistake of putting resources directly inside <ContentPage.Resources> and forgetting to wrap in a <ResourceDictionary>. This buggy code in a XAML file:

    <ContentPage.Resources>
        <Color x:Key="MyColor">#123456</Color>
    </ContentPage.Resources>

Will build just fine, and depending on the resources, the app could fail in a number of ways at runtime. Try something similar in C#:

Resources = new KeyValuePair("MyColor", Color.Yellow);

And the app won’t even build.

XamRight

I figured there had to be a better way, some way to use XAML and databinding, and also get the kind of type safety and typo catching that I rely on with C#. From personal conversations, reading forums.xamarin.com and stackoverflow, I’m pretty sure I’m not the only one who could benefit from this checking.

After spending a lot of time wading into Visual Studio extensibility, I’m happy to announce that XamRight is just about ready for beta testing! It’s a Visual Studio extension that gives you those little squiggly lines to tell you something’s wrong while editing your Xamarin.Forms XAML. Install it, and it’ll start doing its thing. You can also have it analyze your entire project, just to make sure everything is clean before you commit.

Aside from a parser that understands the structure of XAML, It’s got a fast, custom-built static analyzer to figure out BindingContexts, ListView.ItemsSource, and other key pieces of information needed to correctly understand how the Xamarin runtime will evaluate your XAML files. While it isn’t possible to automatically deduce all BindingContexts in every program, it’s already been used effectively on several apps.

While there is still quite a backlog of features that we plan to add to XamRight, it has already makes app development easier for us, so we can deliver high quality apps quicker and more reliably for our clients.

Here’s a four minute demo showing how it works on the Xamarin CRM app.

Interested in giving it a try? Contact us for access to the beta.

Tab Reselection in Xamarin.Forms, Part 2

In Part 1, we created a Forms project with iOS and Android targets that adds a tab reselect/double-tap event to Xamarin.Forms TabbedPage. There are two things left: handling UWP, and dealing with NavigationPage.

Tab Reselect for UWP

The underlying control for TabbedPage on UWP is the Pivot, and as far as I can tell, there is no event that fires on all tab selection events unless it involves a change of selected tab. So we have a somewhat harder problem than with either iOS or Android.

We can be notified on all Tapped events on the TabbedPage. But that fires for the entire page, not just the tab labels. When that event is raised due to tab selection, the event args tell us that control receiving the tap is the TextBlock for the tab label. However a TextBlock could correspond to a tab label, a Forms Label element, or possibly something else, and the TextBlock objects used as tab labels are not marked in any obvious, supported way (there is no IsTabLabel property, for instance). And there is no way to ask the Pivot for the list of tab label TextBlock elements, at least without excessive digging and reflection. I’d prefer to avoid that.

Let’s look at how Xamarin.Forms sets up the FormsPivot to be used as a TabbedPageRenderer using this ResourceDictionary: https://github.com/xamarin/Xamarin.Forms/blob/8c5fd096945301a2db0d85baf77ce46812a8d89f/Xamarin.Forms.Platform.UAP/TabbedPageStyle.xaml. The page titles go in TextBlocks named TabbedPageHeaderTextBlock. These are the tab labels we’re looking for.

If we look in the source code for the TabbedPageRenderer (https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Platform.UAP/TabbedPageRenderer.cs), we see it looking for the same string to use in setting the style. Bingo! We can use that name too to tell if we’re looking at a tab label. It’s not pretty, as it relies on a non-public implementation detail, but it will serve the purpose.

The basic approach ends up being similar to iOS, where we keep track of the previously-selected tab, then fire the event whenever that same tab is tapped:

public class MainTabPageRenderer : TabbedPageRenderer
{
    private Xamarin.Forms.Page _prevPage;

    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);

        Control.Tapped += Control_Tapped;
        _prevPage = Control.SelectedItem as Xamarin.Forms.Page;
    }

    private void Control_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
    {
        var src = e.OriginalSource as TextBlock;
        if (src != null
             && src.Name == "TabbedPageHeaderTextBlock"
             && Element is TabReselectDemo.MainPage)
        {
            var newPage = src.DataContext as Xamarin.Forms.Page;
            if (newPage == _prevPage)
            {
                var page = Element as TabReselectDemo.MainPage;
                page.NotifyTabReselected();
            }
            _prevPage = newPage;
        }
    }
}

I’d feel better if the string “TabbedPageHeaderTextBlock” were a const that could be accessed from the base class, but unfortunately it is private and not even accessible via reflection. This is also going to get called quite often, which is unfortunately from a perf perspective, but that hasn’t been a problem in my uses of it.

NavigationPage

If you aren’t using NavigationPage children for your TabbedPage, that’s all you need to know. If you are using NavigationPage, which is likely, there’s one last detail to cover.

A common thing that people want to do on tab reselection is to pop to the root of the navigation stack for that tab. This is straightforward to implement in the common Forms code:

    private async void MainPage_OnTabReselected(Page curPage)
    {
        if (curPage is NavigationPage)
        {
            var navPage = curPage as NavigationPage;
            await navPage.PopToRootAsync();
            curPage = navPage.CurrentPage;
        }
   ...

And if that’s what you want to do, then great, done!

However if you don’t want to pop to the root, you’ve got a bit of work. The pop to root happens automatically on iOS (the PopToRootAsync call is redundant there). This is a built-in behavior on iOS, but it is possible to override in a ShouldSelectViewController delegate on the iOS renderer:

protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
    base.OnElementChanged(e);

    ShouldSelectViewController = HandleUITabBarSelection;
}

bool HandleUITabBarSelection(UITabBarController tabBarController, UIViewController viewController)
{
    return viewController != tabBarController.SelectedViewController;
}

This just suppresses passing the selection notification along in the case of double-taps/reselects. With that, your iOS app will now behave exactly the same way as Android and UWP with respect to tab double-tap/reselect.

Since the current implementation of the TabbedRenderer does not set ShouldSelectViewController, this approach does not affect the Xamarin-provided behavior of TabbedPage. However like some of the previous work, we are depending on implementation details that could change in the future. So to warn in case things change in future Forms releases, I’d add this before setting the delegate:

System.Diagnostics.Debug.Assert(ShouldSelectViewController == null,
                                "Fix double-tap implementation");

Wrapping Up

I generally try to stick to well-supported extensibility points when working with any framework. I couldn’t completely do that here, so here’s my self-scoring on bad I feel (on a scale of 1-10) about how I implemented this extensibility:

  • iOS: 1 (I don’t feel bad at all. I stuck to pretty standard extensibility points.)
  • iOS (NavigationPage behavior): 2 (Slight risk of future changes needed, but the assert will tell us about that.)
  • Android: 3 (overriding a private, non-virtual method seems fishy, but major changes in version upgrades could cause compile failures)
  • UWP: 4 (copying a string literal from a base class is not great, and changes in version upgrades would cause runtime behavior changes without build breaks)

In the way that I think about things, I prefer using documented interfaces, as those are most likely to be stable when doing upgrades. Failing that, I’d rather have something fail to build after upgrade. That tells me something is wrong right away, and so is fast to discover and deal with.

However sometimes you have to take dependencies on things that won’t break at compile time if the platform changes. In those cases, it’s a tough call. I generally prefer it when things just don’t work rather than crash, but it can depend on the situation.

Anyway, the code is on GitHub: https://github.com/david-js/XFTabReselect. The master branch has the code without NavigationPage support, and the NavigationPage branch (shockingly) adds that in.

Tab Reselection in Xamarin.Forms, Part 1

It’s getting to be pretty common these days to have an app with tab navigation to assign some special meaning to tapping on the active tab selector again — often something like pop to the root of the navigation stack, scroll to the top or load fresh content.  Unfortunately, Xamarin.Forms does not expose a way to detect this gesture, so we can’t support this pattern in our Xamarin.Forms apps without platform-specific code.

However since the renderers for TabbedPage can be derived from to add some behavior, we can fix that, and add an event in our cross-platform code that fires whenever a tab is double-tapped. This post will only handle Android and iOS. UWP will be handled in a subsequent post.

The Common Part

This is what we want to end up in our cross-platform code:

public partial class MainPage : TabbedPage
{
    // ... other stuff snipped out...

    public event Action<Page> OnTabReselected;

    // Will be called by custom renderers
    public void NotifyTabReselected()
    {
        OnTabReselected?.Invoke(CurrentPage);
    }
}

OnTabReselected will be used by other parts of the code to be notified on reselection. We’re going to use NotifyTabReselected to communicate from the custom renderers back to the PCL.

Tab Reselect for iOS

The Xamarin.Forms iOS TabbedRenderer is pretty easy to customize.  We will set it up to notify us on all tab selections, and it’s up to us to remember the last one. Of course we also need to figure out the initially selected tab, so that if the user’s first tap is on the initially-active tab, we’ll fire the event.

    public class MainTabPageRenderer : TabbedRenderer
    {
        private UIKit.UITabBarItem _prevItem;

        public override void ViewDidAppear(bool animated)
        {
            base.ViewDidAppear(animated);

            if (SelectedIndex < TabBar.Items.Length)
                _prevItem = TabBar.Items[SelectedIndex];
        }

        public override void ItemSelected(UIKit.UITabBar tabbar,
                                          UIKit.UITabBarItem item)
        {
            if (_prevItem == item && Element is MainPage)
            {
                var mainTabPage = Element as MainPage;
                mainTabPage.NotifyTabReselected();
            }
            _prevItem = item;
        }
    }

We use ViewDidAppear to initialize _prevItem. There are other overrides that would probably work; we just need on that is late enough that TabBar and SelectedIndex are initialized, and early enough that ItemSelected won’t get called first. ViewDidAppear fits the bill. OnElementChanged is called too early, so TabBar.Items is not yet available.

Overall, pretty straightforward. Onward!

Tab Reselect for Android

Unfortunately things aren’t quite as clean here with the switch to AppCompat and the different tab implementation.
The TabbedPageRenderer looks like it was designed to allow extensibility, but that extensibility was never quite exposed in any obvious way. However it is doable, but it’s relies on implementation details that could change.

It’s useful to look at the link above, and see that there is an method that looks like it’s designed to be called when a tab is reselected — called OnTabReselected.  If you trace through, you’ll see that that method is wired up to receive those events, and then do absolutely nothing.  What we’re going to do is to replace our own handler for that one.

This approach works as long as you’re careful to understand what you’re doing and to be extra careful anytime you pick up a new version of Xamarin.Forms.  If a new version ever starts doing anything with OnTabReselected you’ll need to rethink what you’re doing. With any luck, that will be because there is a fully-supported way to hook OnTabReselected events, and we can stop using reflection! In the meantime, this has been tested to work with Xamarin.Forms 2.3.4 for Android AppCompat (and earlier).

With that in mind, here’s the custom renderer implementation for Android:

public class MainTabPageRenderer : TabbedPageRenderer, TabLayout.IOnTabSelectedListener
{
    void TabLayout.IOnTabSelectedListener.OnTabReselected(TabLayout.Tab tab)
    {
        if (Element is MainPage)
        {
            var mainTabPage = Element as MainPage;
            mainTabPage.NotifyTabReselected();
        }
    }
}

This works because the Xamarin.Forms TabbedPageRenderer (at least the AppCompat version) uses itself as the listener for tab selection notifications, and implements TabLayout.IOnTabSelectedListener itself.  All we’re doing is overriding one of interface methods. So all other listeners on MainTabPageRenderer will be exactly the same as before.

Wrapping Up

For a fairly basic use case, that’s all you need.  Add the ExportRenderer attributes to the custom renderers, and it’ll all work. See complete project at https://github.com/david-js/XFTabReselect.

However if you use NavigationPages as the children of your TabbedPage, there’s an additional complication that I will deal with in a follow-up post.

Update: See the follow-up post for the exciting conclusion, handling UWP and NavigationPages!