Interacting with Other Mods
In a modded Bannerlord setup, multiple mods often extend the same Gauntlet UI screens. Occasionally, you may need to coordinate with or modify another mod's UI extensions—for instance, to resolve visual conflicts, build a compatibility bridge, or replace another mod's widget with your own customized implementation.
UIExtenderEx provides built-in discovery and control APIs that allow you to locate another mod's UIExtender instance and programmatically toggle or deregister its patches and mixins.
Locating Another Mod's Extender
You can retrieve another mod's extender instance using UIExtender.GetUIExtenderFor(string moduleName).
- Module Name: The string passed to this method must match the identifier the target mod used when calling
UIExtender.Create("TargetId"). - Registration Requirement:
GetUIExtenderForreturns theUIExtenderinstance only after the target mod has called itsRegister()method. If the target mod has not been loaded or has not yet registered its assembly, the method returnsnull.
Always use null checks (or C# pattern matching) before interacting with the retrieved instance:
if (UIExtender.GetUIExtenderFor("TargetModId") is { } targetExtender)
{
// Interact with targetExtender
}
Controlling Patches and Mixins
Once you have a reference to another mod's UIExtender, you can modify its behavior at two levels: the entire extender, or individual patch and mixin types.
1. Controlling Individual Patches or Mixins
Often, you only want to disable a single conflicting button or mixin property without turning off the rest of the other mod's features. You can enable or disable specific types using their System.Type:
using Bannerlord.UIExtenderEx;
using HarmonyLib;
// Locate the target mod's extender (e.g., Mod Configuration Menu)
if (UIExtender.GetUIExtenderFor("MCM.UI") is { } mcmExtender)
{
// Resolve the patch or mixin type (using AccessTools for soft dependencies)
var conflictingPrefabPatch = AccessTools.TypeByName("MCM.UI.UIExtenderEx.OptionsPrefabExtension1");
if (conflictingPrefabPatch is not null)
{
// Disable only this specific prefab patch
mcmExtender.Disable(conflictingPrefabPatch);
}
var conflictingMixin = AccessTools.TypeByName("MCM.UI.UIExtenderEx.OptionsVMMixin");
if (conflictingMixin is not null)
{
// Disable only this specific ViewModel mixin
mcmExtender.Disable(conflictingMixin);
}
}
If you later need to restore the extension, you can re-enable it:
mcmExtender.Enable(conflictingPrefabPatch);
Tip
If your mod has a direct assembly reference (hard dependency) to the target mod, you can pass typeof(ConflictingPatchType) directly instead of resolving it by name with AccessTools.TypeByName.
2. Controlling the Entire Extender
If you need to disable or enable all patches and mixins belonging to a mod at once:
// Temporarily disable all patches and mixins registered by the mod
mcmExtender.Disable();
// Re-enable all patches and mixins registered by the mod
mcmExtender.Enable();
3. Permanently Deregistering an Extender
If you want to completely unload another mod's extensions with no possibility of re-enabling them during the session:
// Unregisters the runtime and removes the extender completely
mcmExtender.Deregister();
Warning
Calling Deregister() completely removes the extender from the runtime lookup table and cleans up its internal resources. You cannot re-enable a deregistered extender with Enable(). Use Disable() instead if the extension might need to be re-activated later.
Lifecycle and Execution Timing
When altering another mod's extensions, timing and load order are critical.
SubModule Initialization Order
- Dependency Configuration: Ensure your mod loads after the target mod. Declare the target mod in your
SubModule.xmlunder<DependedModules>or configure the target mod under<ModulesToLoadAfterThis>. - Execution Hook:
- Most mods initialize and register their extender inside
MBSubModuleBase.OnSubModuleLoad(). In this standard case, you can place your override logic in your ownOnSubModuleLoad(). - Some mods (such as MCM) defer registration to
OnBeforeInitialModuleScreenSetAsRoot(). If the target mod registers there, execute your compatibility overrides in your ownOnBeforeInitialModuleScreenSetAsRoot()method after the base call.
- Most mods initialize and register their extender inside
Runtime Impact and Screen Refresh
- Open Screens: Gauntlet builds its widget hierarchy when a screen or movie is loaded. If a screen is already open, altering its patches will not modify the live widgets currently displayed. The changes take effect the next time that screen or movie opens.
- Compiled Prefabs and Cache Invalidation: When you call
Disable(),Enable(), orDeregister(), UIExtenderEx invalidates the cached prefab templates and updates the active fingerprint for affected compiled movies. The next time the movie opens, Gauntlet will load the updated XML or recompile a new variant reflecting the active patches. See Enabling, disabling and deregistering for technical details on cache invalidation.
This page was last modified at 10/05/2026 13:34:58 +03:00 (UTC).
Commit Message
Author: Vitalii Mikhailov
Commit: f3b725a97c3f299c20e56b5f206e27142ec15edd
* Added C# Auto-Generated XML prefab compilation for patched and dynamic movies, matching vanilla UI performance
* Added name-based property binding fallback for ViewModel subtypes not declared on the parent
* Improved unparseable prefab error logging with name and file details
* Fixed game crash with 3-segment attribute paths
* Added Bannerlord.UIExtenderE...