Compatibility
The content of this page can only be used if the following condition(s) are all met:
- You must be running M-Files 20.12 or higher.
Overview
UI Extensibility Applications that have been updated to v4 schema, using the instructions linked above, will not work with M-Files Desktop versions older than 20.12. It is important that all of your M-Files Desktop clients are updated to version 20.12 or later before updating your UI Extensibility applications.
M-Files 20.12 brings changes in which the M-Files Desktop Client builds and renders the client interface. These changes can bring significant performance improvements. These improvements are only activated if all UIX applications are marked as “fast browsing compatible”. If one or more applications is not fast browsing compatible then you may see this dialog in your M-Files Desktop Client:
The primary difference between the legacy rendering mode and fast-browsing mode is that the lifecycle for the ShellFrame instance has changed.
In the legacy rendering mode, a ShellFrame
instance would be created every time you entered a view, then destroyed when navigating to a new view. This meant that all UI components that were attached to the ShellFrame
(such as commands, tabs, etc.) needed to be re-created when the user navigated between views.
In fast-browsing mode the ShellFrame
is created once - during the ShellUI
startup process - and is not typically re-created when navigating between views. The ShellFrame
instance is created during the ShellUI
startup process, during offline/online transitions and, in rare cases, during navigation.
For most applications this new behavior will not directly affect the application’s functionality. Some applications - those that rely on the previous ShellFrame
lifecycle - may need alterations to correctly function in fast-browsing mode.
New events
As the ShellFrame
’s lifecycle cannot be used to identify when the user changes view, M-Files 20.12 adds two additional new events to the ShellFrame
:
Event_ViewLocationChanged
Event_ViewLocationChangedAsync
These events are raised automatically when the user navigates into a new view. The Async
event is recommended to be used when the handler logic is more complex and should not block the user interface.
If registering to Event_ContentChanged
within Event_ViewLocationChanged
, it is possible that you will receive multiple callbacks before the listing is completely ready. These callbacks may include no items in the list. Using Event_ViewLocationChangedAsync
should ensure that your code is only called when the listing is ready.
Converting applications to fast-browsing compatible
M-Files version requirements
The v4 client schema is only supported in M-Files 20.12 and higher. Clients older than this version will not run any applications that target the v4 schema.
When enabling fast-browsing compatibility you may wish to change the required-mfiles-version
value in the appdef.xml
file to require M-Files 20.12 or newer:
<required-mfiles-version>20.12.0.0</required-mfiles-version>
Altering the application
Most applications will not require modification. If your application does not require modification then you can simply update the application definition file.
Updating the application definition file
The application definition file (appdef.xml
) requires two changes:
- The referenced schema must be changed to
http://www.m-files.com/schemas/appdef-client-v4.xsd
. - Any and all
ShellUI
module elements should have afast-browsing-compatible
attribute added with a value oftrue
. - If upgrading from the v1 schema: Ensure that you add the <platforms> and <platform> elements as appropriate (example below).
Below is a full example of an updated application definition file:
<?xml version="1.0"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.m-files.com/schemas/appdef-client-v4.xsd">
<guid>ff800931-f24a-aa39-99cf-b04b280e1af4</guid>
<name>Fast-Browsing Compatible application</name>
<version>0.1</version>
<description></description>
<publisher></publisher>
<copyright></copyright>
<required-mfiles-version>20.12.0.0</required-mfiles-version>
<optional>true</optional>
<enabled-by-default>true</enabled-by-default>
<platforms>
<platform>Desktop</platform>
</platforms>
<modules>
<module environment="shellui" fast-browsing-compatible="true">
<file>shellui.js</file>
</module>
</modules>
<dashboards>
<dashboard id="wrapper" >
<content>wrapper.html</content>
</dashboard>
</dashboards>
</application>
Identifying problematic code
The typical use-case which requires modification is when an application uses the ShellFrame
’s Started
event to check the current path. This may be the case if your application only shows a tab in a certain view, for example. Code similar to the following is of concern:
shellFrame.Events.Register
(
MFiles.Event.Started,
function () {
// Show the current path
shellFrame.ShowMessage(shellFrame.CurrentPath)
}
);
Another typical use-case where the application requires changes is when the application uses the ShellListing’s Started event to react to shell listings. Code similar to following is of concern as the ShellListing is not recreated when navigating between views:
function OnNewShellUI( shellUI ) {
return {
OnNewShellFrame: function ( shellFrame ) {
return {
OnNewShellListing: function ( shellListing ) {
return {
OnStarted: function () {
shellFrame.ShowMessage("ShellListing.Count: " + shellListing.Items.Count);
}
};
}
};
}
};
}
In the legacy rendering mode this would be executed every time the user went into a view; in the fast-browsing mode this would be executed only when the user first opened the M-Files interface.
Using the new events
The ShellFrame
Started
event will still fire when the user first opens M-Files but, from then on, the ViewLocationChanged
event will fire instead. To behave in the same way, code must now be written as:
function reactToPathChanging()
{
// Show the current path
shellFrame.ShowMessage(shellFrame.CurrentPath)
}
// React when the user first opens M-Files.
shellFrame.Events.Register
(
MFiles.Event.Started,
reactToPathChanging
);
// React when they navigate within a view.
shellFrame.Events.Register
(
MFiles.Event.ViewLocationChanged,
reactToPathChanging
);
And the code for the OnShellListing case must be written as:
function OnNewShellUI( shellUI ) {
return {
OnNewShellFrame: function ( shellFrame ) {
return {
OnNewShellListing: function (shellListing) {
return {
OnStarted: function () {
shellFrame.ShowMessage("ShellListing.Count: " + shellListing.Items.Count);
}
};
},
OnViewLocationChangedAsync: function () {
// Register to ContentChanged event in ViewLocationChangedAsync event, so that the
// first ContentChanged event includes the full listing.
var event = shellFrame.Listing.Events.Register(Event_ContentChanged, function ( shellItems ) {
// Do the work related to listing.
shellFrame.ShowMessage("ShellListing.Count " + shellItems.Count);
// Unregister the event so that this function triggers only once per view. Note also that
// if we do not unregister the event, it is not unregistered automatically when changing
// the view as the ShellFrame persists. As a result, we would have multiple registrations
// leading to multiple messages after changing views.
shellFrame.Listing.Events.Unregister( event );
});
}
};
}
};
}
If the application is running on a 20.12 (or later) vault but one or more of the applications is not explicitly marked as fast-browsing-compatible, then all applications will run in legacy mode. For this reason it is important that your updated application continues to function using the old event model. Note that the old event model is also needed when first entering M-Files and when performing online/offline transitions.
For most applications, all the ShellFrame related components should be created when the ShellFrame is created. There is usually no need to create these components from the new events, but if there is, do note that the components persist until the ShellFrame is recreated.
Checking the rendering mode
Even if your application is marked as fast-browsing-compatible, it is still possible that the M-Files Desktop Client will be running in legacy rendering mode. This can happen if other applications are installed in the vault and these applications are not fast-browsing-compatible.
If you need to check the current rendering mode then you can do so using the following code style:
// Returns true if the client is running in fast browsing mode,
// i.e. all apps in the vault support fast browsing.
if( shellFrame.ShellUI.FastBrowsingActive )
{
shellFrame.Events.Register
(
MFiles.Event.ViewLocationChanged,
function () {
// TODO: React to the location changing.
shellFrame.ShowMessage(shellFrame.CurrentPath)
}
);
}
Additional considerations - an example
Consider an application using an approach such as the one below. In this code the application checks the current path and adds a tab if it matches some expected value.
shellFrame.Events.Register(
MFiles.Event.Started,
function () {
onShellFrameStarted( shellFrame )
} );
function onShellFrameStarted( shellFrame ) {
// Check if we are in the special view.
if( shellFrame.CurrentPath === MySpecialViewPath ) {
// Create tab with dashboard for my special view.
// NOTE: This tab and dashboard will automatically be destroyed
// when the shellframe location changes.
var myTab = shellFrame.RightPane.AddTab( "myTab", "My Tab", "_last" );
myTab.ShowDashboard( "myDashboard", {} );
myTab.Visible = true;
myTab.Select();
}
}
If we lifted this code and adapted it so that it instead reacted to the ViewLocationChanged
event, we could end up with a situation where the code adds multiple tabs to the ShellFrame. This is because previously all tabs would be removed when the user navigated (and the old ShellFrame destroyed), whereas the tabs would persist with fast-browsing enabled.
In this instance, instead, the code should be modified to ensure that the tab is still only added once, showing and hiding the tab as appropriate as the user navigates between views:
shellFrame.Events.Register(
MFiles.Event.Started,
function () {
// Listen for location changes if necessary.
// NOTE: We can't access members on shellframe until it has started.
if( shellFrame.ShellUI.FastBrowsingActive ) {
shellFrame.Events.Register(
MFiles.Event.ViewLocationChanged,
function () {
reactToPathChanging( shellFrame )
} );
}
// React to initial location of shell frame.
reactToPathChanging( shellFrame )
} );
// New global holding our tab if it was already created.
// Would be better as object member, but kept simple for example.
// NOTE: myTab is initialized when there is a new ShellFrame. If
// myTab was defined outside the scope of the ShellFrame (in
// ShellUI scope), it would need to be reinitialized with a new
// ShellFrame as the tab that it is referring to is destroyed in
// ShellFrame recreation.
var myTab;
function reactToPathChanging( shellFrame ) {
// Check if we are in the special view.
if( shellFrame.CurrentPath === MySpecialViewPath) {
// We are in my special view.
// This shell frame may have visited my special view already,
// in which case we already would have created the tab, so we
// need to create it only if we haven't already.
if( !myTab ){
// Create the tab and dashboard.
myTab = shellFrame.RightPane.AddTab( "myTab", "My Tab", "_last" );
myTab.ShowDashboard( "myDashboard", {} );
}
// Make sure the tab is visible.
myTab.Visible = true;
myTab.Select();
} else {
// We are not in my special view.
// This shell frame may have visited my special view already,
// so we need to make sure if myTab was created, that it isn't
// visible anymore now that we've left my special view.
if( myTab ) {
myTab.Visible = false;
}
}
}