Skip to content

DYN-10717 Fix the Save/Save As menu items hardcoded - #17255

Open
edwin-vasquez-ucaldas wants to merge 20 commits into
masterfrom
DYN-10717_Fix_disabled_Save_and_Save_As_options
Open

DYN-10717 Fix the Save/Save As menu items hardcoded#17255
edwin-vasquez-ucaldas wants to merge 20 commits into
masterfrom
DYN-10717_Fix_disabled_Save_and_Save_As_options

Conversation

@edwin-vasquez-ucaldas

@edwin-vasquez-ucaldas edwin-vasquez-ucaldas commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Purpose

The Save/Save As menu items are hardcoded IsEnabled="False" in XAML, and no code path re-enables them for freshly created (as opposed to opened) workspaces.

  • The MenuItems don't use CanExecute at all in DynamoView.xaml. IsEnabled="False" is a literal, not a binding — WPF never re-derives it from the command's CanExecute (which, per DynamoViewModel.cs:3248 and :3372, always returns true anyway).

  • The only two things that flip IsEnabled back to true are event handlers in DynamoView.xaml.cs:
    OnWorkspaceOpened — wired to Model.WorkspaceOpened += OnWorkspaceOpened
    DynamoViewModel_RequestEnableShortcutBarItems — wired to RequestEnableShortcutBarItems

  • WorkspaceOpened only fires from OpenWorkspace() (DynamoModel.cs) for example opening an existing file. It does not fire from AddHomeWorkspace(), which is what creates the blank/default workspace at Dynamo startup, and is also the pattern used when a tab is closed and a fresh empty workspace takes its place.

Ctrl+S bypasses the bug because KeyBinding in XAML only checks Command.CanExecute (hardcoded true), never MenuItem.IsEnabled, completely independent mechanisms bound to the same command.

Before fix
DYN-10717-before

After fix
DYN-10717-after

Declarations

Check these if you believe they are true

Release Notes

Remove the hardcoded IsEnabled="False" and drive enablement through a real CanExecute predicate, removing the redundant imperative toggle mechanism entirely. It fixes the whole class of bug rather than patching another missed call site.

Reviewers

@jasonstratton @RobertGlobant20

@jnealb

Copilot AI lite review requested due to automatic review settings July 29, 2026 00:01

@github-actions github-actions Bot 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.

See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10717

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 addresses a WPF UI bug in DynamoCoreWpf where the Save / Save As menu items were hardcoded as disabled in DynamoView.xaml, leaving them unusable for newly created (unsaved) workspaces (while keyboard shortcuts still worked via CanExecute).

Changes:

  • Removed hardcoded IsEnabled="False" from the Save / Save As MenuItems in DynamoView.xaml.
  • Removed code-behind logic that imperatively re-enabled those menu items when certain events fired.
  • Trimmed trailing whitespace in a generated XML documentation file.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs Removes imperative enablement of Save/Save As menu items from event handlers (but currently leaves them out of the broader enable/disable flow).
src/DynamoCoreWpf/Views/Core/DynamoView.xaml Removes hardcoded disabled state for Save/Save As menu items so WPF can derive enablement from other mechanisms.
doc/distrib/xml/en-US/DSCoreNodes.xml Whitespace-only cleanup in XML documentation output.
Comments suppressed due to low confidence (1)

src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs:472

  • OnWorkspaceOpened still re-enables Export and the shortcut bar Save button, but it no longer re-enables the Save/Save As menu items. If those menu items were disabled via OnEnableShortcutBarItems(false) (e.g., when Start Page is shown), opening a workspace may not restore them. Re-enabling them here keeps behavior consistent with the other UI elements this handler restores.
        private void OnWorkspaceOpened(WorkspaceModel workspace)
        {
            if (!(exportMenu is null))
            {
                exportMenu.IsEnabled = true;
            }

Comment thread src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs
Copilot AI review requested due to automatic review settings July 29, 2026 01:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

test/DynamoCoreWpfTests/DynamoViewTests.cs:162

  • This test flips the global static DynamoModel.IsTestMode but doesn’t guarantee it’s restored if an assertion fails, which can cascade into unrelated failures in later tests (TearDown doesn’t reset IsTestMode). Capture the original value and restore it in a finally block.
            DynamoModel.IsTestMode = false;
            ViewModel.CloseHomeWorkspaceCommand.Execute(null);
            DynamoModel.IsTestMode = true;

Copilot AI review requested due to automatic review settings July 29, 2026 03:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/DynamoCoreWpf/Views/Core/DynamoView.xaml.cs:440

  • Save/Save As MenuItems are no longer disabled when DynamoViewModel raises RequestEnableShortcutBarItems(false) (the handler now only disables export/shortcut bar). Since the Save commands' CanExecute predicates are currently hard-coded to always return true (DynamoViewModel.cs:3248, 3372) and Dynamo.UI.Commands.DelegateCommand does not auto-requery, the Save menu items will remain enabled even in UI-lock states like Guided Tour start (GuidesManager.cs:166). Consider moving the enable/disable logic into the commands' CanExecute (e.g., return !GuideFlowEvents.IsAnyGuideActive) and calling RaiseCanExecuteChanged when that state toggles, so both menu items and keybindings behave consistently.
        private void DynamoViewModel_RequestEnableShortcutBarItems(bool enable)
        {
            if (!(exportMenu is null))
            {
                exportMenu.IsEnabled = enable;

src/DynamoCoreWpf/Views/Core/DynamoView.xaml:358

  • The hardcoded IsEnabled="False" removal makes Save/Save As enabled by default, but the PR description/release note says enablement should be driven by a real CanExecute predicate. Currently both CanShowSaveDialogIfNeededAndSaveResultCommand and CanShowSaveDialogAndSaveResult always return true (DynamoViewModel.cs:3248, 3372), so enablement is effectively unconditional and won’t respect any intentional UI-disable state (e.g., Guided Tour). Implementing a state-based CanExecute (and raising CanExecuteChanged when it changes) would align behavior with the PR intent.
                        <MenuItem Name="saveThisButton"
                                  Command="{Binding ShowSaveDialogIfNeededAndSaveResultCommand}"
                                  Header="{x:Static p:Resources.DynamoViewFileMenuSave}"
                                  InputGestureText="Ctrl + S" />
                        <MenuItem Name="saveButton"

…m AddHomeWorkspace() and the tab-close-creates-new-workspace path too
Comment thread doc/distrib/xml/en-US/DSCoreNodes.xml
Comment thread src/DynamoCoreWpf/Views/Core/DynamoView.xaml
…red CanExecute, gated on guided tour, Start Page, and workspace dirty state
Copilot AI review requested due to automatic review settings July 30, 2026 20:00
Copilot AI previously approved these changes Jul 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Ready to approve

The changes consistently centralize Save/Save As enablement in CanExecute, remove conflicting imperative UI toggles, and include targeted regression tests for the reported scenarios.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 6/7 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@jasonstratton jasonstratton removed this from the 4.2 milestone Jul 30, 2026
Copilot AI review requested due to automatic review settings July 30, 2026 20:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

New code introduces a couple of avoidable null-reference risks by invoking RaiseCanExecuteChanged() without null-checks on public settable command properties.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Comments suppressed due to low confidence (2)

src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs:3302

  • SetGuidedTourActive calls RaiseCanExecuteChanged() on the Save/Save As commands without null-checks. These commands are public settable properties, so a null assignment (by external code or future changes) would cause a crash when a guided tour starts/ends. Use null-conditional invocations to keep this gating method robust.
        internal void SetGuidedTourActive(bool isActive)
        {
            isGuidedTourActive = isActive;
            ShowSaveDialogIfNeededAndSaveResultCommand.RaiseCanExecuteChanged();
            ShowSaveDialogAndSaveResultCommand.RaiseCanExecuteChanged();

src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs:1338

  • SaveCommandsTrackedWorkspace_PropertyChanged calls ShowSaveDialogIfNeededAndSaveResultCommand.RaiseCanExecuteChanged() without a null-check, but ShowSaveDialogIfNeededAndSaveResultCommand is a public settable property (PublicAPI.Shipped). If it is ever unset (e.g., by external consumers or during future refactors), this handler will throw and potentially break workspace switching / dirty tracking. Use a null-conditional invocation here to keep the handler resilient.

This issue also appears on line 3298 of the same file.

        private void SaveCommandsTrackedWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(WorkspaceModel.HasUnsavedChanges))
                ShowSaveDialogIfNeededAndSaveResultCommand.RaiseCanExecuteChanged();
  • Files reviewed: 6/6 changed files
  • Comments generated: 0 new
  • Review effort level: Low

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

@RobertGlobant20

Copy link
Copy Markdown
Contributor

@edwin-vasquez-ucaldas seems that there are two regresssions also update the branch
image

Copilot AI review requested due to automatic review settings July 30, 2026 22:45
Remove or correct this assertion.
Refactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed.
Check failure: Offload the code that's conditional on this type test to the appropriate subclass and remove the condition.
Copilot AI review requested due to automatic review settings August 5, 2026 21:27
Comment thread src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs Fixed
Comment thread src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs Fixed
Comment thread test/DynamoCoreTests/CoreTests.cs Dismissed
Comment thread test/DynamoCoreTests/CoreTests.cs Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Human review recommended

It changes core Undo/Redo and dirty-state semantics as well as WPF command enablement behavior, which is subtle and warrants final human validation despite strong regression test coverage.

Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Change return type to 'void'; not a single caller uses the returned value.
Refactor this method to reduce its Cognitive Complexity from 31 to the 15 allowed.
Copilot AI review requested due to automatic review settings August 6, 2026 00:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

One of the newly added selection/undo tests sets HasUnsavedChanges = false without updating the saved undo depth, which can cause Undo/Redo to re-mark the workspace dirty and fail the intended regression scenario.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (2)

test/DynamoCoreTests/Models/DynamoModelCommandsTest.cs:241

  • Setting HasUnsavedChanges to false here does not update the workspace's saved undo depth (savedUndoDepth), so subsequent Undo/Redo operations can re-mark the workspace as dirty even though the test intends a clean/saved baseline. Use MarkAsSaved() to keep the dirty flag and saved undo depth consistent.
            CurrentDynamoModel.CurrentWorkspace.HasUnsavedChanges = false;

test/DynamoCoreTests/Models/DynamoModelCommandsTest.cs:210

  • This test manually clears HasUnsavedChanges, but with the new savedUndoDepth tracking, a plain assignment can leave the workspace in an inconsistent 'clean' state relative to the undo history. Prefer MarkAsSaved() so later Undo/Redo behavior in this test (or future edits) stays consistent with real save flows.

This issue also appears on line 241 of the same file.

            CurrentDynamoModel.CurrentWorkspace.HasUnsavedChanges = false;
  • Files reviewed: 17/17 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs Fixed
Copilot AI review requested due to automatic review settings August 6, 2026 01:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Dirty-state recalculation on Undo/Redo can clear HasUnsavedChanges for persisted changes that are not represented in the saved-state-affecting undo depth, risking incorrect Save gating.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/DynamoCore/Graph/Workspaces/UndoRedo.cs
… HasUnsavedChanges purely from SavedStateAffectingUndoDepth.
Copilot AI review requested due to automatic review settings August 6, 2026 02:12
Comment thread test/DynamoCoreTests/CoreTests.cs Dismissed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new independentDirtyFlag wiring can cause undo-tracked edits to permanently latch the dirty state, preventing Undo back-to-saved from ever returning the workspace to “clean” in some common flows.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs Outdated
Copilot AI review requested due to automatic review settings August 6, 2026 05:07
Copilot AI dismissed their stale review, a newer Copilot review was requested August 6, 2026 05:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Ready to approve

The changes consistently align menu/toolbar/hotkey enablement with real CanExecute + corrected dirty tracking, and the PR includes targeted regression tests for the key failure modes.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details
  • Files reviewed: 25/25 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Copilot AI review requested due to automatic review settings August 6, 2026 05:34
@sonarqubecloud

sonarqubecloud Bot commented Aug 6, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

There are confirmed edge cases where save/dirty-state synchronization may not update correctly (guided-tour active state changes without events and untracked dirty setters that can be cleared by undo-depth logic).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (1)

src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs:906

  • Save enablement now relies on GuidedTourStart/GuidedTourFinish to trigger NotifySaveCommandsChanged(), but GuideFlowEvents.IsAnyGuideActive can be toggled without raising those events (e.g. GuidesManager.ContinueTourButton_Click sets GuideFlowEvents.IsAnyGuideActive = true at src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs:288). In that flow, CanExecute will return false but menu/toolbar enable state may not refresh, leaving Save/Save As appearing enabled while a tour is active.

Consider centralizing guided-tour active state changes behind an event that is always raised (e.g. make the IsAnyGuideActive setter raise a state-changed event, or update GuidesManager to call GuideFlowEvents.OnGuidedTourStart/Finish instead of setting IsAnyGuideActive directly) and have DynamoViewModel subscribe to that signal.

            TrackWorkspaceForSaveCommands(model.CurrentWorkspace);
            GuideFlowEvents.GuidedTourStart += OnGuidedTourStateChanged;
            GuideFlowEvents.GuidedTourFinish += OnGuidedTourStateChanged;
  • Files reviewed: 25/25 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +107 to +112
private void UpdateHasUnsavedChangesFromSavedStateAffectingDepth()
{
// OR in independentDirtyFlag rather than overwriting: the undo stack being back
// at its saved position only means undo-tracked content is clean again
bool undoDepthIndicatesUnsaved = undoRecorder.SavedStateAffectingUndoDepth != savedUndoDepth;
HasUnsavedChanges = undoDepthIndicatesUnsaved || independentDirtyFlag;
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.

4 participants