Skip to content

DevExpress-Examples/maui-stocks-mini

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DevExpress Stocks App for .NET MAUI

DevExpress Mobile UI allows you to use a .NET cross-platform UI toolkit and C# to build native apps for iOS and Android.

DevExpress Mobile UI for .NET MAUI

The DevExpress Mobile UI for Xamarin.Forms and .NET MAUI is free of charge. To learn more about our offer and to reserve your copy, visit Free DevExpress Mobile UI for Xamarin.Forms and .NET MAUI.

Requirements

Please register the DevExpress NuGet Gallery in Visual Studio to restore the NuGet packages used in this solution. See the following topic for more information: Get Started with DevExpress Mobile UI for .NET MAUI.

What's in This Repository

This repository contains a sample application designed to display historical stock information (past three months) for companies listed on NASDAQ. The app includes two screens. The main app screen uses the DevExpress Collection View for .NET MAUI to display a list of stock symbols.

iPhone 12 Pixel 5

The second screen uses the DevExpress Chart View for .NET MAUI to display historical data (daily open-close-high-low stock prices and transaction volume).

iPhone 12 Pixel 5

How to Reproduce This Application

The following step-by-step tutorial details how to reproduce this application.

Create a New Project

  1. Create a new .NET MAUI project in Visual Studio 2022. Name it Stocks. If you are new to .NET MAUI, the following Microsoft help topic will be of value: Build your first app.

    You can also call the following command in a CLI to create a new .NET MAUI project:

    dotnet new maui -n Stocks 
    
  2. Install the following packages from your personal NuGet package source:
    • DevExpress.Maui.CollectionView—contains the DevExpress .NET MAUI DXCollectionView component.
    • DevExpress.Maui.Charts—contains the DevExpress .NET MAUI ChartView component.

Collection View and Charts for .NET MAUI support both iOS and Android. Your project cannot target MacCatalyst and/or Windows. To remove them, right-click the project and click Edit Project File. Remove any references to Windows and MacCatalyst. Use the project file in this repository as an example. In addition, please remove MacCatalyst and Windows folders from the Platforms folder in Solution Explorer.

The Main Page

Our main page displays a list of companies. In the MainPage.xaml file, you must:

  1. Define the dxcv XAML namespace that refers to the DevExpress.Maui.CollectionView CLR namespace.
  2. Remove default content and add an instance of the DXCollectionView class to the page. Remove default content event handlers in the code-behind. We recommend that you remove default styles (fonts, colors, and other settings) in the App.xaml file.
<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://schemas.microsoft.com/dotnet/2021/maui/design"
    xmlns:dxcv="clr-namespace:DevExpress.Maui.CollectionView;assembly=DevExpress.Maui.CollectionView"
    xmlns:local="clr-namespace:Stocks"
    Title="Market"
    BackgroundColor="{DynamicResource BackgroundColor}"
    x:Class="Stocks.MainPage">
    <dxcv:DXCollectionView/>
</ContentPage>

Register Handlers for the Chart and Collection Views

The .NET MAUI Framework requires a registered handler for all third-party controls used in an application. Review the following Microsoft help topic for more information: Register handlers.

In the MauiProgram.cs file, call the UseDevExpress method to register handlers for the DXCollectionView, ChartView, and other DevExpress controls.

using DevExpress.Maui.CollectionView;
using DevExpress.Maui.Charts;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Controls.Xaml;

[assembly: XamlCompilationAttribute(XamlCompilationOptions.Compile)]

namespace Stocks {
    public static class MauiProgram {
        public static MauiApp CreateMauiApp() {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .UseDevExpress()
                .ConfigureFonts(fonts => {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                });
            return builder.Build();
        }
    }
}

Data Source

If we run the application as it stands, it will display an empty collection view on the main page. The next step is to populate the app with data. This sample application follows the MVVM pattern. To store data, the application uses the following classes:

  • Symbol—contains company name, ticker, and a collection of daily historical data.
  • StockPrice—contains open-close-high-low prices and transaction volume for a single day.

Create a new class in the project and paste the following code:

using System;
using System.Collections.Generic;

namespace Stocks {
    public class Symbol {
        public string Ticker { get; set; }
        public string Name { get; set; }
        public IList<StockPrice> Prices {  get; set; }
    }
    public class StockPrice {
        public DateTime Date { get; set; }
        public double Open { get; set; }
        public double Close { get; set; }
        public double High { get; set; }
        public double Low { get; set; }
        public double Volume { get; set; }
    }
}

Populate the Data Source

Most mobile applications use a REST API to obtain data from a web service. The response can be formatted in HTML, XML, JSON, or any other format. This sample application uses static data formatted in JSON and stored in a file. The JSON file contains an array of companies, with an array of daily historical stock prices for each.

Download the symbols.json file, and add this file to the solution. Right-click the project, click Add > Existing Item. Once added, right-click the file, go to properties, and set Build Action to Embedded resource.

Embedded resource

Specify the logical name in the project file as shown below.

<EmbeddedResource Include="symbols.json">
	<LogicalName>symbols</LogicalName>
</EmbeddedResource>

Use the code below to read data from the file and populate the data source. Create a new class in the project as follows:

using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Stocks {
    public static class Data {
        static readonly string logicalName = "symbols";

        static IList<Symbol> symbols;
        public static IList<Symbol> Symbols {
            get {
                if (symbols == null) {
                    symbols = GetSymbols();
                }
                return symbols;
            }
        }

        static IList<Symbol> GetSymbols() {
            List<Symbol> symbols = null;
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(logicalName))
            using (StreamReader reader = new(stream, Encoding.UTF8)) {
                var json = reader.ReadToEnd();
                symbols = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Symbol>>(json);
            }
            return symbols;
        }
    }
}

You should install the Newtonsoft.Json package to deserialize data. Review the following Microsoft help topic for more information: Install and use a package in Visual Studio.

The Main View Model

To proceed, we will need to create a view model for the main page. As the data source contains an array of daily stock prices, we can display the price change next to each company in the list. The code below defines the following view models:

  • ItemViewModel—a view model for an item in the view. It contains company name, close stock price, price change, and change percentage.
  • MainViewModel—a view model for the main page. It contains a collection of item view models. When the main view model is created, it populates the item collection with data.
using System;
using System.Collections.Generic;

namespace Stocks {
    public class MainViewModel {
        public IList<ItemViewModel> Items { get; set; }

        public MainViewModel() {
            Items = new List<ItemViewModel>();
            foreach (Symbol symbol in Data.Symbols) {
                var symbolViewModel = new ItemViewModel();
                symbolViewModel.Ticker = symbol.Ticker;
                symbolViewModel.CompanyName = symbol.Name;
                symbolViewModel.Change = symbol.Prices[0].Close - symbol.Prices[1].Close;
                symbolViewModel.ChangePercent = symbol.Prices[0].Close / symbol.Prices[1].Close - 1;
                symbolViewModel.Date = symbol.Prices[0].Date;
                symbolViewModel.ClosePrice = symbol.Prices[0].Close;
                Items.Add(symbolViewModel);
            }
        }
    }

    public class ItemViewModel {
        public string Ticker { get; set; }
        public string CompanyName { get; set; }
        public double ClosePrice { get; set; }
        public double Change { get; set; }
        public double ChangePercent { get; set; }
        public DateTime Date { get; set; }
    }
}

Update the Main Page Markup

We can now update main page markup so it displays data from the view model. We set the ContentPage.BindingContext property to a view model object and bind the DXCollectionView.ItemsSource property to a collection in this view model.

<ContentPage.BindingContext>
    <local:MainViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
    <dxcv:DXCollectionView
        ItemsSource="{Binding Items}">
    </dxcv:DXCollectionView>
</ContentPage.Content>
Item Template

The DXCollectionView.ItemTemplate property allows you to specify a data template used to render items in the view. In this example, the template contains multiple labels and an image. Review the following Microsoft help topic for more information on data templates: Creating a Data Template.

Let's create a grid layout in the item template.

<dxcv:DXCollectionView.ItemTemplate>
    <DataTemplate>
        <Grid
            Margin="0"
            BackgroundColor="Transparent"
            RowSpacing="0"
            ColumnSpacing="0"
            Padding="8,0"
            ColumnDefinitions="*, *"
            RowDefinitions="Auto, Auto, 1">
        </Grid>
    </DataTemplate>
</dxcv:DXCollectionView.ItemTemplate>

We also need to populate the grid layout with labels and images.

<Label
    Text="{Binding Ticker}"
    Margin="12,12,0,0"
    VerticalOptions="End"
    TextColor="{DynamicResource PrimaryTextColor}"
    FontSize="Medium"/>
<Label
    Text="{Binding CompanyName}"
    Grid.Row="1"
    Margin="12,4,0,12"
    VerticalOptions="Start"
    FontSize="Caption"
    TextColor="{DynamicResource SecondaryTextColor}"/>
<Label
    Text="{Binding ClosePrice}"
    Grid.Column="1"
    VerticalOptions="End"
    Margin="0,12,12,0"
    HorizontalOptions="End"
    FontSize="Medium"
    TextColor="{DynamicResource PrimaryTextColor}"/>
<dx:DXStackLayout
    Orientation="Horizontal"
    Grid.Column="1"
    Grid.Row="1"
    VerticalOptions="Start"
    HorizontalOptions="EndAndExpand"
    Margin="0,4,12,12">
    <dx:DXImage
        Source="{Binding Change, Converter={local:DoubleToImageSourceConverter
        PositiveValue='quote_arrow_up',
        NegativeValue='quote_arrow_down',
        ZeroValue='not_changed'}}"
        WidthRequest="18"
        HeightRequest="18"
        VerticalOptions="Start"
        Margin="0,0,3,0"/>
    <Label
        Text="{Binding Change, StringFormat='{0:+0.00;-0.00;0.00}'}"
        TextColor="{Binding Change, Converter={local:DoubleToColorConverter
        PositiveValue='RisingValueColor',
        NegativeValue='FallingValueColor',
        ZeroValue='TextColor'}}"
        VerticalOptions="Start"
        FontSize="Caption"
        Margin="3,0"/>
    <Label
        Text="{Binding ChangePercent, StringFormat='{0:(+0.00%);(-0.00%);(0.00%)}'}"
        TextColor="{Binding Change, Converter={local:DoubleToColorConverter
        PositiveValue='RisingValueColor', 
        NegativeValue='FallingValueColor', 
        ZeroValue='TextColor'}}"
        VerticalOptions="Start"
        Margin="3,0,0,0"
        FontSize="Caption"/>
</dx:DXStackLayout>
<dx:DXSeparator
    Grid.Row="2"
    Grid.ColumnSpan="2"
    Color="{DynamicResource SeparatorColor}"
    Margin="12,0"/>
Value Converters

As you may notice, the markup uses a converter to display an up or down arrow (depending on price increase/decrease). You can find the appropriate image files in the Images folder. Copy these files to the Resources folder in your project. The text color also depends on price change. To learn more about converters, review the following Microsoft help topic: Binding Value Converters.

using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Graphics;
using System;
using System.Globalization;

namespace Stocks {
    public class DoubleToImageSourceConverter : IValueConverter, IMarkupExtension<DoubleToImageSourceConverter> {
        public ImageSource ZeroValue { get; set; } = string.Empty;
        public ImageSource PositiveValue { get; set; } = string.Empty;
        public ImageSource NegativeValue { get; set; } = string.Empty;
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
            if (!(value is double doubleValue) || targetType != typeof(ImageSource)) return null;
            switch (doubleValue) {
                case > 0: return PositiveValue;
                case < 0: return NegativeValue;
                default: return ZeroValue;
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
            throw new NotSupportedException();
        }

        public object ProvideValue(IServiceProvider serviceProvider) {
            return this;
        }

        DoubleToImageSourceConverter IMarkupExtension<DoubleToImageSourceConverter>.ProvideValue(IServiceProvider serviceProvider) {
            return this;
        }
    }

    public class DoubleToColorConverter : IValueConverter, IMarkupExtension<DoubleToColorConverter>{
        public string ZeroValue { get; set; } = string.Empty;
        public string PositiveValue { get; set; } = string.Empty;
        public string NegativeValue { get; set; } = string.Empty;
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
            if (!(value is double doubleValue) || targetType != typeof(Color)) return null;
            switch (doubleValue) {
                case > 0: return (Color)Application.Current.Resources[PositiveValue];
                case < 0: return (Color)Application.Current.Resources[NegativeValue];
                default: return (Color)Application.Current.Resources[ZeroValue];
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
            throw new NotSupportedException();
        }

        public DoubleToColorConverter ProvideValue(IServiceProvider serviceProvider) {
            return this;
        }

        object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) {
            return this;
        }
    }
}
Theme

The application applies a dark theme to controls. You can find colors and styles in the DarkTheme.xaml and SharedStyles.xaml files within this repository. Create similar styles and colors in your project and add them to the resource dictionary in the App.xaml file.

Run the Application

You can now execute the application. Your main page should now display a list of companies.

iPhone 12 Pixel 5

The Historical Data Page

The second page displays historical data (using our .NET MAUI Chart component). Create a new Content Page in Visual Studio and name it HistoricalDataPage. Do the following in the markup:

  1. Define the dxc XAML namespace that refers to the DevExpress.Maui.Charts CLR namespace.
  2. Add an instance of the ChartView class to the page.
<ContentPage
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:dxc="clr-namespace:DevExpress.Maui.Charts;assembly=DevExpress.Maui.Charts"
    xmlns:local="clr-namespace:Stocks"
    x:Class="Stocks.HistoricalDataPage">
    <ContentPage.Content>
        <dxc:ChartView/>
    </ContentPage.Content>
</ContentPage>

Ensure the page's Build Action is set to MauiXaml.

Populate the Chart with Data

We’ll need to create a view model for this page and populate it with data. The code below defines the HistoricalDataViewModel class and exposes the following properties:

  • StockPrices—daily open-close-high-low stock prices.
  • RangeStart and RangeEnd—specify the visible date range in the chart. The chart displays data for the last 60 days. Users can scroll the chart to explore all historical price data.
using System;
using System.Collections.Generic;
using System.Linq;

namespace Stocks {
    public class HistoricalDataViewModel {
        public ItemViewModel Item { get; set; }
        public IList<StockPrice> StockPrices { get; set; }
        public DateTime RangeStart { get; set; }
        public DateTime RangeEnd { get; set; }

        public HistoricalDataViewModel(ItemViewModel item) {
            Item = item;
            Symbol symbol = Data.Symbols.Where(s => s.Ticker == this.Item.Ticker).First();
            RangeStart = symbol.Prices.First().Date;
            RangeEnd = RangeStart.AddDays(-60);
            StockPrices = new List<StockPrice>();
            foreach(StockPrice price in symbol.Prices) {
                StockPrices.Add(price);
            }
        }
    }
}

Update the Historical Data Page Markup

We can now update the historical data page so it displays data from the view model. We set the ContentPage.BindingContext property to a view model object in the page constructor.

using Microsoft.Maui.Controls;

namespace Stocks {
    public partial class HistoricalDataPage : ContentPage {
        public HistoricalDataPage(HistoricalDataViewModel viewModel) {
            InitializeComponent();
            BindingContext = viewModel;
            Title = viewModel.Item.Ticker;
        }
    }
}

At the top of the page, the app displays company name and the last price. Below company name, the app displays a chart. We place these elements within a grid layout.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="115"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
</Grid>

The first grid row contains company name and the last price. We use labels and images to display this information.

<dx:DXStackLayout
    Grid.Row="0" Grid.Column="0"
    BackgroundColor="Transparent"
    Orientation="Vertical"
    HorizontalOptions="StartAndExpand"
    VerticalOptions="CenterAndExpand"
    ItemSpacing="0"
    Margin="12">
    <Label
        Text="{Binding Item.CompanyName}"
        TextColor="{DynamicResource TextColor}"
        FontSize="Subtitle"
        Margin="0,0,12,0"/>
    <dx:DXStackLayout
        Orientation="Horizontal"
        ItemSpacing="0"
        HorizontalOptions="StartAndExpand">
        <Label
            Text="{Binding Item.ClosePrice, StringFormat='{0:0.00}'}"
            TextColor="{DynamicResource TextColor}"
            FontSize="Title"
            Margin="0,0,12,0"
            VerticalOptions="End"
            VerticalTextAlignment="End"
            LineBreakMode="TailTruncation"/>
        <dx:DXImage
            WidthRequest="18"
            HeightRequest="18"
            HorizontalOptions="End"
            Margin="0,0,3,0"
            Source="{Binding Item.Change, 
            Converter={local:DoubleToImageSourceConverter
                PositiveValue='quote_arrow_up', 
                NegativeValue='quote_arrow_down',
                ZeroValue='not_changed'}}"
            VerticalOptions="End">
            <dx:DXImage.WidthRequest>
                <OnPlatform x:TypeArguments="x:Double">
                    <On Platform="Android" Value="20"/>
                    <On Platform="iOS" Value="24"/>
                </OnPlatform>
            </dx:DXImage.WidthRequest>
            <dx:DXImage.HeightRequest>
                <OnPlatform x:TypeArguments="x:Double">
                    <On Platform="Android" Value="20"/>
                    <On Platform="iOS" Value="24"/>
                </OnPlatform>
            </dx:DXImage.HeightRequest>
        </dx:DXImage>
        <Label
            Text="{Binding Item.Change, StringFormat='{0:+0.00;-0.00;0.00}'}"
            TextColor="{Binding Item.Change, 
            Converter={local:DoubleToColorConverter
                PositiveValue='RisingValueColor', 
                NegativeValue='FallingValueColor', 
                ZeroValue='TextColor'}}"
            HorizontalOptions="End"
            VerticalOptions="End"
            FontSize="Caption"
            Margin="3,0"/>
        <Label
            Text="{Binding Item.ChangePercent, StringFormat='{0:(+0.00%);(-0.00%);(0.00%)}'}"
            TextColor="{Binding Item.Change, 
            Converter={local:DoubleToColorConverter
                PositiveValue='RisingValueColor', 
                NegativeValue='FallingValueColor', 
                ZeroValue='TextColor'}}"
            HorizontalOptions="End"
            VerticalOptions="End"
            Margin="3,0,0,0"
            FontSize="Caption"/>
    </dx:DXStackLayout>
    <Label
        Text="{Binding Item.Date, StringFormat='Date: {0:d}'}"
        TextColor="{DynamicResource SecondaryTextColor}"
        FontSize="Caption"/>
</dx:DXStackLayout>

We place the chart in the second row.

<dxc:ChartView
    Theme="Dark"
    Grid.Row="1"
    AxisXNavigationMode="ScrollingAndZooming"
    AxisMaxZoomPercent="100000">
    <dxc:ChartView.ChartStyle>
        <dxc:ChartStyle
            BackgroundColor="{StaticResource BackgroundColor}">
            <dxc:ChartStyle.Padding>
                <dxc:Padding Left="8" Right="8"/>
            </dxc:ChartStyle.Padding>
        </dxc:ChartStyle>
    </dxc:ChartView.ChartStyle>
</dxc:ChartView>
Axes

The ChartView.AxisX and ChartView.AxisY properties allow you to configure chart axes:

<dxc:ChartView.AxisX>
    <dxc:DateTimeAxisX
        x:Name="axisX"
        EmptyRangesVisible="False"
        MeasureUnit="Day">
        <dxc:DateTimeAxisX.Range>
            <dxc:DateTimeRange
                SideMargin="3"
                VisualMin="{Binding RangeStart}"
                VisualMax="{Binding RangeEnd}"/>
        </dxc:DateTimeAxisX.Range>
    </dxc:DateTimeAxisX>
</dxc:ChartView.AxisX>
<dxc:ChartView.AxisY>
    <dxc:NumericAxisY
        AlwaysShowZeroLevel="False"
        AutoRangeMode="VisibleValues">
        <dxc:NumericAxisY.DisplayPosition>
            <dxc:AxisDisplayPositionFar/>
        </dxc:NumericAxisY.DisplayPosition>
        <dxc:NumericAxisY.Layout>
            <dxc:AxisLayout Anchor1="0.333" Anchor2="1.0" />
        </dxc:NumericAxisY.Layout>
        <dxc:NumericAxisY.Label>
            <dxc:AxisLabel Position="Inside" TextFormat="$#.#"/>
        </dxc:NumericAxisY.Label>
        <dxc:NumericAxisY.Style>
            <dxc:AxisStyle
                LineVisible="False"
                MajorGridlinesVisible="True"
                MajorGridlinesColor="{StaticResource SeparatorColor}"/>
        </dxc:NumericAxisY.Style>
    </dxc:NumericAxisY>
</dxc:ChartView.AxisY>
Japanese Candlestick Chart

The CandleStickSeries contains open-close-high-low stock prices. The CandleStickSeries.Data property is set to a SeriesDataAdapter object. This object interprets bound data source fields. To specify data source fields with data, we use ValueDataMember objects. Review the following topic for more information: Data Adapters.

<dxc:ChartView.Series>
    <dxc:CandleStickSeries>
        <dxc:CandleStickSeries.Data>
            <dxc:SeriesDataAdapter
                DataSource="{Binding StockPrices}"
                ArgumentDataMember="Date">
                <dxc:ValueDataMember Type="Open" Member="Open"/>
                <dxc:ValueDataMember Type="High" Member="High"/>
                <dxc:ValueDataMember Type="Low" Member="Low"/>
                <dxc:ValueDataMember Type="Close" Member="Close"/>
            </dxc:SeriesDataAdapter>
        </dxc:CandleStickSeries.Data>
    </dxc:CandleStickSeries>
</dxc:ChartView.Series>

We assign a CandleStickSeriesStyle object to the CandleStickSeries.Style property to specify candlestick-related appearance settings.

<dxc:CandleStickSeries.Style>
    <dxc:CandleStickSeriesStyle
        RisingFill="{StaticResource RisingValueColor}"
        RisingStroke="{StaticResource RisingValueColor}"
        FallingFill="{StaticResource FallingValueColor}"
        FallingStroke="{StaticResource FallingValueColor}"/>
</dxc:CandleStickSeries.Style>
Bar Chart

The BarSeries display data as bars. We use Bar charts to display daily stock volumes.

<dxc:BarSeries>
    <dxc:BarSeries.Data>
        <dxc:SeriesDataAdapter DataSource="{Binding StockPrices}"
                                ArgumentDataMember="Date">
            <dxc:ValueDataMember Type="Value"
                                Member="Volume" />
        </dxc:SeriesDataAdapter>
    </dxc:BarSeries.Data>
    <dxc:BarSeries.Style>
        <dxc:BarSeriesStyle
            Fill="{StaticResource SymbolDetailPage_VolumeChartColor}"
            Stroke="{StaticResource SymbolDetailPage_VolumeChartColor}"/>
    </dxc:BarSeries.Style>
</dxc:BarSeries>

We can use the BarSeries.AxisY property to specify the Y-axis. A NumericAxisY object allows you to specify the following settings:

<dxc:BarSeries.AxisY>
    <dxc:NumericAxisY
        AutoRangeMode="VisibleValues">
        <dxc:NumericAxisY.LabelValueNotation>
            <dxc:AxisLabelEngineeringNotation/>
        </dxc:NumericAxisY.LabelValueNotation>
        <dxc:NumericAxisY.Layout>
            <dxc:AxisLayout Anchor1="0" Anchor2="0.333" />
        </dxc:NumericAxisY.Layout>
        <dxc:NumericAxisY.DisplayPosition>
            <dxc:AxisDisplayPositionFar/>
        </dxc:NumericAxisY.DisplayPosition>
        <dxc:NumericAxisY.Label>
            <dxc:AxisLabel Position="Inside" TextFormat="$#">
                <dxc:AxisLabel.Style>
                    <dxc:AxisLabelStyle>
                        <dxc:AxisLabelStyle.TextStyle>
                            <dxc:TextStyle Color="{StaticResource TextColor}"/>
                        </dxc:AxisLabelStyle.TextStyle>
                    </dxc:AxisLabelStyle>
                </dxc:AxisLabel.Style>
            </dxc:AxisLabel>
        </dxc:NumericAxisY.Label>
        <dxc:NumericAxisY.Style>
            <dxc:AxisStyle
                LineVisible="False"
                MajorGridlinesVisible="True"
                MajorGridlinesColor="{StaticResource SeparatorColor}"/>
        </dxc:NumericAxisY.Style>
    </dxc:NumericAxisY>
</dxc:BarSeries.AxisY>

Navigation Between Two Pages

When a user taps a company in the list on the main page, the application displays historical data for that company on the second page. Let's wrap the main page in a NavigationPage to support navigation from the main page to the second page (and back). Update the App.xaml.cs file as follows:

using Microsoft.Maui.Controls;
using Application = Microsoft.Maui.Controls.Application;

namespace Stocks {
    public partial class App : Application {
        public App() {
            InitializeComponent();
            MainPage = new NavigationPage(new MainPage());
        }
    }
}

In the MainPage.xaml file and the code-behind, handle the DXCollectionView.Tap event as follows:

using DevExpress.Maui.CollectionView;

private async void DXCollectionView_Tap(object sender, CollectionViewGestureEventArgs e) {
    var symbolViewModel = (ItemViewModel)e.Item;
    var historicalDataViewModel = new HistoricalDataViewModel(symbolViewModel);
    Navigation.PushAsync(new HistoricalDataPage(historicalDataViewModel));
}

Run the Application

Let’s execute the application once more. Users can now tap a company name on the main page and analyze the company's historical data on the second page.

iPhone 12 Pixel 5

iPhone 12 Pixel 5

Documentation

More Examples