Skip to content

Reset Module Algorithm

The full walkthrough of btnResetToDefault_ItemClick in FCoreModule_Popup.vb. This is the most algorithmically interesting handler — it's three-phase and operates on the live tab control plus the global default settings store.

Goal

Restore a module's tabs to its default set:

  • Tabs that aren't in the default set are removed.
  • Tabs in the default set that don't exist locally are added.
  • Existing tabs that match by name have their contents reset to the default version.
  • Tab order is rewritten to match the order in Instance.DefaultSaveAndShare.

Phase 1 — Resolve the default-tab folder

The default settings store (Instance.DefaultSaveAndShare) is a flat list of SaveAndShareFile entries. Each entry has a FolderPath that identifies which module it belongs to. This handler maps the module's ModuleID to a folder path:

Dim folderPath As String = ""
Select Case moduleID
    Case "ExecutiveModule"        : folderPath = "\\1. Executive\"
    Case "FinancialModule"        : folderPath = "\\2. Financial\"
    Case "CustomerServiceModule"  : folderPath = "\\3. Customer Service\"
    Case "SalesMarketingModule"   : folderPath = "\\4. Sales And Marketing\"
    Case "InventoryModule"        : folderPath = "\\5. Inventory\"
    Case "PurchasingModule"       : folderPath = "\\6. Purchasing\"
    Case "ProductionModule"       : folderPath = "\\7. Production\"
    Case "MRPModule"              : folderPath = "\\8. Material Requirements\"
    Case "Analytical Dashboard"   : folderPath = "\\9. Analytical Dashboard\"
End Select

If folderPath = "" Then
    MsgBox("There are no default settings registered for this module.", MsgBoxStyle.Information)
    Exit Sub
End If

Same mapping used by the existing per-tab reset in FPulseTab_Popup.btnResetTabToDefaultSettings_ItemClick. If ModuleID doesn't appear in this list (e.g. a custom module), the user gets an informational message and the handler exits cleanly.

Company defaults

If Instance.DefaultCompanySettings is non-Nothing, the handler also computes a company-specific default folder path and checks whether any defaults exist for it:

Dim companyFolderPath As String = ""
Dim hasCompanyDefaults As Boolean = False
If Instance.DefaultCompanySettings IsNot Nothing Then
    companyFolderPath = "\\*" & Instance.License.CompanyName & " (" & AppCon.DatabaseName & ")" & "\" & folderPath.Trim("\") & "\"
    For Each curFile As SaveAndShareFile In Instance.DefaultSaveAndShare.Tabs("")
        If curFile.FolderPath = companyFolderPath Then
            hasCompanyDefaults = True
            Exit For
        End If
    Next
End If

Same precedence as the per-tab reset: company defaults are preferred when they exist.

Phase 2 — Confirm with the user

Two paths depending on whether company defaults exist:

Dim usePath As String
If hasCompanyDefaults Then
    Dim result As MsgBoxResult = MsgBox(
        "Reset the '" & curCoreModule.ModuleInfo.ModuleCaption & "' module to default?" & vbCrLf & vbCrLf &
        "All tabs will be restored to defaults; tabs that aren't in the default set will be removed." & vbCrLf & vbCrLf &
        "Yes  = use your company's defaults" & vbCrLf &
        "No   = use Leahy Consulting's original defaults" & vbCrLf &
        "Cancel = do nothing",
        MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Question, "Reset Module")
    If result = MsgBoxResult.Cancel Then Exit Sub
    usePath = If(result = MsgBoxResult.Yes, companyFolderPath, folderPath)
Else
    If MsgBox(
        "Reset the '" & curCoreModule.ModuleInfo.ModuleCaption & "' module to default?" & vbCrLf & vbCrLf &
        "All tabs will be restored to defaults; tabs that aren't in the default set will be removed.",
        MsgBoxStyle.YesNoCancel Or MsgBoxStyle.Question, "Reset Module") <> MsgBoxResult.Yes Then
        Exit Sub
    End If
    usePath = folderPath
End If
User input Outcome
Yes (company defaults exist) usePath = companyFolderPath
No (company defaults exist) usePath = folderPath (Leahy original)
Yes (no company defaults) usePath = folderPath
No / Cancel Exit handler — no changes

After this phase, usePath is the single folder path to read defaults from.

Phase 3 — Apply changes

Collect the default tabs

Dim defaultFiles As New List(Of SaveAndShareFile)
For Each curFile As SaveAndShareFile In Instance.DefaultSaveAndShare.Tabs("")
    If curFile.FolderPath = usePath Then
        defaultFiles.Add(curFile)
    End If
Next

If defaultFiles.Count = 0 Then
    MsgBox("There are no default tabs registered for this module.", MsgBoxStyle.Information)
    Exit Sub
End If

defaultFiles is the list of tabs we want, in the order they appear in DefaultSaveAndShare. The order matters — it's used in step 3 to drive the final tab order.

Step 1 — Remove tabs not in the default set

Dim defaultNames As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)
For Each f As SaveAndShareFile In defaultFiles
    defaultNames.Add(f.FileName)
Next

Dim toRemove As New List(Of MyTabPage)
For Each tp As MyTabPage In tc.TabPages
    If Not defaultNames.Contains(tp.Text) Then
        toRemove.Add(tp)
    End If
Next
For Each tp As MyTabPage In toRemove
    tc.TabPages.Remove(tp)
    tp.Dispose()
Next

The HashSet(Of String) with OrdinalIgnoreCase makes the lookup O(1) and case-insensitive (matching the FileName.Equals patterns used elsewhere in the codebase).

The collect-then-remove pattern avoids modifying the collection during iteration.

Step 2 — Reset existing tabs / add missing ones

For i As Integer = 0 To defaultFiles.Count - 1
    Dim defFile As SaveAndShareFile = defaultFiles(i)

    Dim existing As MyTabPage = Nothing
    For Each tp As MyTabPage In tc.TabPages
        If String.Equals(tp.Text, defFile.FileName, StringComparison.OrdinalIgnoreCase) Then
            existing = tp
            Exit For
        End If
    Next

    If existing Is Nothing Then
        Dim newTab As New MyTabPage(True)
        tc.TabPages.Add(newTab)
        newTab.SerialData = PulseFileIO.DeserializeFromBuffer(
            defFile.SaveAndShareData.ToArray, PulseFileIO.SerializationFlags.UseCompression)
        newTab.LoadSettings()
    Else
        existing.SerialData = PulseFileIO.DeserializeFromBuffer(
            defFile.SaveAndShareData.ToArray, PulseFileIO.SerializationFlags.UseCompression)
        existing.LoadSettings()
    End If
Next

For each file in defaultFiles:

  • If a tab with that name exists locally → overwrite its SerialData with the deserialized default and call LoadSettings() to apply the change.
  • If no such tab exists → construct a new MyTabPage(True), add it, set SerialData, call LoadSettings().

After step 2 the tab control contains exactly the right set of tabs — but the order may still be wrong (existing tabs kept their positions, new tabs were appended).

Step 3 — Reorder

For targetIndex As Integer = 0 To defaultFiles.Count - 1
    Dim wantName As String = defaultFiles(targetIndex).FileName
    Dim foundIndex As Integer = -1
    For j As Integer = 0 To tc.TabPages.Count - 1
        If String.Equals(tc.TabPages(j).Text, wantName, StringComparison.OrdinalIgnoreCase) Then
            foundIndex = j
            Exit For
        End If
    Next
    If foundIndex >= 0 AndAlso foundIndex <> targetIndex Then
        tc.TabPages.Move(targetIndex, tc.TabPages(foundIndex))
    End If
Next

tc.PerformLayout()

Walks defaultFiles in order. For each one, finds where it currently lives in tc.TabPages and — if it's not already in the right slot — calls Move(targetIndex, page) to put it there.

MyTabControl.TabPages.Move(int, page) is the same DevExpress method used elsewhere in the codebase (e.g. FPulseTab_Popup.btnMoveLeft_ItemClick line 91).

PerformLayout() at the end forces the tab control to re-render with the new order.

Cursor + try/finally

The whole apply phase is wrapped:

Dim parentForm As Form = Activator.FindForm
If parentForm IsNot Nothing Then parentForm.Cursor = Cursors.WaitCursor

Try
    ' ... step 1, 2, 3 ...
Finally
    If parentForm IsNot Nothing Then parentForm.Cursor = Cursors.Default
End Try

Wait cursor during the work; restored even if anything throws.

Edge cases

Case Handling
Module has no entry in the Select Case mapping Info message, exit cleanly
Module is in the mapping but has no default files Info message, exit cleanly
User cancels at confirmation Exit cleanly, no changes
Tab name collision (case-insensitive) Existing tab is reset (not duplicated)
defaultFiles has duplicate names Step 2 finds the first match and resets it; step 3 may move it multiple times (idempotent — last move wins)
All current tabs are non-default Every current tab gets removed in step 1, every default gets created in step 2

Risk notes

  • Destructive on confirmation. Once the user clicks Yes, work begins immediately. There's no second confirmation.
  • No undo. Save-and-Share-Module beforehand if the current state is worth preserving.
  • Dispose() on removed tabs. Step 1 calls tp.Dispose(). This matches the disposal pattern in CCoreModule.Settings_DataLoadEnd, which similarly disposes tab pages before recreating them. If a tab was being focused-on at removal time, focus moves automatically to whatever page DevExpress chooses next.