Skip to content

Give Download the Explorer folder dialog instead of the old tree - #2

Closed
skfd wants to merge 4 commits into
masterfrom
fix/download-folder-picker
Closed

Give Download the Explorer folder dialog instead of the old tree#2
skfd wants to merge 4 commits into
masterfrom
fix/download-folder-picker

Conversation

@skfd

@skfd skfd commented Aug 15, 2026

Copy link
Copy Markdown
Member

Review before any of this goes upstream. Only the first commit is meant for
jamesnovak/XrmDocumentTemplateManager; the other two are local tooling.

The change

PerformDownloadTemplates opened FolderBrowserDialog with RootFolder = MyComputer.
On .NET Framework that is still the SHBrowseForFolder tree -- no path bar, no search, no
typing or pasting a path -- and MyComputer rooted it at This PC, so every download started
the walk from the top. There is no AutoUpgradeEnabled on net4x, so the Explorer dialog has
to be asked for directly through IFileOpenDialog with FOS_PICKFOLDERS.

Download was the only place left on the old dialog. The three upload paths already use
OpenFileDialog, which is the modern dialog on net4x anyway.

Helper/FolderPicker.cs is ported from the Plugin Documenter tool, namespace change only.
It falls back to FolderBrowserDialog if the shell call fails, so nothing is lost where the
COM dialog is unavailable. It also reopens on the folder used for the last download, and the
dialog is now disposed, which the old code never did.

Before and after

Both in docs/shots/, produced by tests/dialog-shots.ps1:

dialog-before.png the old tree, rooted at This PC, showing only drives
dialog-after.png breadcrumbs, search, navigation pane, typeable Folder field

The "after" shot is FolderPicker.Pick reflected out of the built assembly, so it is the
shipping code rather than a mock-up.

Commits

  • 82aa266 Give Download the Explorer folder dialog instead of the old tree -- the fix.
    Self-contained and the only one that should go upstream. Touches
    DocTemplateManagerControl.cs, adds Helper/FolderPicker.cs, one <Compile> entry.
  • 2e6dccf Build and run the tool the way the sibling tools do -- build.ps1 and
    xtb.ps1, matching the other xrm-* tools. Local only.
  • 9e7ca4b Seed mock templates and photograph the two folder dialogs -- tests/seed.ps1,
    tests/dialog-shots.ps1, tests/ui.ps1, the screenshots. Local only.

Things worth a look while reviewing

  • Retargeted v4.7 to v4.8 in 2e6dccf. The 4.7 targeting pack is no longer
    installable, so the project would not build as it stood. Nothing here needs 4.7 and
    XrmToolBox itself is built against 4.6.2 -- but it is a change to the project file, so it
    should probably be left out of anything sent upstream.
  • dotnet build cannot compile this project at all. The .resx files hold images and
    the SDK build refuses them (MSB3823/MSB3822) unless the project takes on a
    System.Resources.Extensions dependency it does not have today. build.ps1 uses the full
    MSBuild instead, and xtb.ps1 hands the result to Start-XtbSandbox as -DllPath -NoBuild.
    That -NoBuild switch is now on main in
    xrmtoolbox-sandbox
    (2232af7), but it has not been published to the PowerShell Gallery yet -- the
    installed module is still 0.1.0, so xtb.ps1 needs a PSModulePath pointing at that repo
    until a release goes out.
  • tests/ui.ps1 is not reliable and its header says so. XrmToolBox rebuilds its
    automation tree while the tab opens, so elements go stale between being found and being
    used. It is kept only because it is the one way to see the seeded data in the tool. The
    dialog shots deliberately do not depend on it.
  • Seeding is fiddlier than it looks -- see the commit message on 9e7ca4b.
    associatedentitytypecode is a string holding the table's logical name despite the name,
    and setting it is not enough because the server derives the association from the template
    content's customXml and overwrites the attribute from it.

Related

Found while testing this: #1, where a template whose associated table cannot be resolved
crashes XrmToolBox outright. Fixed separately in #3.

skfd and others added 3 commits August 14, 2026 15:36
FolderBrowserDialog on .NET Framework is still the SHBrowseForFolder tree: no
path bar, no search, no typing or pasting a path. There is no AutoUpgradeEnabled
to set on net4x, so the Explorer dialog has to be asked for directly through
IFileOpenDialog with FOS_PICKFOLDERS. RootFolder = MyComputer made it worse by
rooting the tree at This PC, so every download started the walk from the top.

Download was the only place left on the old dialog; the three upload paths
already use OpenFileDialog, which is the modern dialog on net4x anyway.

FolderPicker falls back to FolderBrowserDialog if the shell call fails, so
nothing is lost where the COM dialog is unavailable. It also reopens on the
folder used for the last download, and the dialog is now disposed, which the
old code never did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
build.ps1 and xtb.ps1 mirror the other xrm-* tools, so this fork is driven the
same way: build.ps1 drops the dll into the XrmToolBox in Downloads, xtb.ps1
brings up a throwaway XrmToolBox holding nothing but this tool, pointed at the
active pac organization.

Two things had to differ, because this is the non-SDK, packages.config project
inherited from upstream rather than a project written here. dotnet build cannot
compile it at all -- the .resx files hold images, and the SDK build refuses
those unless the project takes on a System.Resources.Extensions dependency
upstream does not have -- so build.ps1 uses the full MSBuild and hands the
result to Start-XtbSandbox as -DllPath -NoBuild. And the dll lands straight in
bin\Debug with no target framework folder, which -DllPath has to spell out.

Retargeted to v4.8 because the 4.7 targeting pack is no longer installable and
nothing here needs 4.7; XrmToolBox itself is built against 4.6.2.

test_doc.docx is the one from the X-Ray tool, put on the clipboard at launch
because uploading needs a local .docx and that is the thing you always hunt for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The download path could not be shown working at all: the test organization has
no document templates, so the grid is empty and Download stays disabled. seed.ps1
creates six against tables the organization actually has, and takes the names
from the tables so the grid reads like a real system rather than "test 1, 2, 3".

Three things about that were not guessable. associatedentitytypecode reads like
an integer type code but is a string holding the table's logical name. Setting it
is not enough either, because the server takes the association from the template
content's customXml and overwrites the attribute from that, so seed.ps1 rebinds
the customXml of test_doc.docx per table rather than uploading it unchanged --
otherwise every record lands as "none" with no formatted value, and the tool
throws "Unable to load formatted value for associatedentitytypecode" and takes
XrmToolBox down with it. And the record has to be built in C#, because assigning
through Entity's indexer from PowerShell stores a PSObject the WCF serializer
then refuses.

dialog-shots.ps1 opens each dialog directly instead of driving XrmToolBox: the
old five lines for the before, FolderPicker out of the built assembly for the
after. It is also the one place the two PrintWindow flags are documented -- the
modern dialog needs PW_RENDERFULLCONTENT and comes out blank without it, while
the old dialog's tree is a child window that only appears under flag 0.

ui.ps1 photographs the grid. It is left in as the only way to see the seeded data
in the tool, but it is not reliable, and its header says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 15, 2026 00:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the Download destination folder selection by replacing the legacy .NET Framework FolderBrowserDialog tree with an Explorer-style folder picker (IFileOpenDialog + FOS_PICKFOLDERS). It also includes local build/sandbox tooling and test scripts to seed data and capture UI/dialog screenshots.

Changes:

  • Replace Download’s folder selection UI with an Explorer-style folder picker, reopening at the last-used download folder.
  • Add Helper/FolderPicker.cs (COM-based folder picker with legacy fallback) and wire it into the project.
  • Add local scripts for build/sandbox runs and for seeding + photographing UI/dialog behavior; update .gitignore for local artifacts.

Reviewed changes

Copilot reviewed 8 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
DocTemplateManagerControl.cs Switch Download destination selection to FolderPicker.Pick and remember last download folder.
Helper/FolderPicker.cs New Explorer-style folder picker via IFileOpenDialog, with fallback to FolderBrowserDialog.
Futurez.Xrm.Tools.DocTemplateManager.csproj Retarget to .NET Framework 4.8 and include the new FolderPicker compile item.
build.ps1 New local build/deploy script using full MSBuild + nuget restore.
xtb.ps1 New local script to build and launch a private XrmToolBox sandbox instance.
tests/seed.ps1 New script to seed mock templates into an org for manual/E2E validation.
tests/ui.ps1 New UIAutomation-based script to capture screenshots of the tool’s grid state.
tests/dialog-shots.ps1 New script to capture “before vs after” folder dialog screenshots via PrintWindow.
.gitignore Ignore local tooling artifacts (.xtb/, .claude/, .nuget-apikey).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Helper/FolderPicker.cs
Comment on lines +48 to +52
var hwnd = owner != null ? owner.Handle : IntPtr.Zero;
if (dialog.Show(hwnd) != 0)
{
return null; // cancelled
}
Comment thread Helper/FolderPicker.cs
Comment on lines +41 to +45
object item;
if (SHCreateItemFromParsingName(start, IntPtr.Zero, typeof(IShellItem).GUID, out item) == 0)
{
dialog.SetFolder((IShellItem)item);
}
Comment thread build.ps1
& $msbuild $solution /t:Build /p:Configuration=Debug /p:Platform="Any CPU" /v:minimal /nologo
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

$pluginDir = "C:\Users\kk\Downloads\XrmToolbox\Plugins"
<RootNamespace>Futurez.Xrm.Tools</RootNamespace>
<AssemblyName>Futurez.Xrm.Tools.DocTemplateManager</AssemblyName>
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
Reproducing that crash needs a template whose associated table cannot be resolved,
and the ordinary seeding path deliberately produces the opposite: it rebinds the
content customXml to a table the organization has, so the association resolves.

-Broken uploads test_doc.docx unrebound, still naming cr543_opportunity. That is
what moving a template between organizations does, and it lands as 'none' with no
formatted value, which is the state #1 is
about. Run it against a build without that fix and XrmToolBox terminates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@skfd skfd closed this Aug 16, 2026
@skfd
skfd deleted the fix/download-folder-picker branch August 16, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants