Skip to content

ViewModels

hifihedgehog edited this page Mar 19, 2026 · 53 revisions

ViewModels Reference

All ViewModels live in PadForge.App/ViewModels/ under the PadForge.ViewModels namespace. They use CommunityToolkit.Mvvm (ObservableObject, RelayCommand).


ViewModelBase

File: ViewModelBase.cs

Abstract base class for all PadForge ViewModels. Extends ObservableObject (which provides INotifyPropertyChanged and SetProperty helpers).

public abstract class ViewModelBase : ObservableObject
{
    public string Title { get; set; }
}
Property Type Description
Title string Display title for the view. Used by navigation and page headers.

Culture / Localization Support

ViewModelBase subscribes to Strings.CultureChanged in its constructor to support live language switching. When the user changes the UI language at runtime, Strings.ChangeCulture() raises CultureChanged, and every living ViewModel's OnCultureChanged() method is called. Derived ViewModels override OnCultureChanged() to refresh culture-dependent computed properties (status text, display titles, etc.).

Strings.CultureChanged uses a weak event pattern: instance-method subscribers are stored as (WeakReference<Target>, MethodInfo) pairs rather than strong delegates. This means short-lived ViewModels are eligible for garbage collection without needing to explicitly unsubscribe. Dead entries are pruned automatically on each raise. Static-method subscribers use strong references.

protected ViewModelBase()
{
    Strings.CultureChanged += OnCultureChanged;  // weak — no GC leak
}

protected virtual void OnCultureChanged() { }

MainViewModel

File: MainViewModel.cs

Root ViewModel for the application. Serves as the DataContext for MainWindow. Manages navigation state, the collection of pad ViewModels, and app-wide status.

public partial class MainViewModel : ViewModelBase

Child ViewModels

Property Type Description
Pads ObservableCollection<PadViewModel> Virtual controller pad VMs (up to InputManager.MaxPads = 16).
NavControllerItems ObservableCollection<NavControllerItemViewModel> Sidebar navigation items for created controller slots.
Dashboard DashboardViewModel Dashboard overview VM.
Devices DevicesViewModel Devices list VM.
Settings SettingsViewModel Application settings VM.

Navigation

Property Type Description
SelectedNavTag string Currently selected nav tag: "Dashboard", "Pad1".."Pad16", "Devices", "Settings", "About", "Profiles".
IsPadPageSelected bool (computed) true if a Pad page (Pad1..Pad16) is currently selected.
SelectedPadIndex int (computed) Zero-based pad index for the selected Pad page, or -1.
SelectedPad PadViewModel (computed) PadViewModel for the selected Pad page, or null.

Status Properties

Property Type Description
StatusText string Status bar text (default: "Ready").
IsEngineRunning bool Whether the input engine polling loop is active. Also raises EngineStatusText.
EngineStatusText string (computed) "Running" or "Stopped".
PollingFrequency double Current input polling frequency in Hz.
ConnectedDeviceCount int Number of currently connected input devices.
IsViGEmInstalled bool Whether the ViGEmBus driver is available.

Commands

Command Type Execute CanExecute
StartEngineCommand RelayCommand Raises StartEngineRequested !IsEngineRunning
StopEngineCommand RelayCommand Raises StopEngineRequested IsEngineRunning

Events

Event Signature Description
StartEngineRequested EventHandler User requests engine start.
StopEngineRequested EventHandler User requests engine stop.
NavControllerItemsRefreshed EventHandler Raised after RefreshNavControllerItems() completes.

Methods

public void RefreshNavControllerItems()

Rebuilds sidebar controller entries from SettingsManager.SlotCreated[]. Computes per-type instance numbering (Xbox #1, DS4 #1, etc.) and updates NavControllerItems, SlotLabel, TypeInstanceLabel, icon keys, and enabled states.

public void ForceNavControllerItemsRefreshed()

Forces a NavControllerItemsRefreshed event even when slot count hasn't changed. Used after slot reorder/swap.

public void RefreshCommands()

Calls NotifyCanExecuteChanged() on start/stop commands.

NavControllerItemViewModel (nested class)

public class NavControllerItemViewModel : ObservableObject
{
    public NavControllerItemViewModel(int padIndex);
    public int PadIndex { get; }
    public string Tag { get; }                   // "Pad1".."Pad16"
    public int SlotNumber { get; set; }          // 1-based global number
    public string InstanceLabel { get; set; }    // Per-type instance number
    public string IconKey { get; set; }          // "XboxControllerIcon"/"DS4ControllerIcon"/"VJoyControllerIcon"/"MidiControllerIcon"/"KeyboardMouseControllerIcon"
    public bool IsEnabled { get; set; }
    public int ConnectedDeviceCount { get; set; }
}

DashboardViewModel

File: DashboardViewModel.cs

ViewModel for the Dashboard page. At-a-glance overview of all controller slots, engine status, device counts, driver status, and DSU settings.

public partial class DashboardViewModel : ViewModelBase

Slot Summaries

Property Type Description
SlotSummaries ObservableCollection<SlotSummary> Summary cards for active virtual controller slots.
ShowAddController bool Whether the "Add Controller" card is visible (any type has capacity).

Engine Status

Property Type Description
EngineStatus string Current engine status text. Default: "Stopped".
PollingFrequency double Polling frequency in Hz.
PollingFrequencyText string (computed) Formatted: "987.3 Hz" or "---".

Device Counts

Property Type Description
TotalDevices int Total detected input devices.
OnlineDevices int Currently connected devices.
MappedDevices int Devices with active slot mappings.

Driver Status

Property Type Description
IsViGEmInstalled bool ViGEmBus installed. Also raises ViGEmStatusText.
ViGEmStatusText string (computed) "Installed" or "Not Installed".
ViGEmVersion string ViGEmBus driver version.
IsHidHideInstalled bool HidHide installed. Also raises HidHideStatusText.
HidHideStatusText string (computed) "Installed" or "Not Installed".
IsVJoyInstalled bool vJoy driver installed. Also raises VJoyStatusText.
VJoyStatusText string (computed) "Installed" or "Not Installed".

DSU Motion Server

Property Type Description
EnableDsuMotionServer bool Whether DSU server is enabled.
DsuMotionServerPort int UDP port (clamped 1024-65535, default 26760).
DsuServerStatus string Server status for UI display.

Web Controller

Property Type Description
EnableWebController bool Whether the web controller server is enabled
WebControllerPort int HTTP/WebSocket port (1024-65535, default 8080)
WebControllerStatus string Current server status text
WebControllerClientCount int Connected browser clients

Methods

public void RefreshActiveSlots(IList<int> activeSlots, bool canAddMore)

Rebuilds SlotSummaries to match active slots. Updates sequential global numbering. Sets ShowAddController.

SlotSummary (nested class)

public class SlotSummary : ObservableObject
{
    public SlotSummary(int padIndex);
    public int PadIndex { get; }
    public string SlotLabel { get; set; }
    public string DeviceName { get; set; }                // Default: "No device"
    public bool IsActive { get; set; }
    public bool IsVirtualControllerConnected { get; set; }
    public int MappedDeviceCount { get; set; }
    public int ConnectedDeviceCount { get; set; }
    public string StatusText { get; set; }                // "Active"/"Idle"/"No mapping"/"Disabled"
    public bool IsEnabled { get; set; }
    public int SlotNumber { get; set; }                   // 1-based global number
    public string TypeInstanceLabel { get; set; }         // Per-type instance
    public VirtualControllerType OutputType { get; set; }
}

PadViewModel

File: PadViewModel.cs

The most complex VM. Represents a single virtual controller slot (one of 16 pads).

public partial class PadViewModel : ViewModelBase
{
    public PadViewModel(int padIndex);
}

Identity

Property Type Description
PadIndex int Zero-based pad slot index (0-15).
SlotNumber int One-based slot number. Settable property with backing field (_slotNumber), initialized to padIndex + 1 in constructor and updated by RefreshNavControllerItems().
SlotLabel string Display label (e.g., "Virtual Controller 1").
TypeInstanceLabel string Per-type instance number label.

Output Type

Property Type Description
OutputType VirtualControllerType Virtual controller output type. Setting triggers ResetDeadZoneSettings(), RebuildMappings(), RebuildStickConfigs(), RebuildTriggerConfigs(), SyncMacroButtonStyle().
OutputTypeIndex int Int binding for ComboBox (0=Xbox, 1=DS4, 2=VJoy, 3=Midi, 4=KeyboardMouse). Maps directly to VirtualControllerType enum values.

vJoy Configuration

Property Type Description
VJoyConfig VJoySlotConfig Per-slot vJoy configuration. Subscribes to PropertyChanged for dynamic rebuilds.

MIDI Configuration

Property Type Description
MidiConfig MidiSlotConfig Per-slot MIDI configuration. Always present; meaningful when OutputType == Midi. Subscribes to PropertyChanged to push updates to the engine.

Multi-Device Selection

Property Type Description
MappedDevices ObservableCollection<MappedDeviceInfo> Physical devices mapped to this slot.
SelectedMappedDevice MappedDeviceInfo Currently selected device for configuration.
HasSelectedDevice bool (computed) Whether a device is selected.
MappedDeviceName string Primary mapped device name.
MappedDeviceGuid Guid Primary device GUID.
IsDeviceOnline bool Whether the primary device is connected.

MappedDeviceInfo (nested class)

public class MappedDeviceInfo : ObservableObject
{
    public string Name { get; set; }
    public Guid InstanceGuid { get; set; }
    public bool IsOnline { get; set; }
}

XInput Output State (15 buttons, 6 analog)

Property Type Description
ButtonA bool A button state
ButtonB bool B button state
ButtonX bool X button state
ButtonY bool Y button state
LeftShoulder bool Left bumper
RightShoulder bool Right bumper
ButtonBack bool Back/Select
ButtonStart bool Start
LeftThumbButton bool Left stick click
RightThumbButton bool Right stick click
ButtonGuide bool Guide/Home
DPadUp bool D-Pad Up
DPadDown bool D-Pad Down
DPadLeft bool D-Pad Left
DPadRight bool D-Pad Right
LeftTrigger double Left trigger (0.0-1.0)
RightTrigger double Right trigger (0.0-1.0)
ThumbLX double Left stick X normalized (0.0-1.0, 0.5=center)
ThumbLY double Left stick Y normalized
ThumbRX double Right stick X normalized
ThumbRY double Right stick Y normalized
RawThumbLX short Raw left stick X (-32768..32767)
RawThumbLY short Raw left stick Y
RawThumbRX short Raw right stick X
RawThumbRY short Raw right stick Y
RawLeftTrigger byte Raw left trigger (0-255)
RawRightTrigger byte Raw right trigger

Per-device preview values (shown on Sticks/Triggers tabs for selected device only):

DeviceThumbLX, DeviceThumbLY, DeviceThumbRX, DeviceThumbRY (double), DeviceRawThumbLX..DeviceRawThumbRY (short), DeviceLeftTrigger, DeviceRightTrigger (double), DeviceRawLeftTrigger, DeviceRawRightTrigger (byte).

Dead Zones (per-axis X/Y)

Left Stick: LeftDeadZoneX, LeftDeadZoneY, LeftAntiDeadZoneX, LeftAntiDeadZoneY, LeftLinear (all int, clamped 0-100).

Right Stick: RightDeadZoneX, RightDeadZoneY, RightAntiDeadZoneX, RightAntiDeadZoneY, RightLinear (all int, clamped 0-100).

Triggers: LeftTriggerDeadZone, RightTriggerDeadZone, LeftTriggerAntiDeadZone, RightTriggerAntiDeadZone (int, 0-100), LeftTriggerMaxRange, RightTriggerMaxRange (int, 1-100).

Backward compat shims: LeftDeadZone { set => LeftDeadZoneX = LeftDeadZoneY = value; }, RightDeadZone same pattern.

Force Feedback

Property Type Description
ForceOverallGain int Overall FF gain (0-100, default 100).
LeftMotorStrength int Left motor strength (0-100).
RightMotorStrength int Right motor strength (0-100).
SwapMotors bool Swap L/R motors.
LeftMotorDisplay double Live left motor value for visualizer.
RightMotorDisplay double Live right motor value for visualizer.

Audio Rumble

Property Type Description
AudioRumbleEnabled bool Enable audio bass rumble for this slot.
AudioRumbleSensitivity int Bass detection sensitivity.
AudioRumbleCutoffHz int Low-pass cutoff frequency in Hz.
AudioRumbleLeftMotor int Left motor strength percentage for audio rumble (0-100).
AudioRumbleRightMotor int Right motor strength percentage for audio rumble (0-100).
AudioRumbleLevelMeter double Live bass energy level for UI meter display.

Reset commands: ResetAudioRumbleSensitivityCommand, ResetAudioRumbleCutoffHzCommand, ResetAudioRumbleLeftMotorCommand, ResetAudioRumbleRightMotorCommand.

Mappings

Property Type Description
Mappings ObservableCollection<MappingItem> Mapping rows for this slot.
public void RebuildMappings()

Clears and rebuilds Mappings based on OutputType and VJoyConfig. For gamepad presets: 21 standard items (11 buttons + 4 D-Pad + 2 triggers + 4 stick axes). For custom vJoy: dynamic numbered items. Raises MappingsRebuilt.

Dynamic Stick/Trigger Config

Property Type Description
StickConfigs ObservableCollection<StickConfigItem> Dynamic stick config items.
TriggerConfigs ObservableCollection<TriggerConfigItem> Dynamic trigger config items.
public void RebuildStickConfigs()
public void RebuildTriggerConfigs()
public void SyncStickItemFromVm(StickConfigItem item)
public void SyncTriggerItemFromVm(TriggerConfigItem item)
public void SyncAllConfigItemsFromVm()

Macros

Property Type Description
Macros ObservableCollection<MacroItem> Configured macros.
SelectedMacro MacroItem Selected macro.
HasSelectedMacro bool (computed) Whether a macro is selected.
AddMacroCommand RelayCommand Creates new macro with derived button style.
RemoveMacroCommand RelayCommand Removes selected macro.

Map All / Recording

Property Type Description
CurrentRecordingTarget string TargetSettingName of the mapping being recorded. null when idle. Drives controller-tab flash animation.
SelectedConfigTab int Active tab index (0=Controller, 1=Macros, 2=Mappings, 3=Sticks, 4=Triggers, 5=Force Feedback).
VJoyOutputSnapshot VJoyRawState Latest vJoy raw output state for schematic view.

Events

Event Signature
SelectedDeviceChanged EventHandler<MappedDeviceInfo>
MappingsRebuilt EventHandler
TestRumbleRequested EventHandler
TestLeftMotorRequested EventHandler
TestRightMotorRequested EventHandler

DevicesViewModel

File: DevicesViewModel.cs

ViewModel for the Devices page. Shows all detected input devices and raw input state for the selected device.

public partial class DevicesViewModel : ViewModelBase

Device List

Property Type Description
Devices ObservableCollection<DeviceRowViewModel> All known devices.
SelectedDevice DeviceRowViewModel Currently selected device.
HasSelectedDevice bool (computed) Whether a device is selected.
TotalCount int Total detected devices.
OnlineCount int Connected devices.

Raw State Display

Property Type Description
RawAxes ObservableCollection<AxisDisplayItem> Axis values for progress bars.
RawButtons ObservableCollection<ButtonDisplayItem> Button states for circles.
RawPovs ObservableCollection<PovDisplayItem> POV hat values for compass.
KeyboardKeys ObservableCollection<KeyboardKeyItem> Keyboard key layout items.
IsKeyboardDevice bool Whether selected device is a keyboard.
SelectedButtonTotal int Total buttons on selected device.
HasRawData bool Whether raw data is available.
HasGyroData / HasAccelData bool Whether gyro/accel data available.
GyroX / GyroY / GyroZ double Gyroscope values.
AccelX / AccelY / AccelZ double Accelerometer values.
LastRawStateDeviceGuid Guid (internal) Tracks which device's collections are populated.

Slot Toggle Buttons

Property Type Description
ActiveSlotItems ObservableCollection<SlotButtonItem> Dynamic slot assignment buttons.
ToggleSlotCommand RelayCommand<int> Toggle device assignment to slot.

Commands

Command Type Execute
RefreshCommand RelayCommand Raises RefreshRequested.
AssignToSlotCommand RelayCommand<int> Raises AssignToSlotRequested.
HideDeviceCommand RelayCommand Marks device hidden, raises HideDeviceRequested.
RemoveDeviceCommand RelayCommand Removes device row and raises RemoveDeviceRequested.

Events

Event Signature
RefreshRequested EventHandler
AssignToSlotRequested EventHandler<int>
HideDeviceRequested EventHandler<Guid>
RemoveDeviceRequested EventHandler<Guid>
ToggleSlotRequested EventHandler<int>

Methods

internal void RebuildRawStateCollections(int axisCount, int buttonCount, int povCount, bool isKeyboard = false)
internal void ClearRawState()
public void RefreshSlotButtons()
public DeviceRowViewModel FindByGuid(Guid instanceGuid)
public void RefreshCounts()

Display Item Classes

AxisDisplayItem

public class AxisDisplayItem : ObservableObject
{
    public int Index { get; set; }
    public string Name { get; set; }
    public double NormalizedValue { get; set; }  // 0.0-1.0
    public int RawValue { get; set; }            // 0-65535
}

ButtonDisplayItem

public class ButtonDisplayItem : ObservableObject
{
    public int Index { get; set; }
    public bool IsPressed { get; set; }
}

KeyboardKeyItem

public class KeyboardKeyItem : ObservableObject
{
    public int VKeyIndex { get; set; }
    public string Label { get; set; }
    public double X { get; set; }
    public double Y { get; set; }
    public double KeyWidth { get; set; }
    public double KeyHeight { get; set; }
    public bool IsPressed { get; set; }
    public const double LayoutWidth = 556;
    public const double LayoutHeight = 136;
    public static ObservableCollection<KeyboardKeyItem> BuildLayout()
    public static bool IsVKeyPressed(bool[] buttons, int vk)
}

BuildLayout() generates a full ANSI QWERTY keyboard with numpad. Constants: u=24 (key width unit), g=2 (gap), kh=20 (key height), rh=22 (row height). Wrapped in a Viewbox for auto-scaling.

PovDisplayItem

public class PovDisplayItem : ObservableObject
{
    public int Index { get; set; }
    public int Centidegrees { get; set; }  // 0-35900, or -1 for centered
    public bool IsCentered { get; }        // computed
    public double AngleDegrees { get; }    // computed (0-359)
}

SlotButtonItem

public class SlotButtonItem : ObservableObject
{
    public int PadIndex { get; set; }      // 0-based slot index
    public int SlotNumber { get; set; }    // 1-based display number
    public bool IsAssigned { get; set; }
}

DeviceRowViewModel

public class DeviceRowViewModel : ObservableObject
{
    // Identity
    public Guid InstanceGuid { get; set; }
    public string DeviceName { get; set; }
    public string ProductName { get; set; }
    public Guid ProductGuid { get; set; }
    public ushort VendorId { get; set; }
    public ushort ProductId { get; set; }
    public string VendorIdHex { get; }     // computed "045E"
    public string ProductIdHex { get; }    // computed "028E"
    // Status
    public bool IsOnline { get; set; }
    public bool IsEnabled { get; set; }
    public bool IsHidden { get; set; }
    public string StatusText { get; }      // computed "Online"/"Offline"/"Disabled"
    // Capabilities
    public int AxisCount { get; set; }
    public int ButtonCount { get; set; }
    public int PovCount { get; set; }
    public string DeviceType { get; set; }
    public bool HasRumble { get; set; }
    public bool HasGyro { get; set; }
    public bool HasAccel { get; set; }
    // Slot assignment
    public List<int> AssignedSlots { get; }
    public ObservableCollection<SlotBadge> SlotBadges { get; }
    public bool IsUnassigned { get; }
    public void SetAssignedSlots(List<int> slots);
    // Misc
    public string DevicePath { get; set; }
    public string CapabilitiesSummary { get; }
    public void NotifyDisplayChanged();
}

Input Mode

Property Type Description
ForceRawJoystickMode bool Bypass SDL3 gamepad remapping and read raw joystick indices

SettingsViewModel

File: SettingsViewModel.cs

ViewModel for the Settings page.

public partial class SettingsViewModel : ViewModelBase

Theme

Property Type Description
SelectedThemeIndex int 0=System, 1=Light, 2=Dark. Raises ThemeChanged.

Driver Status (3 sections)

ViGEmBus:

Property Type Description
IsViGEmInstalled bool Installed flag.
ViGEmStatusText string (computed) "Installed" / "Not Installed".
ViGEmVersion string Version string.
InstallViGEmCommand RelayCommand CanExecute: !IsViGEmInstalled.
UninstallViGEmCommand RelayCommand CanExecute: IsViGEmInstalled && !HasAnyViGEmSlots().

HidHide:

Property Type Description
IsHidHideInstalled bool Installed flag.
HidHideStatusText string (computed) "Installed" / "Not Installed".
HidHideVersion string Version string.
InstallHidHideCommand RelayCommand CanExecute: !IsHidHideInstalled.
UninstallHidHideCommand RelayCommand CanExecute: IsHidHideInstalled.

vJoy:

Property Type Description
IsVJoyInstalled bool Installed flag.
VJoyStatusText string (computed) "Installed" / "Not Installed".
VJoyVersion string Version string.
InstallVJoyCommand RelayCommand CanExecute: !IsVJoyInstalled.
UninstallVJoyCommand RelayCommand CanExecute: IsVJoyInstalled && !HasAnyVJoySlots().

MIDI Services:

Property Type Description
IsMidiServicesInstalled bool Installed flag.
MidiServicesStatusText string (computed) "Installed" / "Not Installed".
IsMidiOsSupported bool (static) True if OS build >= 26100 (Win11 24H2).
InstallMidiServicesCommand RelayCommand CanExecute: !IsMidiServicesInstalled && IsMidiOsSupported.
UninstallMidiServicesCommand RelayCommand CanExecute: IsMidiServicesInstalled && !HasAnyMidiSlots().

HidHide Whitelist:

Property Type Description
HidHideWhitelistPaths ObservableCollection<string> Application paths whitelisted in HidHide (user-visible paths, not DOS device paths).
SelectedWhitelistPath string Currently selected path in the whitelist ListBox.
AddWhitelistPathCommand RelayCommand Opens file dialog. CanExecute: IsHidHideInstalled.
RemoveWhitelistPathCommand RelayCommand Removes selected path. CanExecute: SelectedWhitelistPath != null.

Uninstall guards:

internal Func<bool> HasAnyViGEmSlots { get; set; }      // set by MainWindow
internal Func<bool> HasAnyVJoySlots { get; set; }       // set by MainWindow
internal Func<bool> HasAnyMidiSlots { get; set; }       // set by MainWindow
internal Func<bool> HasAnyHidHideDevices { get; set; }  // set by MainWindow
public void RefreshDriverGuards()

Uninstall buttons are disabled when the corresponding feature is actively in use:

  • ViGEm: any created slot uses Xbox 360 or DS4
  • vJoy: any created slot uses vJoy
  • MIDI: any created slot uses MIDI
  • HidHide: any device has HidHide enabled

Engine Settings

Property Type Description
AutoStartEngine bool Auto-start on launch (default true).
MinimizeToTray bool Minimize to system tray.
StartMinimized bool Start minimized.
StartAtLogin bool Auto-start on login.
EnablePollingOnFocusLoss bool Continue polling on focus loss (default true).
PollingRateMs int Target polling interval (clamped 1-16).
Use2DControllerView bool Show 2D view instead of 3D.

Settings File

Property Type Description
SettingsFilePath string Path to current settings file.
HasUnsavedChanges bool Unsaved changes flag.
SaveCommand RelayCommand Raises SaveRequested.
ReloadCommand RelayCommand Raises ReloadRequested.
ResetCommand RelayCommand Raises ResetRequested.
OpenSettingsFolderCommand RelayCommand Raises OpenSettingsFolderRequested.

Diagnostics

Property Type Description
SdlVersion string SDL3 library version.
ApplicationVersion string App version.
RuntimeVersion string .NET runtime version.

Profiles

Property Type Description
EnableAutoProfileSwitching bool Auto-profile switching enabled.
ProfileItems ObservableCollection<ProfileListItem> Profile list.
SelectedProfile ProfileListItem Selected profile.
ActiveProfileInfo string Active profile display text.

Profile commands: NewProfileCommand, SaveAsProfileCommand, DeleteProfileCommand, EditProfileCommand, LoadProfileCommand.

ProfileListItem

public class ProfileListItem : ObservableObject
{
    public const string DefaultProfileId = "__default__";
    public bool IsDefault { get; }
    public string Id { get; set; }
    public string Name { get; set; }
    public string Executables { get; set; }
    public string TopologyLabel { get; set; }
    public int XboxCount { get; set; }
    public int DS4Count { get; set; }
    public int VJoyCount { get; set; }
    public int MidiCount { get; set; }
}

MappingItem

File: MappingItem.cs

Represents a single mapping row linking a physical input source to an output target.

public class MappingItem : ObservableObject
{
    public MappingItem(string targetLabel, string targetSettingName, MappingCategory category, string negSettingName = null);
}
Property Type Description
TargetLabel string Human-readable output label (e.g., "A", "Left Stick X").
TargetSettingName string PadSetting property name (e.g., "ButtonA", "LeftThumbAxisX").
Category MappingCategory Grouping category.
NegSettingName string Negative direction property (e.g., "LeftThumbAxisXNeg"). Null for non-axis.
HasNegDirection bool (computed) Whether negative direction is supported.
SourceDescriptor string Mapping descriptor: "Button 0", "Axis 1", "IHAxis 2", "POV 0 Up". Empty=unmapped.
NegSourceDescriptor string Negative-direction descriptor.
SourceDisplayText string (computed) Human-readable source text. For bidirectional: "neg / pos".
IsMapped bool (computed) Whether source is assigned.
IsRecording bool Recording mode active.
RecordButtonText string (computed) "Record" or "Recording...".
CurrentValueText string Live raw value (updated at 30Hz).
IsInverted bool Axis inversion. Setting calls RebuildDescriptor().
IsHalfAxis bool Half-axis mode. Setting calls RebuildDescriptor().
InputChoices ObservableCollection<string> Available source input options for dropdown selection.
SelectedInput string Currently selected source input from the dropdown.

SyncSelectedInputFromDescriptor

public void SyncSelectedInputFromDescriptor()

Synchronizes the SelectedInput dropdown selection from the current SourceDescriptor value. Called when descriptors are loaded or changed externally to keep the dropdown in sync.

Methods

public void LoadDescriptor(string descriptor)      // Parses I/H prefixes, sets flags + descriptor
public void LoadNegDescriptor(string descriptor)   // Sets NegSourceDescriptor
public void SetResolvedSourceText(string text)     // Human-readable override
public void SetResolvedNegText(string text)

Commands

Command Description
ToggleRecordCommand Toggles recording; raises StartRecordingRequested / StopRecordingRequested.
ClearCommand Clears source, neg source, inversion, half-axis.

MappingCategory (enum)

public enum MappingCategory { Buttons, DPad, Triggers, LeftStick, RightStick }

MacroItem

File: MacroItem.cs

Represents a single macro with trigger condition and action sequence.

public class MacroItem : ObservableObject

Identity

Property Type Description
Name string Macro name (default "New Macro").
IsEnabled bool Active flag.

Trigger Condition

Property Type Description
TriggerButtons ushort Xbox bitmask flags (all must be pressed simultaneously).
TriggerCustomButtonWords uint[4] vJoy 128-bit button bitmask. [XmlIgnore].
TriggerCustomButtons string Serializable hex form of TriggerCustomButtonWords.
UsesCustomTrigger bool (computed) Any custom trigger button set.
TriggerSource MacroTriggerSource InputDevice or OutputController.
TriggerDisplayText string (computed) Human-readable trigger combo.
TriggerDeviceGuid Guid Raw device trigger source. Guid.Empty = use bitmask.
TriggerRawButtons int[] Raw button indices.
UsesRawTrigger bool (computed) Uses raw device button trigger path.
TriggerAxisTargetList string Comma-separated axis names for combo trigger (e.g., "LeftStickX,LeftTrigger").
TriggerAxisThreshold int Axis threshold percentage (1-100). Normalized axis must exceed this.
TriggerPovs string[] POV trigger directions as "povIndex:centidegrees" strings.
UsesAxisTrigger bool (computed) True when TriggerAxisTargetList is non-empty.
UsesPovTrigger bool (computed) True when TriggerPovs has entries.
IsRecordingTrigger bool Recording trigger combo.
RecordingLiveText string Live display during recording. [XmlIgnore].
ButtonStyle MacroButtonStyle Display names style. [XmlIgnore].
CustomButtonCount int vJoy button count. [XmlIgnore].

Trigger Options

Property Type Description
TriggerMode MacroTriggerMode OnPress, OnRelease, WhileHeld.
ConsumeTriggerButtons bool Remove trigger buttons from output (default true).

Actions

Property Type Description
Actions ObservableCollection<MacroAction> Ordered action sequence.
SelectedAction MacroAction Selected action.

Repeat Settings

Property Type Description
RepeatMode MacroRepeatMode Once, FixedCount, UntilRelease.
RepeatCount int Repeat count (min 1).
RepeatDelayMs int Delay between repeats (min 0).

Runtime State (not serialized)

Property Type Description
IsExecuting bool Currently executing.
CurrentActionIndex int Position in sequence.
RemainingRepeats int Remaining repeats.
ActionStartTime DateTime When current action started.
WasTriggerActive bool Trigger active on previous frame.

MacroAction (nested class)

public class MacroAction : ObservableObject
{
    public MacroActionType Type { get; set; }
    public bool IsButtonType { get; }        // ButtonPress or ButtonRelease
    public bool IsKeyType { get; }           // KeyPress or KeyRelease
    public bool IsDurationType { get; }      // ButtonPress, KeyPress, or Delay
    public bool IsAxisType { get; }          // AxisSet
    public MacroButtonStyle ButtonStyle { get; set; }
    public int CustomButtonCount { get; set; }
    public ushort ButtonFlags { get; set; }
    public uint[] CustomButtonWords { get; set; }       // 4 x uint = 128 buttons
    public string CustomButtons { get; set; }           // Serializable hex
    public void SetCustomButton(int index, bool pressed);
    public bool IsCustomButtonPressed(int index);
    public bool HasCustomButtons { get; }
    public IReadOnlyList<GamepadButtonOption> ButtonOptions { get; }
    public int KeyCode { get; set; }
    public VirtualKey SelectedVirtualKey { get; set; }
    public string KeyString { get; set; }               // "{Control}{Alt}{Delete}" format
    public int[] ParsedKeyCodes { get; }
    public static int[] ParseKeyString(string keyString);
    public static string FormatKeyString(int[] keyCodes);
    public VirtualKey SelectedKeyToAdd { get; set; }    // Auto-appends to KeyString
    public RelayCommand ClearKeyStringCommand { get; }
    public int DurationMs { get; set; }                 // Hold/delay duration
    public short AxisValue { get; set; }                // -32768..32767
    public MacroAxisTarget AxisTarget { get; set; }
    public float MouseSensitivity { get; set; }         // Mouse/scroll sensitivity (default 10)
    public MacroMouseButton MouseButton { get; set; }   // Left, Right, Middle, X1, X2
    public bool IsMouseButtonType { get; }              // Visibility flag for mouse button UI
    public MacroAxisSource AxisSource { get; set; }     // OutputController or InputDevice
    public Guid SourceDeviceGuid { get; set; }          // Physical device GUID for InputDevice source
    public int SourceDeviceAxisIndex { get; set; }      // Axis index on the source device
    public double MouseAccumulator { get; set; }        // Internal sub-pixel precision accumulator
    public string DisplayText { get; }                  // Computed summary
}

GamepadButtonOption

public class GamepadButtonOption : ObservableObject
{
    public GamepadButtonOption(MacroAction parent, string label, ushort flag);       // Xbox/DS4 bitmask mode
    public GamepadButtonOption(MacroAction parent, string label, int customIndex);   // vJoy custom mode
    public string Label { get; internal set; }
    public ushort Flag { get; }
    public int CustomIndex { get; }     // -1 = use Flag
    public bool IsChecked { get; set; }
    public void Refresh();
}

Enums

public enum MacroTriggerMode { OnPress, OnRelease, WhileHeld, Always }
public enum MacroTriggerSource { InputDevice, OutputController }
public enum MacroRepeatMode { Once, FixedCount, UntilRelease }
public enum MacroActionType { ButtonPress, ButtonRelease, KeyPress, KeyRelease, Delay, AxisSet,
    SystemVolume, AppVolume, MouseMove, MouseButtonPress, MouseButtonRelease, MouseScroll }
public enum MacroAxisTarget { None, LeftStickX, LeftStickY, RightStickX, RightStickY, LeftTrigger, RightTrigger }
public enum MacroButtonStyle { Xbox360, DualShock4, Numbered }
public enum MacroMouseButton { Left, Right, Middle, X1, X2 }
public enum MacroAxisSource { OutputController, InputDevice }

MacroButtonNames (static helper)

public static class MacroButtonNames
{
    public static (string Label, ushort Flag)[] GetButtonDefs(MacroButtonStyle style);
    public static string FormatButtons(ushort flags, MacroButtonStyle style);
    public static string FormatCustomButtons(uint[] words);
    public static MacroButtonStyle DeriveStyle(VirtualControllerType outputType, VJoyPreset vJoyPreset = VJoyPreset.Xbox360);
}

StickConfigItem

File: StickConfigItem.cs

Represents one thumbstick section in the dynamic Sticks tab.

public class StickConfigItem : ObservableObject
{
    public StickConfigItem(int index, string title, int axisXIndex = -1, int axisYIndex = -1);
    public string Title { get; }
    public int Index { get; }
    public double DeadZoneX { get; set; }     // clamped 0-100, has DeadZoneXDigit (int, raw ±32768 units)
    public double DeadZoneY { get; set; }
    public double AntiDeadZoneX { get; set; }
    public double AntiDeadZoneY { get; set; }
    public double Linear { get; set; }
    public double LiveX { get; set; }         // 0.0-1.0 normalized
    public double LiveY { get; set; }
    public short RawX { get; set; }
    public short RawY { get; set; }
    public string RawDisplay { get; }         // Computed: "X: -1234 (50.0%)  Y: 5678 (58.7%)"
    public int AxisXIndex { get; }            // VJoyRawState axis index (-1 for gamepad)
    public int AxisYIndex { get; }
}

Dead Zone Shape

Property Type Description
DeadZoneShape DeadZoneShape (enum) Dead zone algorithm. Default ScaledRadial. Setting raises computed booleans below.
DeadZoneShapeIndex int Int wrapper for ComboBox SelectedIndex binding. Maps display order (ScaledRadial, Radial, Axial, Hybrid, SlopedScaledAxial, SlopedAxial) to the enum.
IsAxialShape bool (computed) True for Axial.
IsRadialShape bool (computed) True for Radial or ScaledRadial.
IsSlopedShape bool (computed) True for SlopedAxial or SlopedScaledAxial.
IsHybridShape bool (computed) True for Hybrid.
HasSlopedWedges bool (computed) True for SlopedAxial, SlopedScaledAxial, or Hybrid. Drives dead zone overlay wedge visualization.

Sensitivity Curves

Property Type Description
SensitivityCurveX string X-axis sensitivity curve. Format: "0,0;0.5,0.2;1,1" (semicolon-separated input,output pairs). Default "0,0;1,1". Setting raises PresetNameX.
SensitivityCurveY string Y-axis sensitivity curve. Default "0,0;1,1". Setting raises PresetNameY.
PresetNameX string (computed) Matches CurveLut.Presets or "Custom".
PresetNameY string (computed) Matches CurveLut.Presets or "Custom".
CurvePresetNames string[] (static) ["Linear", "Smooth", "Aggressive", "Instant", "S-Curve", "Delay", "Custom"].
LiveInputX double Live input value (normalized 0-1) for CurveEditor binding.
LiveInputY double Live input value for Y-axis CurveEditor.

Center Offset Calibration

Property Type Description
CenterOffsetX double X-axis center offset (-100 to 100%). Corrects stick drift before dead zone. Has CenterOffsetXDigit (int, raw ±32768 units).
CenterOffsetY double Y-axis center offset (-100 to 100%).
IsCalibrating bool Whether calibration is in progress
HardwareRawX short Unprocessed hardware value for calibration
HardwareRawY short Unprocessed hardware value for calibration

StartCalibration() samples HardwareRawX/HardwareRawY over ~0.5s (15 frames at 33ms) and sets offset to negate the average drift.

Max Range

Property Type Description
MaxRangeX double X-axis max range (1-100%). Full physical deflection maps to this ceiling. Has MaxRangeXDigit (int).
MaxRangeY double Y-axis max range (1-100%).

Reset Commands

Command Resets To
ResetAllCommand All properties to defaults (shape, offsets, DZ, ADZ, linear, curves, range)
ResetDeadZoneShapeCommand DeadZoneShape = ScaledRadial
ResetCenterOffsetXCommand / ResetCenterOffsetYCommand CenterOffset = 0
ResetDeadZoneXCommand / ResetDeadZoneYCommand DeadZone = 0
ResetAntiDeadZoneXCommand / ResetAntiDeadZoneYCommand AntiDeadZone = 0
ResetLinearCommand Linear = 0
ResetSensitivityXCommand / ResetSensitivityYCommand SensitivityCurve = "0,0;1,1"
ResetMaxRangeXCommand / ResetMaxRangeYCommand MaxRange = 100

Static Methods

internal static double ApplyCurve(double magnitude, string curveString)
internal static PointCollection BuildTriggerCurvePoints(string curveString, double deadZone, double maxRange, int chartSize = 120, int sampleCount = 64)

ApplyCurve looks up or builds a spline LUT from the curve string and returns the mapped output. BuildTriggerCurvePoints generates a 0-1 curve with dead zone flattened, used for trigger chart rendering.


TriggerConfigItem

File: TriggerConfigItem.cs

Represents one trigger section in the dynamic Triggers tab.

public class TriggerConfigItem : ObservableObject
{
    public TriggerConfigItem(int index, string title, int axisIndex = -1);
    public string Title { get; }
    public int Index { get; }
    public double DeadZone { get; set; }     // clamped 0-100, has DeadZoneDigit (int, raw 0-65535 units)
    public double MaxRange { get; set; }     // clamped 1-100, has MaxRangeDigit
    public double AntiDeadZone { get; set; } // clamped 0-100, has AntiDeadZoneDigit
    public double LiveValue { get; set; }    // 0.0-1.0 normalized
    public ushort RawValue { get; set; }
    public string RawDisplay { get; }        // Computed: "32768 (50.0%)"
    public int AxisIndex { get; }            // VJoyRawState axis index (-1 for gamepad)
}

Sensitivity Curve

Property Type Description
SensitivityCurve string Sensitivity curve. Format: "0,0;0.5,0.2;1,1". Default "0,0;1,1". Setting raises PresetName.
PresetName string (computed) Matches CurveLut.Presets or "Custom".
CurvePresetNames string[] (static) Same as StickConfigItem: ["Linear", "Smooth", "Aggressive", "Instant", "S-Curve", "Delay", "Custom"].
LiveInputForCurve double Live input value (normalized 0-1) for CurveEditor binding.

Reset Commands

Command Resets To
ResetAllCommand All properties (DZ, max range, ADZ, curve)
ResetRangeCommand DeadZone = 0, MaxRange = 100
ResetAntiDeadZoneCommand AntiDeadZone = 0
ResetSensitivityCommand SensitivityCurve = "0,0;1,1"

VJoySlotConfig

File: VJoySlotConfig.cs

Per-slot vJoy configuration. Drives HID descriptor generation, stick/trigger/POV/button counts, and mapping generation.

public enum VJoyPreset { Xbox360, DualShock4, Custom }

public class VJoySlotConfig : ObservableObject
{
    public const int MaxAxes = 8;
    public VJoyPreset Preset { get; set; }           // Setting calls ApplyPresetDefaults()
    public int ThumbstickCount { get; set; }         // Clamped: sticks*2 + triggers <= MaxAxes
    public int TriggerCount { get; set; }            // Clamped: sticks*2 + triggers <= MaxAxes
    public int PovCount { get; set; }                // Clamped 0-4
    public int ButtonCount { get; set; }             // Clamped 0-128
    public int TotalAxes { get; }                    // Min(ThumbstickCount*2 + TriggerCount, MaxAxes)
    public int MaxThumbsticks { get; }               // (MaxAxes - TriggerCount) / 2
    public int MaxTriggers { get; }                  // MaxAxes - ThumbstickCount * 2
    public bool IsGamepadPreset { get; }             // Preset != Custom
    public void ComputeAxisLayout(out int[] stickAxisX, out int[] stickAxisY, out int[] triggerAxis);
    public void ApplyPresetDefaults();
}

ComputeAxisLayout() Algorithm

Interleaves sticks and triggers: [StickX, StickY, Trigger] per group.

For min(sticks, triggers) groups:
  stickAxisX[g] = g * 3
  stickAxisY[g] = g * 3 + 1
  triggerAxis[g] = g * 3 + 2

Remaining sticks get consecutive pairs: offset, offset+1
Remaining triggers get consecutive singles: offset++

Example with 3 sticks, 2 triggers:

Axis 0: Stick 1 X   Axis 1: Stick 1 Y   Axis 2: Trigger 1
Axis 3: Stick 2 X   Axis 4: Stick 2 Y   Axis 5: Trigger 2
Axis 6: Stick 3 X   Axis 7: Stick 3 Y

Preset Defaults

Preset Sticks Triggers POVs Buttons
Xbox360 2 2 1 11
DualShock4 2 2 1 14
Custom (keep current)

VJoySlotConfigData (serialization DTO)

public class VJoySlotConfigData
{
    [XmlAttribute] public int SlotIndex { get; set; }
    [XmlAttribute] public VJoyPreset Preset { get; set; }
    [XmlAttribute] public int ThumbstickCount { get; set; }
    [XmlAttribute] public int TriggerCount { get; set; }
    [XmlAttribute] public int PovCount { get; set; }
    [XmlAttribute] public int ButtonCount { get; set; }
}

MidiSlotConfig

File: MidiSlotConfig.cs

Per-slot MIDI configuration. Drives CC number/note number range generation and velocity for MidiVirtualController.

public class MidiSlotConfig : ObservableObject
{
    public int Channel { get; set; }          // 1-16 (clamped)
    public int CcCount { get; set; }          // 0-127 (clamped)
    public int StartCc { get; set; }          // 0-127 (clamped, with interdependent clamping: StartCc + CcCount <= 128)
    public int NoteCount { get; set; }        // 0-127 (clamped)
    public int StartNote { get; set; }        // 0-127 (clamped, with interdependent clamping: StartNote + NoteCount <= 128)
    public int Velocity { get; set; }         // 0-127 (clamped)
}

Setters implement interdependent clamping: changing CcCount auto-clamps StartCc so the range stays within 0-127, and vice versa. Same for NoteCount/StartNote.

MidiSlotConfigData (serialization DTO)

public class MidiSlotConfigData
{
    [XmlAttribute] public int SlotIndex { get; set; }
    [XmlAttribute] public int Channel { get; set; } = 1;
    [XmlAttribute] public int CcCount { get; set; } = 6;
    [XmlAttribute] public int StartCc { get; set; } = 1;
    [XmlAttribute] public int NoteCount { get; set; } = 11;
    [XmlAttribute] public int StartNote { get; set; } = 60;
    [XmlAttribute] public int Velocity { get; set; } = 127;
}

Clone this wiki locally