Access to XAML controls in a React Native for Windows application (Part 2)

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Tech Community.

In the previous blog post I showed one approach to manage a scenario that can't be done in React Native for Windows out of the box: getting access to the native XAML controls behind the JSX ones, so that you can support scenarios like showing a flyout near another control.

Thanks to the feedback from Steven Moyes and Alexander Sklar from the React Native for Windows PG, I've found out that there's a better way to achieve the same goal. Alexander is the main maintainer of an open-source library, published by Microsoft, called react-native-xaml, which enables you to bring any XAML control into your React Native for Windows applications. The only caveat is that such a UI won't be any more really cross-platform, since these controls can't be rendered on Android, iOS or macOS. However, the same limitation applies to the approach we have seen in the previous post, since Flyout is a control which is available only for Windows.

Thanks to react-native-xaml, you can easily build a component like the following one:

 

import React from 'react';
import { DatePicker } from 'react-native-xaml';
import { View } from 'react-native';

const MyXamlComponent = () => {

    return (
    <View>
        <DatePicker dayVisible={false} onSelectedDateChanged={(args) => console.log(args.nativeEvent.args.newDate) } />
      </View>
    );
    
}

export default MyXamlComponent;

 

We are importing the DatePicker control from the react-native-xaml library, which is mapped to the native DatePicker control in WinUI. Thanks to the library, we have also access to the same properties and events we have in XAML, but exposed in camel case to follow the JavaScript naming conventions. In the previous snippet, you can see we're setting the dayVisible property to hide the day from the selection and we're subscribing to the onSelectedDateChanged event to get notified any time the user selects a date. We even have access to the event arguments raised by the event, using the nativeEvent.args property. Now we have a fully fledged DatePicker control available in our application, without having to write a custom native module:

 

datepicker.png

Is it possible to use react-native-xaml to implement the scenario we have seen yesterday with the Flyout control? Yes!

 

Connecting a flyout to an existing control

Thanks to react-native-xaml, we can import true XAML controls in our application, which means that they behave in the same way they would do in a native XAML application. As such, we can simply attach a flyout in the same way we would do in a XAML page. Let's see the code:

 

import React, {useState} from 'react';
import { TextBox, MenuFlyout, MenuFlyoutItem } from 'react-native-xaml';
import { View } from 'react-native';

const XamlNotificationComponent = () => {

    return (
    <View>
      <TextBox text="this is a textbox with a menuFlyout">
        <MenuFlyout>
          <MenuFlyoutItem text="option 1" onClick={() => { alert('clicked 1'); }} />
          <MenuFlyoutItem text="option 2" onClick={() => { alert("clicked 2");}} />
        </MenuFlyout>
        </TextBox>
        
      </View>
    );
}

export default XamlNotificationComponent;

First, we import a few controls from the react-native-xaml library: TextBox, MenuFlyout and MenuFlyoutItem. Then, exactly like we would do in XAML, we define a new MenuFlyout control, and we set it as children of the TextBox control. This way, we'll anchor the flyout to the TextBox, so that it will be displayed close to it. Like in XAML, we can subscribe to the onClick event to trigger an action when an item is selected. In this case, we just display a popup with a message.

We have created the flyout, now we must make it visible. Let's expand the code to add a Button to our component, which will trigger the flyout once it's clicked:

 

import React, {useState} from 'react';
import { TextBox, MenuFlyout, MenuFlyoutItem, Button } from 'react-native-xaml';
import { View } from 'react-native';

const XamlNotificationComponent = () => {
    [isOpen, setIsOpen] = useState(false);

    return (
    <View>
      <TextBox text="this is a textbox with a menuFlyout">
        <MenuFlyout isOpen={isOpen} onClosed={() => { setIsOpen(false); }}>
          <MenuFlyoutItem text="option 1" onClick={() => { alert('clicked 1'); }} />
          <MenuFlyoutItem text="option 2" onClick={() => { alert("clicked 2");}}/>
        </MenuFlyout>
        </TextBox>
        <Button content="Show flyout" onClick={(a) => { setIsOpen(true); }} />

      </View>
    );
    
}

export default XamlNotificationComponent;

Compared to the first snippet, we have made the following changes:

  • Using the useState() React hook, we have created a new property to store in the state called isOpen. We're going to use it to store the information if the flyout is open or closed.
  • We have connected the isOpen property in the state to the isOpen property of the MenuFlyout control. This way, we can keep them coordinated and, when the state changes, also the flyout will be displayed or hidden automatically.
  • Flyouts leverage a light dismiss UI. They can be closed simply by clicking outside the flyout. As such, since we need to make sure that we keep the state coordinated with the status of the control, we subscribe to the onClosed event, and we set the isOpen property in the state to false.
  • We have added a Button control and we have subscribed to the onClick event. When it's clicked, we're going to set the isOpen property in the state to true, which will make the flyout appear.

That's it! Now, when you click on the button, you will see the flyout being displayed on top of the TextBox control, in the same way we did in the previous blog post, but without needing to create a custom native module:

flyout.png

 

 

Displaying a flyout in a specific area of the application

In the previous blog post, we used the reference approach supported by React Native to pass the JSX control to our native module, so that we could retrieve the underneath XAML control and show the flyout close to it. The reference approach can also be helpful in case you want to use react-native-xaml to display a flyout in a specific area of the application. Let's see the code!

 

import React from 'react';
import { View} from 'react-native';
import { MenuFlyout, MenuFlyoutItem, Button } from 'react-native-xaml';

class XamlNotificationWithRefComponent extends React.Component {

    constructor(props) {
        super(props);
        this.myFlyout = React.createRef();
    }
      showNotification = () => {
          MenuFlyout.ShowAt(this.myFlyout, { point: { x: 120, y: 130}});
      };

      render() {
        return (
            <View>
                <MenuFlyout ref={this.myFlyout}>
                    <MenuFlyoutItem text='option 1' />
                    <MenuFlyoutItem text='option 2' />
                </MenuFlyout>
                <Button
                    content="Click me"
                    onClick={this.showNotification} />
            </View>
        );
    }
}

export default XamlNotificationWithRefComponent;

First, we create a MenuFlyout control in JSX, using the react-native-xaml library. However, this time, we are using the React.createRef() method we've learned in the previous post to store a reference to the control using the myFlyout variable. Once we have a reference, we can call the ShowAt() method exposed by the base MenuFlyout control, which requires two parameters:

  • The MenuFlyout control we want to display. This is where we use the reference we have just acquired.
  • The coordinates where we want to display it, using a point object.

Thanks to this code, we can display the flyout in any position we want, as in the following image:

 

flyout-coordinates.png

 

Wrapping up

react-native-xaml is an amazing library, which helps you to get the best of both worlds: the great developer experience offered by React Native and the powerful XAML controls offered by WinUI. If you want to see more examples of how to use it, I recommend checking the guide you'll find in the GitHub repository.

 

I've also updated my sample to support the scenarios we've seen in this post.

 

Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.