VOOZH about

URL: https://www.nuget.org/packages/MapLargeInc.Cef.Avalonia/

⇱ NuGet Gallery | MapLargeInc.Cef.Avalonia 0.0.5-alpha




👁 Image
MapLargeInc.Cef.Avalonia 0.0.5-alpha

This is a prerelease version of MapLargeInc.Cef.Avalonia.
dotnet add package MapLargeInc.Cef.Avalonia --version 0.0.5-alpha
 
 
NuGet\Install-Package MapLargeInc.Cef.Avalonia -Version 0.0.5-alpha
 
 
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MapLargeInc.Cef.Avalonia" Version="0.0.5-alpha" />
 
 
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MapLargeInc.Cef.Avalonia" Version="0.0.5-alpha" />
 
Directory.Packages.props
<PackageReference Include="MapLargeInc.Cef.Avalonia" />
 
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MapLargeInc.Cef.Avalonia --version 0.0.5-alpha
 
 
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: MapLargeInc.Cef.Avalonia, 0.0.5-alpha"
 
 
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MapLargeInc.Cef.Avalonia@0.0.5-alpha
 
 
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MapLargeInc.Cef.Avalonia&version=0.0.5-alpha&prerelease
 
Install as a Cake Addin
#tool nuget:?package=MapLargeInc.Cef.Avalonia&version=0.0.5-alpha&prerelease
 
Install as a Cake Tool
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Introduction

MapLarge.Cef.Avalonia is a library to integrate MapLarge in a browser and allow for communication from and to the .NET application itself. It is based on CEFGlue ("Chromium as a .NET UserControl") and AvaloniaUI and has a set of utilities to ease communication.

Technical background

Integration with a browser is made possible through a technology called CEF, which stands for Chromium Embedded Framework. This allows two way communication with a Chromium browser inside a desktop application.

The MapLarge.CEF.Avalonia library provides an API to simplify the integration and communicates with a server-side ADK extension, called the 'CEF ADK extension'. ADK extensions are server side plugins and the CEF ADK extension is made to facilitate the communication specifically. So ensure this is installed in the same place as where your dashboard is deployed.

Implementation

Prerequisites

  • Running MapLarge Server: You should already have running MapLarge server. This can be either a local Docker image, or a full deployment. As long as you have a URL and the right access.
  • The MapLarge.Cef extension deployed to the MapLarge server
  • The Nuget packages: In order to get started, you'll need to reference both these MapLarge.Cef.Core library and the MapLarge.Cef.Avalonia library, which are delivered as nuget packages.

It's strongly recommended to have run through the MapLarge ADK Dashboard tutorials as this will get you familiar with MapLarge in general, provide understanding of the strengths of the query and layer options, and ADK development in general. However, the test application comes with an example dashboard to get you more familiar with the .NET side.

Adding the MapLarge dashboard

In your application, create a UserControl or Window and add a Decorator. This is the container for the browser user control:

<UserControl xmlns="https://github.com/avaloniaui"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
 x:Class="AvaloniaExampleApp.YourUserControl">

 <DockPanel>

 ...

 
 <Decorator x:Name="browserWrapper" />

 </DockPanel>
</UserControl>

Next up is connecting the decorator with the MapLarge specific Avalonia controller.

In this code below, the AvaloniaCefBrowser is the generic CEF component that is the basis for the communication, and the MapLargeBrowserController is the wrapper class that adds API to simplify the two-way communication.

 public partial class YourUserControl : UserControl
 {
 private readonly MapLargeBrowserController _mapLargeController;
 private readonly AvaloniaCefBrowser _browser;

 public YourUserControl()
 {
 InitializeComponent();

 _browser = new AvaloniaCefBrowser();
 var url = "https://yoururl.com/dashboard";
 _mapLargeController = new MapLargeBrowserController(_browser, url, null, true);

 // Avalonia requires the browser window to be part of a decorator component.
 var browserWrapperControl = this.FindControl<Decorator>("browserWrapper")!;
 browserWrapperControl.Child = _browser;
 }
 }

From that point on, you can use methods as such:

 private async void OnZoomToButtonClicked(object sender, EventArgs e) {
 await _mapLargeController.MapZoom();
 }

Authentication

By default, MapLarge requires users to login before using a dashboard. This can be done manually, but the MapLargeBrowserController has an optional parameter to provide a login details provider as a Func, to automatically provide these details once MapLarge has been initialized. This is particularly helpful for desktop applications, where the credentials may be stored internally and you do not want to ask to log in each time the application is restarted.

Once the login completes, the users credentials are stored in the browser component and subsequent communication will continue passing the token on through the browser.

The requirement to be authenticated also means that certain functionality and data is only available after the login was successful, which is why there is an event on the MapLargeBrowserController called 'LoginCompleted'. This event will be invoked after login and serves as a way to complete the initialization of your application.

_mapLargeController = new MapLargeBrowserController(_browser, serverUrl, LoginDetailsProvider, devMode);
_mapLargeController.LoginCompleted += InitializeAfterLogin;

Direct JavaScript calls

The MapLargeBrowserController has convenience methods for creating layers, etc, but since the Javascript is rich, dynamic and quickly evolving, the controller also provides a way to directly call Javascript functions. By returning the result of the function, the data can be directly be used. Note that, if working with async calls, you will need to use a callback method, which is explained below.

In the following example, we are retrieving a list of the available base layers ('satellite', 'dark', 'blank', etc):

// Get the available base layers by calling a specific MapLarge Javascript function directly
await _mapLargeController.RunScript("return ml.ui.map.BaseLayers.getCustomSupportedNames()", (string[] data) =>
{
 foreach (var name in data)
 {
 // ..
 }
});

Browser messages

Interactions in the browser window stay by default confined to the browser window, but there are situations where it is desired to have them trigger the .NET application. In the example below, we act on a mouse-over event in the browser to feed back the elevation underneath the mouse cursor.

First step is to start listening to the events:

_mapLargeController.MessageReceived += OnMessageReceived;

And the implementation:

private string OnMessageReceived(object sender, dynamic message)
{
 var result = "";

 // Note: Since Javascript is a dynamic language, we preserve this flexibility here.
 // It is recommended to use this place to statically type the data
 if (message is not Dictionary<string, object> messageInfo) return result;

 var userMessageType = messageInfo["type"].ToString();
 switch (userMessageType)
 {
 ...

 // Mouse hover callback 
 case "ElevationHover": 
 {
 // Note the use of the dispatcher: The callback from the browser is pushed over an event on a separate thread,
 // while the UI is rendered on the main thread
 if (Double.TryParse(messageInfo["data"].ToString(), out var elevation))
 {
 Dispatcher.UIThread.Post(() => { HoveredElevation = elevation; });
 }
 break;
 }
 
 ...
 }

 return result;
}

The typescript (dashboard) side of this looks similar to this:

private onElevationHovered(hoverInfo: any): boolean {
 const elevation = Number.parseFloat(hoverInfo.data.DEFAULT);
 cef.Admin.postBrowserMessage({
 Type: 'ElevationHover',
 Data: elevation
 });

 // Since we are redirecting the browser functionality, we'll return false to not show the hover popup in the browser
 return false;
}

A few extra comments on this particular code:

  • In order to not overwhelm the client, it may be desired to throttle the callbacks on the Typescript side. In this example code we stripped this code for readability, but in the example dashboard you can see how to throttle this call if needed.
  • Events are returned on a separate thread than the UIThread and thus need the Dispatcher to update the user interface
  • The MessageReceived method is invoked if the message is of type 'BrowserMessage', other message types are reserved for internal MapLarge communication, like logging in and clientside rendering callbacks

Server side layers

MapLarge LayerJSON is a format in which MapLarge defines the layer. It roughly consists out of a query part and a style part. It is one of the most powerful aspects on MapLarge and allows for fast serverside database queries, combined with extensive ways of styling the layer.

Creating layers through MapLarge LayerJSON can be daunting, which is why some convenience functions are provided. Following is an example on how to create a simple serverside layer that visualizes the 'Hotels' table where the color of the location is determined by the star rating, which is a column in the table.

private async void CreateServerSideLayerButtonClicked(object sender, RoutedEventArgs e)
{
 var layer = SimpleLayerBuilder.CreateBasicLayer("Hotels", LayerType.GeoDot);
 layer.Query.Table.Name = "hms/hotels";

 AddHotelStyling(layer);

 await _mapLargeController.AddLayer(layer);
}

private void AddHotelStyling(Layer layer) {
 var style1 = new Style {
 FillColor = "255-0-170-0"
 };

 var style1Rule = new StyleRule(style1) {
 Where = QueryWhere.Create("StarRating", TestSymbol.LessOR, 1)
 };

 layer.AddStyleRule(style1Rule);

 ...

 // And a catch all
 var catchAllStyle = new Style {
 FillColor = "255-255-0-0"
 };

 var catchAllStyleRule = new StyleRule(catchAllStyle) {
 Where = QueryWhere.Create("StarRating", TestSymbol.CatchAll, 0)
 };

 layer.AddStyleRule(catchAllStyleRule);
}

Client side layers

The more complex type of layer might be the clientside layer, which allows for data to be provided directly from the .NET side. This means that the data is not stored in the MapLarge Database, but directly rendered to the map. Note that this is not the most optimally performant option for MapLarge, but since it is the most complex to implement, this is chosen as example.

private async void onAddObjectsButtonClicked(object sender, EventArgs e)
{
 var layer = SimpleLayerBuilder.CreateBasicLayer("YourLayerId", LayerType.GeoClientDot);

 AddStylingToPointLayer(layer);

 await _mapLargeController.AddClientSideLayer(layer, GetPointDataJson);
}

private void AddStylingToPointLayer(Layer layer) {
 var blueStyle = new Style {
 FillColor = "blue"
 };

 var redStyle = new Style {
 FillColor = "red"
 };

 var styleRule = new StyleRule(blueStyle) {
 Where = new QueryWhere(
 new QueryWhereTest("id",
 new QueryWhereTestTest(TestSymbol.Greater),
 new QueryWhereValue(new QueryWhereLiteral(25_000))
 )
 )
 };

 // Or using the convenience method
 var catchAllStyleRule = new StyleRule(redStyle) {
 Where = QueryWhere.Create("id", TestSymbol.CatchAll, 0)
 };

 layer.AddStyleRule(styleRule);
 layer.AddStyleRule(catchAllStyleRule);
}

In this example, the GetPointDataJson method is used as a callback method, which is automatically called the moment MapLarge redraws the map. The result of this callback requires data similar to GeoJSON, but also in this case, MapLarge provides convenience methods to construct this data.

private string GetPointDataJson(string layerId) {
 var nObj = 50_000;

 var objectsData = new ClientSideData();

 // Define the column names
 objectsData.Columns = new List<ClientSideDataColumn>() {
 new ClientSideDataColumn("geom"),
 new ClientSideDataColumn("id"),
 new ClientSideDataColumn("name")
 };

 // Fill the data with the same ordered columns in the data for the amount of objects requested
 for(var i = 0; i < nObj; i++) {
 var id = i;
 var name = "Object_" + i;

 var lat = _randomizer.NextDouble() * (63 - 18) + 18;
 var lon = (_randomizer.NextDouble() * (140 - 64) + 64) * -1;

 // Note that the Point object has a custom JSON serializer
 var geom = Point.FromLatLon(lat, lon);

 var objectData = new List<object> {
 geom,
 id,
 name
 };

 objectsData.Data.Add(objectData);
 }

 // Write the minimal amount of data, omiting default values
 var options = new JsonSerializerOptions() {
 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
 }; 
 var json = JsonSerializer.Serialize(objectsData, options);

 return json;
}

There are convenience objects for creation of points, lines and polygons. However, if desired, this can be also done manually using arrays. For more information on ClientSide Layers, please refer to the ClientSide Layer document.

Dashboard interaction

Just like Avalonia, MapLarge Dashboards use the MVVM paradigm to separate the data from the user interface. For that reason it also supports data binding, which subsequently can be used to communicate from the .NET side.

Property binding

For example, there's an playbackPercentage member of the viewmodel of the dashboard, defined as such:

private _playbackPercentage: number = 0;
public get playbackPercentage(): number {
 return this._playbackPercentage;
}

public set playbackPercentage(v: number) {
 this._playbackPercentage = v;
}

Then in order to change this from the MapLarge controller side, a call to the SetProperty method on the MapLargeController will do the update:

private void PlaybackSlider_OnValueChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
 ...

 _playbackIndex = newPlaybackIndex;
 _mapLargeController.SetProperty("playbackPercentage", _playbackIndex);
}

Calling view model functions directly

Calling view model functions is similar to property calling, except it uses the ExecuteViewModelFunction, which allows for parameterized function calling. In a previous example we showed how to communicate data back from the browser to the .NET application and in this example, we show a practical example of this:

private async void AddWaypoint_OnClick(object? sender, RoutedEventArgs e)
{
 await _mapLargeController.ExecuteViewModelFunction<Waypoint?>("startWaypointDrawing", SelectedAircraft.Name);
}

The respective function in the dashboard:

public startWaypointDrawing(aircraftName: string): void {
 const aircraft = this._allAircraft.find(a => a.name === aircraftName);

 if (!aircraft) throw new Error(`Aircraft with name ${aircraftName} not found`);

 const rs = new ml.ui.map.RegionSelect({
 map: this._map.map,
 drawingType: "marker",
 callback: (wkt, json, drawing) => {
 const ll = ml.util.geo.wktPointToLatLng(wkt);
 const lastWaypoint = aircraft.waypoints.slice(-1)[0];

 const waypoint: IWaypoint = {
 name: this.natoPhoneticAlphabet[aircraft.waypoints.length % this.natoPhoneticAlphabet.length],
 latitude: +ll.lat,
 longitude: +ll.lng,
 altitude: lastWaypoint?.altitude ?? 100
 };

 cef.Admin.postBrowserMessage({
 Type: "NewWaypoint",
 Data: waypoint
 });
 }
 });

 rs.beginDrawing();
}

For completeness, we are showing the full implementation of the function here, including the postBrowserMessage call. This call is needed because of the asynchronous nature of the callback.

Due to this postBrowserMessage call, the message handler on the .NET side will trigger (OnMessageReceived). The corresponding code looks like this:

case "NewWaypoint":
{
 var waypoint = JsonSerializer.Deserialize<Waypoint>(JsonSerializer.Serialize(messageInfo["data"]));
 SelectedAircraft.Waypoints.Add(waypoint);
 
 _mapLargeController.SetProperty("allAircraft", AllAircraft);
 break;
}

Since we know the structure of a 'waypoint', we can use a JsonSerializer to deserialize this object into its static definition.

Test application

Included is a test application that shows the various concepts explained above and more, as:

  • A way to easily add a MapLarge dashboard to a desktop application, in particular Avalonia
  • Ability to auto-login with a method to return the login details (LoginDetailsProvider)
  • Ability to perform further initialization after login (LoginCompleted)
  • Ability to receive and handle callbacks from the dashboard, through MessageReceived handlers (MessageReceived)
  • Ability to add layers to the dashboard, with the layer building is simplified on the C# side
  • Ability to add client side layers to the dashboard, again with simplified layer building on the C# side and the data is returned from the C# side
  • Example for datasource update callbacks
  • Example for mouse hover callbacks, which requires the dispatcher due to threading related issues
  • Example of manual logging in, using a convenience method
  • Example of zooming in using a convenience method
  • Example of how to call the ML JavaScript API directly (return ml.ui.map.BaseLayers.getCustomSupportedNames())
Product Versions Compatible and additional computed target framework versions.
.NET net8.0 net8.0 is compatible.  net8.0-android net8.0-android was computed.  net8.0-browser net8.0-browser was computed.  net8.0-ios net8.0-ios was computed.  net8.0-maccatalyst net8.0-maccatalyst was computed.  net8.0-macos net8.0-macos was computed.  net8.0-tvos net8.0-tvos was computed.  net8.0-windows net8.0-windows was computed.  net9.0 net9.0 was computed.  net9.0-android net9.0-android was computed.  net9.0-browser net9.0-browser was computed.  net9.0-ios net9.0-ios was computed.  net9.0-maccatalyst net9.0-maccatalyst was computed.  net9.0-macos net9.0-macos was computed.  net9.0-tvos net9.0-tvos was computed.  net9.0-windows net9.0-windows was computed.  net10.0 net10.0 was computed.  net10.0-android net10.0-android was computed.  net10.0-browser net10.0-browser was computed.  net10.0-ios net10.0-ios was computed.  net10.0-maccatalyst net10.0-maccatalyst was computed.  net10.0-macos net10.0-macos was computed.  net10.0-tvos net10.0-tvos was computed.  net10.0-windows net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.5-alpha 196 10/20/2025
0.0.4-alpha 158 10/20/2025
0.0.3-alpha 168 10/20/2025
0.0.2-alpha 163 10/20/2025
0.0.1-alpha 162 10/20/2025