Monthly Archives: October 2022

Change the statusbar using MAUI

The status bar is the top most bar which shows things such as the battery, wifi etc. indicators. Now I got a little way to achieving this but then hit a snag – so shout out to Change Status Bar Color for Android and iOS in .NET MAUI where Gerald Versluis demonstrates the MAUI Cummunity Toolkit behaviour for solving this problem.

First off install the CommunityToolkit.Maui Nuget package 1.3.0 or above.

In MauiProgram.cs add the following to the builder.

.UseMauiCommunityToolkit();

In the MainPage.xaml we can set up our behaviour like this

<!-- You'll need the following -->
xmlns:behaviors="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"

<!-- Then within the ContentPage put the following -->
<ContentPage.Behaviors>
   <behaviors:StatusBarBehavior StatusBarColor="#FF013558" StatusBarStyle="LightContent" />
</ContentPage.Behaviors>

The statusbar colour is whatever you’re setting for you apps. base colour. The status bar style refers to the text/icons. Obviously for a dark theme/colour, such as above, you’ll want to set the style to light content. If you go for a lighter background/theme you’ll tend to set the style to DarkContent.

These fields are ofcourse bindable, so you can change as you wish.

And that’s it!

Wait, before we end this discussion, you’ll notices things don’t quite work for iOS. We do not see the colour changes to the status bar. In this case we need to edit the info.plist (best to use an XML/text editor as the option is not supported in the Visual Studio UI for this) and add

<key>UIViewControllerBasedStatusBarAppearance</key>
<false />

useState and useEffect in React

Since React’s move to using hooks instead of classes, two of the primary hooks we need to get used to using are useState and useEffect.

Note: I’m using Typescript for my React apps. so code listed will have types etc. but if you’re from a pure Javascript background it should still be pretty obvious what’s going on.

useState

So, in the time before hooks, we might use classes. We’d pass props into the constructor of the class and set state within the class. Hence, we might end up with something like this

interface IViewerProps {
   startAt: number;
}

interface IViewerState {
    counter: number;
}

export class Viewer extends React.Component<IViewerProps, IViewerState> {
   constructor(props: IViewerProps) {
      super(props);
         this.state = {
            counter:  props.startAt
         }

      this.onClick = this.onClick.bind(this);
   }

   onClick(): void {
      this.setState({
         counter: this.state.counter + 1
      });
   }

   render() {
      return (
         <div>
            <div>{this.state.counter}</div>
            <button onClick={this.onClick}>Click Me</button>
         </div>
      );
   };
}

In this instance we’d pass a startAt value via the props, assign to the internal state then update this internal state.

Functional Components

Now, the move to functional based components ofcourse would lose the ability to maintain state across function calls, unless we had some way to associate it with that function call. Whereas a class would handle this initialization within its constructor. In the case of a functional component, we need a way where subsequent calls to that function cannot reinitialize the state or – in the case of out button counter example, the state would simply be reset to the one supplied by the props each render.

Let’s look at recreating the class above but as a functional component.

export function Viewer(props: IViewerProps) {
   const [state, setState] = useState(props.startAt);

   function onClick(): void {
      setState(state + 1);
   }

   return (
      <div>
         <div>{state}</div>
            <button onClick={onClick}>Click Me</button>
         </div>
   );
}

Now in this case useState is initialized the first time it’s used with the props.startAt value. This initialization does not take place again, during the lifecycle of this function, so that when you click the button it updates the state and re-renders the component without reinitializing the state. We can see this by putting console.log(`Props: ${props.startAt} State: ${state}`); after the useState line. In this case you’ll see the props value remains constant but the state changes on each click of the button.

This is great. But, what happens if the parent control actually needs to change the props. So, for example maybe we click a reset button to reset the value to the default.

useEffect

Whilst useState allows us to store state between function calls on a React component, we need a way to handle side effects, or more specifically in this example, we need ways of changing the state when the props change.

Let’s assume our parent component can set and reset the initial state for our Viewer component via the props. In fact, here’s that App component to demonstrate this


function App() {
   const [state, setState] = useState(1);

   function onReset() {
      setState(state === 0 ? 1 : 0);
   }

   return (
      <div className="App">
         <header className="App-header">
            <Viewer startAt={state} onReset={onReset}/>
         </header>
      </div>
   );
}

Note: this is a silly contrived example as we need the props to actually change – but for real work usage, imagine at some point a change in your app. is stored to localStorage and maybe. onReset loads the latest from localStorage. If that props value has now changed it will not (at this time) be reflected in the Viewer render.

You can see we’re using useState to supply the state as props to our Viewer component from our App. If you load the app as it stands, you’ll see nothing changes on the page, from our original implementation. The Viewer will keep incrementing even when reset is clicked. This is because we have no way to reset the state (remember it’s created like it would be in a constructor, i.e. when the function was first called).

This is where we use useEffect. The useEffect hook allows us to respond to changes in the props (and/or other dependencies), by adding the following code below the useState line in the Viewer component

useEffect(() => {
   setState(props.startAt);
}, [props])

Now when the props change (the [props] code denotes useEffecthas a dependency on the props value) useEffect will call setState, updating it with the latest props. We could ofcourse make this more granular by just having a dependency on [props.startAt]. We can supply an array of dependencies, any of which changes will cause useEffect code to execute.

Note: Ofcourse with a React class-based component we will also have the issue of how to reinitialize state from the props, because the props are set via the constructor. Hence this is not an issue just for functional components but in these cases useEffect is an elegant solution.

Changing your MAUI application’s title bar colour

A quick post on how to change your MAUI application’s title bar background colour.

Navigate to Resources/Styles/Colors.xaml and change the Primary colour, for example

<Color x:Key="Primary">#FF013558</Color>

You will probably want to also change the Platforms/Android/Resources/values/colours.xml, to change the top status bar background colour on Android

<color name="colorPrimaryDark">#FF013558</color>

Changing a MAUI application splash screen

This is quick post on manipulating your MAUI application’s splash screen.

  • The first thing to change is the splash.svg file in Resources/Splash. It seems to (by default) be 456 * 456 (height and width).
  • Open the .csproj file and change the following line’s Color and/or .svg
    <MauiSplashScreen 
       Include="Resources\Splash\splash.svg" 
       Color="#FF013558" 
       BaseSize="128,128" />
    

Getting Started Storybook (Latest update)

A while back I posted Getting started with Storybook. However, like with most things JavaScript, things have changed, and those instructions are now out of date and also, now, much simpler.

I have an existing React web app. so how do I add storybook ?

Note: Do not add storybook from NPM via yarn/npm, instead use the following instructions.

From you web app’s root folder run the following from the root of an existing project

npx storybook init

This will try to detect the framework being use. In the case of React this worked a treat. It will then add a stories folder to your src folder with a bunch of examples. It adds the .storybook folder with the information to tell storybook what files to look for and, ofcourse, add all the package.json dependencies etc.

Now all you need do is run

yarn storybook

So simple.

Here’s a very simple example of a .stories.tsx. I have a Header component which simpler writes a string out for a given date.

import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';

import { Header } from '../controls/Header';

export default {
  title: 'Header',
  component: Header,
  parameters: {
    layout: 'fullscreen',
  },
} as ComponentMeta<typeof Header>;

const Template: ComponentStory<typeof Header> = (args) => <Header {...args} />;

export const Display = Template.bind({});
Display.args = {
   endDateTime: new Date(Date.parse("2022-12-31T23:59:00.000Z"))
};

The Display export is shown as a view on a component within storybook and we also get the endDateTime editor for trying different inputs out.

A few tips and tricks for using Infragistics XamDataGrid

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

I’m working on a project using the XamDataGrid. I’ve used the UltraWinGrid by Infragistics in the past but that doesn’t help at all when moving code from Windows Formds to WPF. So here’s a list of a few commonly required features and how to do them using the XamDataGrid.

Note: this post only refers to using version 12.2

Grid lines

Problem: By default there are no grid lines on the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style TargetType="{x:Type igDP:CellValuePresenter}">
   <Setter Property="BorderThickness" Value="0,0,1,1" />
   <Setter Property="BorderBrush" Value="{x:Static SystemColors.ControlLightBrush}" />
</Style>

Obviously replace the BorderBrush colour with your preferred colour.

Remove the group by area

Problem: I want to remove the group by section from the XamDataGrid.

Solution: In your ResourceDictionary (within Generic.xaml or wherever you prefer) add the following

<Style x:Key="IceGrid" TargetType="igDP:XamDataGrid">
   <Setter Property="GroupByAreaLocation" Value="None" />
</Style>

don’t forget to apply the style to your grid, i.e.

<dp:XamDataGrid Style="{StaticResource IceGrid}" DataSource="{Binding Details}">
   <!- Your grid code -->
</dp:XamDataGrid>

Column formatting

Problem: We want to change the numerical formatting for a column

Solution: We can set the EditorStyle for a field (editor doesn’t mean it will make the field editable)

<dp:XamDataGrid.FieldLayouts>
   <dp:FieldLayout>
      <dp:FieldLayout.Fields>
         <dp:Field Name="fee" Label="Fee" Width="80">
            <dp:Field.Settings>
               <dp:FieldSettings>
                  <dp:FieldSettings.EditorStyle>
                     <Style TargetType="{x:Type editors:XamNumericEditor}">
                        <Setter Property="Format" Value="0.####" />
                     </Style>
                  </dp:FieldSettings.EditorStyle>
               </dp:FieldSettings>
           </dp:Field.Settings>
        </dp:Field>         
      </dp:FieldLayout.Fields>
   </dp:FieldLayout>
</dp:XamDataGrid.FieldLayouts>

This code creates a field named fee and with the label Fee and the editor is set to only display decimal places if they actually exist.

As we’re defining the fields you’ll need to turn off auto generation of fields, as per

<dp:XamDataGrid.FieldLayoutSettings>
   <dp:FieldLayoutSettings AutoGenerateFields="False" />
</dp:XamDataGrid.FieldLayoutSettings>

First Chance Exceptions can be more important than you might think

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

You’ll often see First Chance exceptions in the output window of Visual Studio without them actually causing any problems with your application, so what are they ?

A first chance exception is usually associated with debugging an application using Visual Studio, but they can occur at runtime in release builds as well.

A first chance exception occurs whenever an exception occurs. The exception is thrown at which point .NET searches for a handler (a catch) up the call stack. If a catch is found then .NET passes control to the catch handler code. So in this instance, let’s say we see a first chance exception which is caught in some library code. Then, whilst debugging, we’ll see the message that a first chance exception occurred, but it won’t cause an exception to halt an application because it’s caught. On the other hand if no catch is found the exception becomes a second chance exception within the debugging environment and this causes the debugger to break (if it’s configured to break on exceptions).

We can watch for first chance exceptions by adding an event handler to the AppDomain.CurrentDomain.FirstChanceException, for example

AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;

private void CurrentDomain_FirstChanceException(
    object sender, FirstChanceExceptionEventArgs e)
{
   // maybe we'll log e.Exception
}

So first chance exceptions don’t tend to mean there’s problems within your code however using async/await, especially with void async methods you may find first chance exceptions are the a precursor for an unhandled exception from one of these methods.

If you check out Jon Skeet’s answer on stackoverflow to Async/await exception and Visual Studio 2013 debug output behavior he succinctly describes the problems that can occur with exception handling in async/await code.

To use this great quote “When an exception occurs in an async method, it doesn’t just propagate up the stack like it does in synchronous code. Heck, the logical stack is likely not to be there any more.”. With the async/await on a Task, the task itself contains any exceptions, but when there is no task, when we’re async/await on a void method, then there’s no means to propagate any exceptions. Instead these exceptions are more likely to first appear as First Chance exceptions and then unhandled exceptions.

See also https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Xamarin.Forms lifecycle

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

The following is an example of what’s generated as part of the Xamarin.Forms project.

public class App : Application
{
   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
   }
}
  • OnStart is called when the application starts
  • OnSleep is called when the application goes into sleep mode and/or terminates
  • OnResume is called when an application is resumed after a sleep

Swift Tasks

Swift uses the Task struct to execute code concurrently. We can run a Task and await the return value or we can use in a “fire and forget” manner. We create a task like this

Task {
   // do something
}

We do not need to call a start method or the likes, once created the code starts. If you intend to return a value you’d write something like this

let result = await Task { () -> String in
   "Hello World"
}.value

Ofcourse we might also have the possibility of throwing an exception, hence we’d use

do {
   let result = try await Task { () -> String in
      throw MyError.failed
   }.value
} catch {
}

Deploying a Xamarin Forms application to TestFlight

Note: This post was written a while back but sat in draft. I’ve published this now, but I’m not sure it’s relevant to the latest versions etc. so please bear this in mind.

Pre-requisites

You’ll need an Apple developer account if you intend to eventually deploy your application to the Apple store, using this site https://developer.apple.com/account, however you can use the free provisioning using Free provisioning for Xamarin.iOS apps.

As I already have an Apple Developer account this post will concentrate on provisioning with a Developer account id.

TestFlight

TestFlight is the Apple beta testing tool which allows users to access your application in test (i.e. not live on the app store). Your users will need to install TestFlight, see Beta Testing Made Simple with TestFlight and you’ll need to set up an application for testing via https://appstoreconnect.apple.com/

Deploying your application to TestFlight

Once you’ve built and signed your application so it’s ready for distribution then, one of the simplest ways to deploy your application to TestFlight is install Transporter on your Mac and simply Add the application to Transport which in turn will upload to your TestFlight account.

Your application will go through a build process and when completed you’ll receive an email letting you know. You’ll be waiting for the status of your build (within appstoreconnect) to change state to Approved (this may take a couple of days although I’ve also seen posts from people stating this can take longer). Once approved we select our External Groups (testers) and you should see a Enable Public Link – click on this (enable it). Now you’ll have a link to send to your users, although appstoreconnect should automatically send this to any testers you add.

Note: Your TestFlight build is available for a total of 90 days, ofcourse this excludes the build and review process. If you do not update the application within that time the app will expire.

Once your build has passed review, it’ll go into a Testing status, any testers you’ve added already will get a code to redeem in TestFlight from their device, you can also enable a link that you can send to people to load the application for testing. This obviously allows you to have a core group of testers as well as a public access to testing.

When your application is deployed to your testers via TestFlight, the icon that you launch the application from will have a small orange dot (it looks red on a dark background) to indicate this is a beta application.

When you’re ready to update your testers with a new application, if you only change the Build version, i.e. if your app version is 0.1.0 and build was 1.0, just changing this to 2.0 will mean your application need not go through review again. Instead it’ll go into a Submit Ready state, waiting for you to assign testers and then it’ll switch to Testing.

References

Automatic Provisioning for Xamarin.iOS
TestFlight – How to Upload and Distribute Your App | App Store 2021