Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
0389294
new DSPrythonNet3 project and PythonServices modified
ivaylo-matov Sep 3, 2025
b70f95d
Revert "new DSPrythonNet3 project and PythonServices modified"
ivaylo-matov Sep 29, 2025
acb9b38
Merge remote-tracking branch 'upstream/master' into DYN-9388-Net3-pac…
ivaylo-matov Sep 29, 2025
ee79b74
pythonnet3 loads as default
ivaylo-matov Sep 29, 2025
62af16c
resources
ivaylo-matov Sep 29, 2025
cdabfa3
notifications transfer
ivaylo-matov Sep 29, 2025
2dfc883
resources correction
ivaylo-matov Sep 29, 2025
c0f5720
cancel button removed
ivaylo-matov Sep 29, 2025
250ad77
resources updates to match new design
ivaylo-matov Sep 29, 2025
5c32588
python update bar relocated
ivaylo-matov Sep 30, 2025
68741ed
request to update banner removed and new design for engine dropdown (…
ivaylo-matov Sep 30, 2025
95c1660
python update bar relocated & request to update bar removed
ivaylo-matov Sep 30, 2025
a076326
Merge branch 'DYN-9388-Net3-package-does-not-load' of https://github.…
ivaylo-matov Sep 30, 2025
0058c33
for pr
ivaylo-matov Sep 30, 2025
e5f81c1
public api register
ivaylo-matov Sep 30, 2025
6434f92
Update check_file_version.ps1
ivaylo-matov Sep 30, 2025
7319898
Update check_file_version.ps1
ivaylo-matov Sep 30, 2025
4b9a297
Merge branch 'master' into DYN-9388-Net3-package-does-not-load
ivaylo-matov Oct 1, 2025
87879d7
disable migration assistant on python3 nodes
ivaylo-matov Oct 6, 2025
39002a2
comments response
ivaylo-matov Oct 6, 2025
b56779a
Merge branch 'master' into DYN-9388-Net3-package-does-not-load
ivaylo-matov Oct 6, 2025
f526113
missing tooltip
ivaylo-matov Oct 8, 2025
efb6ba7
dscpython removed
ivaylo-matov Oct 10, 2025
94ef70f
net3package from \extern removed
ivaylo-matov Oct 10, 2025
eb5f6a7
nuget in place
ivaylo-matov Oct 10, 2025
7a39d15
migrator working and \extern removed
ivaylo-matov Oct 11, 2025
76c30e9
unit tests
ivaylo-matov Oct 11, 2025
c676f4b
comments in #16582 addressed
ivaylo-matov Oct 11, 2025
468f488
nuget updated to 1.4.2
ivaylo-matov Oct 12, 2025
c10eddc
ShowCPythonNotifications correction
ivaylo-matov Oct 12, 2025
77b6621
tag resources
ivaylo-matov Oct 12, 2025
2a56e43
Merge branch 'master' into DYN-9388-Net3-package-does-not-load
ivaylo-matov Oct 12, 2025
d28afd9
Update check_file_version.ps1
ivaylo-matov Oct 13, 2025
6f8b452
Merge branch 'DYN-9388-Net3-package-does-not-load' of https://github.…
ivaylo-matov Oct 13, 2025
78b92be
Update DynamoCore.csproj
ivaylo-matov Oct 15, 2025
892e408
Update ScriptMigrator.cs
ivaylo-matov Oct 15, 2025
13c6f62
postpone setting default python engine
ivaylo-matov Oct 15, 2025
2272b1b
dependency test package zipped
ivaylo-matov Oct 15, 2025
d920ffc
Merge remote-tracking branch 'upstream/master' into DYN-9388-Net3-pac…
ivaylo-matov Oct 22, 2025
5dd8d66
DYN-9720: Trigger backup before saving a graph with Pythonnet3
ivaylo-matov Oct 22, 2025
1bbd955
upadate custom nodes - wip
ivaylo-matov Oct 22, 2025
a020a1d
backup notification updated with title and time stamp
ivaylo-matov Oct 22, 2025
07a6bdc
nester nodes - wip
ivaylo-matov Oct 24, 2025
f650507
toast closes on custom nodes closed
ivaylo-matov Oct 24, 2025
10d9ca0
wip
ivaylo-matov Oct 27, 2025
d5fe84d
Merge branch 'DYN-9720-plus-DYN-9719' of https://github.com/ivaylo-ma…
ivaylo-matov Oct 27, 2025
8d3cf49
option 1 and 2 woking
ivaylo-matov Oct 28, 2025
402c3a6
cleanup
ivaylo-matov Oct 28, 2025
7c6e2d6
for demo
ivaylo-matov Oct 31, 2025
a4003d0
toast message condensed
ivaylo-matov Oct 31, 2025
6ac9120
banners off after permanent upgrade
ivaylo-matov Oct 31, 2025
9940bf1
migration services
ivaylo-matov Nov 1, 2025
5ffec4d
automatic run fixed
ivaylo-matov Nov 4, 2025
e0dc695
cleanup
ivaylo-matov Nov 4, 2025
de9210f
temp
ivaylo-matov Nov 4, 2025
8bdfc83
dyf save works with replacing views
ivaylo-matov Nov 4, 2025
a237612
dyf save work with replacing engine only
ivaylo-matov Nov 5, 2025
77a30ae
backups
ivaylo-matov Nov 5, 2025
57ef999
Merge branch 'DYN-9720-plus-DYN-9719' of https://github.com/ivaylo-ma…
ivaylo-matov Nov 5, 2025
d94e49b
Merge remote-tracking branch 'upstream/master' into DYN-9720-plus-DYN…
ivaylo-matov Nov 5, 2025
c8e21bb
cleanup after merging with master
ivaylo-matov Nov 5, 2025
837bf70
cleanup
ivaylo-matov Nov 5, 2025
5d0a04a
Update src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
ivaylo-matov Nov 5, 2025
f1aae00
Update src/DynamoCore/Migration/PythonEngineUpgradeService.cs
ivaylo-matov Nov 5, 2025
2f84172
typo
ivaylo-matov Nov 5, 2025
ecb137b
bug fix
ivaylo-matov Nov 5, 2025
de88f75
save upgrade in try catch
ivaylo-matov Nov 7, 2025
90c95d7
response to DYN-9828 ensure toast always shows
ivaylo-matov Nov 7, 2025
a4978ed
Merge branch 'master' into DYN-9720-plus-DYN-9719
ivaylo-matov Nov 7, 2025
05d9fff
revised toast with hyperlink to backup folder
ivaylo-matov Nov 7, 2025
9107fde
benglin comments adressed
ivaylo-matov Nov 7, 2025
573d7ee
Merge remote-tracking branch 'upstream/master' into DYN-9720-plus-DYN…
ivaylo-matov Nov 7, 2025
f81847a
close toast from toast manager
ivaylo-matov Nov 7, 2025
a3ad7da
Merge branch 'DYN-9720-plus-DYN-9719' of https://github.com/ivaylo-ma…
ivaylo-matov Nov 7, 2025
831e287
Merge branch 'DYN-9720-revised-toast-with-hyperlink-to-backup-folder_…
ivaylo-matov Nov 7, 2025
879943c
toast shows hyperlink to backup folder
ivaylo-matov Nov 7, 2025
e8f836f
engine upgrade banned updated
ivaylo-matov Nov 10, 2025
869ba49
Merge branch 'master' into DYN-9720-plus-DYN-9719
ivaylo-matov Nov 10, 2025
498592b
reg APIs
ivaylo-matov Nov 10, 2025
776553c
notifications bug fixed
ivaylo-matov Nov 12, 2025
0748e6a
Merge branch 'master' into DYN-9720-plus-DYN-9719
zeusongit Nov 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 274 additions & 0 deletions src/DynamoCore/Migration/PythonEngineUpgradeService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Workspaces;
using Dynamo.Interfaces;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Dynamo.Models.Migration.Python
{
/// <summary>
/// Headless service that detects Python engine usage in a workspace,
/// performs temporary engine upgrades on nodes, and (optionally) commits
/// permanent upgrades for custom node workspaces.
/// </summary>
public sealed class PythonEngineUpgradeService
{
private readonly DynamoModel dynamoModel;
private readonly IPathManager pathManager;

public PythonEngineUpgradeService( DynamoModel dynamoModel, IPathManager pathManager)
{
this.dynamoModel = dynamoModel;
this.pathManager = pathManager;
}

/// <summary>
/// Custom node definition IDs temporarily migrated
/// </summary>
public readonly HashSet<Guid> TempMigratedCustomDefs = new HashSet<Guid>();

/// <summary>
/// Custom node definition IDs permanently migrated
/// </summary>
public readonly HashSet<Guid> PermMigratedCustomDefs = new HashSet<Guid>();

/// <summary>
/// Custom node workspaces touched during this session
/// </summary>
public readonly HashSet<WorkspaceModel> TouchedCustomWorkspaces = new HashSet<WorkspaceModel>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Both TouchedCustomWorkspaces and TempMigratedCustomDefs maintain identical information (just in different forms), which makes keeping them in sync challenging and error-prone. For example, it's unclear whether we need to remove TouchedCustomWorkspaces entries in CommitCustomNodeMigrationsOnSave after the loop, when entries are removed from TempMigratedCustomDefs.

Here's what I'd suggest to simplify their usage:

  1. Keep only the lightweight TempMigratedCustomDefs hash set, which stores only Guid values.

  2. When CommitCustomNodeMigrationsOnSave needs the corresponding WorkspaceModel, it can retrieve the heavyweight WorkspaceModel from CustomNodeManager using the Guid.

This way we only need to maintain one HashSet.


/// <summary>
/// Custom node definition IDs for which a toast/notice has already been shown
/// </summary>
public readonly HashSet<Guid> CustomToastShownDef = new HashSet<Guid>();

/// <summary>
/// Result of a simple scan: direct python nodes + custom node definitions that contain python.
/// </summary>
public sealed class Usage
{
public WorkspaceModel Workspace { get; }
public IEnumerable<NodeModel> DirectPythonNodes { get; }
public IEnumerable<Guid> CustomNodeDefIdsWithPython { get; }

internal Usage(WorkspaceModel workspace, IReadOnlyList<NodeModel> pyNodes, IReadOnlyList<Guid> customDefs)
{
Workspace = workspace;
DirectPythonNodes = pyNodes ?? Enumerable.Empty<NodeModel>();
CustomNodeDefIdsWithPython = customDefs ?? Enumerable.Empty<Guid>();
}
}

/// <summary>
/// Scan the workspace for python usage (direct) and one level of custom nodes.
/// </summary>
public Usage DetectPythonUsage(WorkspaceModel workspace, Func<NodeModel, bool> isPythonNode)
{
if (workspace == null) throw new ArgumentNullException(nameof(workspace));
if (isPythonNode == null) throw new ArgumentNullException(nameof(isPythonNode));

// Direct python nodes
var directNodes = workspace.Nodes.Where(isPythonNode).ToList();

// Custom nodes that contain python
var customDefIds = new HashSet<Guid>();
foreach (var func in workspace.Nodes.OfType<Dynamo.Graph.Nodes.CustomNodes.Function>())
{
var defId = func.Definition?.FunctionId ?? Guid.Empty;
if (defId == Guid.Empty) continue;

if (CustomNodeHasPython(defId, isPythonNode) || TempMigratedCustomDefs.Contains(defId))
{
customDefIds.Add(defId);
}
}

return new Usage( workspace, directNodes, customDefIds.ToList());
}

/// <summary>
/// Upgrade the engine for a set of python nodes in memory and return the count changed.
/// </summary>
public int UpgradeNodesInMemory(
IEnumerable<NodeModel> pyNodes,
WorkspaceModel workspace,
Action<NodeModel, WorkspaceModel> setEngine)
{
if (pyNodes is null) throw new ArgumentNullException(nameof(pyNodes));
if (workspace is null) throw new ArgumentNullException(nameof(workspace));
if (setEngine is null) throw new ArgumentNullException(nameof(setEngine));

var nodes = pyNodes.ToList();
nodes.ForEach(node => setEngine(node, workspace));
return nodes.Count;
}

/// <summary>
/// Commits migration changes for custom-node workspaces by editing the .dyf JSON in place:
/// switches Python nodes from CPython3 to PythonNet3 and saves a backup file
/// </summary>
public void CommitCustomNodeMigrationsOnSave()
{
foreach (var workspace in TouchedCustomWorkspaces.ToList())
{
try
{
if (!TryGetCustomIdAndPath(workspace, out var defId, out var dyfPath) || string.IsNullOrEmpty(dyfPath)) continue;

SaveCustomNodeBackup(workspace, dyfPath, PythonServices.PythonEngineManager.CPython3EngineName);

var upgraded = SwitchDyfPythonEngineInPlace(
dyfPath,
PythonServices.PythonEngineManager.CPython3EngineName,
PythonServices.PythonEngineManager.PythonNet3EngineName);

if (upgraded)
{
PermMigratedCustomDefs.Add(defId);
TempMigratedCustomDefs.Remove(defId);
}
}
catch (Exception ex)
{
this.dynamoModel?.Logger?.Log(ex);
}
}
}

/// <summary>
/// Build a backup file path for a .dyn or .dyf backup
/// </summary>
public string BuildDynBackupFilePath(WorkspaceModel workspace, string token)
{
if (workspace == null || pathManager == null) return null;
if (DynamoModel.IsTestMode) return null;

var backupDir = pathManager.BackupDirectory;
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");

string baseName = Path.GetFileNameWithoutExtension(workspace.FileName);
string ext = Path.GetExtension(workspace.FileName);

baseName = SanitizeFileName(baseName);
var fileName = $"{baseName}.{token}.{timestamp}{ext}";

return Path.Combine(backupDir, fileName);
}

/// <summary>
/// Save a .dyn backup of the given workspace with the given token.
/// </summary>
public string SaveGraphBackup(WorkspaceModel workspace, string token)
{
var path = BuildDynBackupFilePath(workspace, token);
if (string.IsNullOrEmpty(path)) return null;

try
{
workspace.Save(path, true);
return path;
}
catch (Exception ex)
{
this.dynamoModel?.Logger?.Log(ex);
return null;
}
}

/// <summary>
/// Save a .dyf backup of the given custom node workspace before engine upgrade.
/// </summary>
private string SaveCustomNodeBackup(WorkspaceModel workspace, string sourcePath, string token)
{
var backupPath = BuildDynBackupFilePath(workspace, token);
if (string.IsNullOrEmpty(backupPath)) return null;

try
{
File.Copy(sourcePath, backupPath);
return backupPath;
}
catch (Exception ex)
{
this.dynamoModel?.Logger?.Log(ex);
return null;
}
}

private bool SwitchDyfPythonEngineInPlace(string dyfPath, string oldEngName, string newEngName)
{
if (string.IsNullOrEmpty(dyfPath) || !File.Exists(dyfPath)) return false;

var root = JObject.Parse(File.ReadAllText(dyfPath));
var nodes = root["Nodes"] as JArray;
if (nodes == null || nodes.Count == 0) return false;

try
{
foreach (var n in nodes.OfType<JObject>())
{
var concrete = n.Value<string>("ConcreteType");
if (string.IsNullOrEmpty(concrete) || !concrete.StartsWith("PythonNodeModels", StringComparison.Ordinal)) continue;

var engine = n.Property("Engine", StringComparison.OrdinalIgnoreCase);
if (engine != null && string.Equals((string)engine.Value, oldEngName, StringComparison.Ordinal))
{
engine.Value = newEngName;
}
}

File.WriteAllText(dyfPath, root.ToString(Formatting.Indented));
return true;
}
catch (Exception ex)
{
this.dynamoModel?.Logger?.Log(ex);
return false;
}
}

private bool TryGetCustomIdAndPath(WorkspaceModel workspace, out Guid defId, out string dyfPath)
{
defId = Guid.Empty;
dyfPath = null;

if (workspace is CustomNodeWorkspaceModel cws)
{
defId = cws.CustomNodeId;
var cnm = dynamoModel?.CustomNodeManager;
if (cnm != null &&
cnm.NodeInfos.TryGetValue(defId, out var info) &&
!string.IsNullOrEmpty(info.Path))
{
dyfPath = info.Path;
}
return true;
}
return false;
}

private static string SanitizeFileName(string name)
{
var invalid = Path.GetInvalidFileNameChars();
return new string(name.Select(ch => invalid.Contains(ch) ? '_' : ch).ToArray());
}

private bool CustomNodeHasPython(Guid defId, Func<NodeModel, bool> isPythonNode)
{
if (dynamoModel?.CustomNodeManager == null) return false;

CustomNodeWorkspaceModel ws;
if (dynamoModel.CustomNodeManager.TryGetFunctionWorkspace(defId, DynamoModel.IsTestMode, out ws) && ws != null)
{
return ws.Nodes?.Any(isPythonNode) == true;
}

return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I see TryGetFunctionWorkspace calls being used in various places. For consistency and avoid repetition, what do you think of this approach?

// New method other components can use
internal ICustomNodeWorkspaceModel TryGetFunctionWorkspace(DynamoModel dynamoModel, Guid guid)
{
    ICustomNodeWorkspaceModel ws;
    var cnm = dynamoModel?.CustomNodeManager;

    if (cnm != null && cnm.TryGetFunctionWorkspace(guid, dynamoModel.IsTestMode, out ws))
    {
        return ws;
    }

    return null;
}

Then we can update CustomNodeHasPython to be this way:

private bool CustomNodeHasPython(Guid defId, Func<NodeModel, bool> isPythonNode)
{
    var cws = this.TryGetFunctionWorkspace(DynamoModel, defId) as CustomNodeWorkspaceModel;
    return cws?.Nodes != null && cws.Nodes.Any(isPythonNode);
}

Other callers can also be simplified, too.


}
}
9 changes: 9 additions & 0 deletions src/DynamoCoreWpf/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/DynamoCoreWpf/Properties/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4238,4 +4238,7 @@ To open this graph in Dynamo 3.3–3.6.x: Install the PythonNet package from the
<data name="OfflineStatusTooltip" xml:space="preserve">
<value>Not connected to the internet.</value>
</data>
<data name="ToastHyperlinkPathText" xml:space="preserve">
<value>here</value>
</data>
</root>
3 changes: 3 additions & 0 deletions src/DynamoCoreWpf/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4225,4 +4225,7 @@ To open this graph in Dynamo 3.3–3.6.x: Install the PythonNet package from the
<data name="OfflineStatusTooltip" xml:space="preserve">
<value>Not connected to the internet.</value>
</data>
<data name="ToastHyperlinkPathText" xml:space="preserve">
<value>here</value>
</data>
</root>
11 changes: 8 additions & 3 deletions src/DynamoCoreWpf/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2171,7 +2171,7 @@ Dynamo.ViewModels.DynamoViewModel.PublishNewPackageCommand.get -> Dynamo.UI.Comm
Dynamo.ViewModels.DynamoViewModel.PublishNewPackageCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.PublishSelectedNodesCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.PublishSelectedNodesCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.PythonEngineUpgradeToastRequested -> System.Action<string, bool>
Dynamo.ViewModels.DynamoViewModel.PythonEngineUpgradeToastRequested -> System.Action<string, bool, string>
Dynamo.ViewModels.DynamoViewModel.RecentFiles.get -> System.Collections.ObjectModel.ObservableCollection<string>
Dynamo.ViewModels.DynamoViewModel.RecentFiles.set -> void
Dynamo.ViewModels.DynamoViewModel.RedoCommand.get -> Dynamo.UI.Commands.DelegateCommand
Expand Down Expand Up @@ -2210,11 +2210,11 @@ Dynamo.ViewModels.DynamoViewModel.ScaleFactorLog.set -> void
Dynamo.ViewModels.DynamoViewModel.SelectAll(object parameter) -> void
Dynamo.ViewModels.DynamoViewModel.SelectAllCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.SelectAllCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.ShowPythonEngineUpgradeCanvasToast(string message, bool stayOpen = true) -> void
Dynamo.ViewModels.DynamoViewModel.ShowGraphPropertiesCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.ShowGraphPropertiesCommand.set -> void
Dynamo.ViewModels.DynamoViewModel.ToastManager.get -> Dynamo.Wpf.UI.ToastManager
Dynamo.ViewModels.DynamoViewModel.ToastManager.set -> void
Dynamo.ViewModels.DynamoViewModel.ShowPythonEngineUpgradeCanvasToast(string message, bool stayOpen = true, string filePath = null) -> void
Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubbles(object parameter) -> void
Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubblesCommand.get -> Dynamo.UI.Commands.DelegateCommand
Dynamo.ViewModels.DynamoViewModel.UnpinAllPreviewBubblesCommand.set -> void
Expand Down Expand Up @@ -3826,7 +3826,7 @@ Dynamo.Wpf.UI.MouseBehaviour.MouseY.get -> double
Dynamo.Wpf.UI.MouseBehaviour.MouseY.set -> void
Dynamo.Wpf.UI.ToastManager
Dynamo.Wpf.UI.ToastManager.CloseRealTimeInfoWindow() -> void
Dynamo.Wpf.UI.ToastManager.CreateRealTimeInfoWindow(string content, bool stayOpen = false, bool showHeader = false, string headerText = "", bool showHyperlink = false, string hyperlinkText = "", System.Uri hyperlinkUri = null) -> void
Dynamo.Wpf.UI.ToastManager.CreateRealTimeInfoWindow(string content, bool stayOpen = false, bool showHeader = false, string headerText = "", bool showHyperlink = false, string hyperlinkText = "", System.Uri hyperlinkUri = null, bool showFileLink = false, System.Uri fileLinkUri = null) -> void
Dynamo.Wpf.UI.ToastManager.ToastManager(System.Windows.UIElement rootElement) -> void
Dynamo.Wpf.UI.VisualConfigurations
Dynamo.Wpf.Utilities.CompactBubbleHandler
Expand Down Expand Up @@ -4320,6 +4320,8 @@ Dynamo.Wpf.Views.GuidedTour.RatingControl.RatingControl() -> void
Dynamo.Wpf.Views.GuidedTour.RatingControl.Value.get -> int
Dynamo.Wpf.Views.GuidedTour.RatingControl.Value.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.FileLinkUri.get -> System.Uri
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.FileLinkUri.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.HeaderContent.get -> string
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.HeaderContent.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.HyperlinkText.get -> string
Expand All @@ -4328,6 +4330,8 @@ Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.HyperlinkUri.get -> System.Uri
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.HyperlinkUri.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.InitializeComponent() -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.RealTimeInfoWindow() -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.ShowFileLink.get -> bool
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.ShowFileLink.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.ShowHeader.get -> bool
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.ShowHeader.set -> void
Dynamo.Wpf.Views.GuidedTour.RealTimeInfoWindow.ShowHyperlink.get -> bool
Expand Down Expand Up @@ -5744,6 +5748,7 @@ static Dynamo.Wpf.Properties.Resources.TermsOfUseDeclineButton.get -> string
static Dynamo.Wpf.Properties.Resources.TermsOfUseViewTitle.get -> string
static Dynamo.Wpf.Properties.Resources.TitleFileInUse.get -> string
static Dynamo.Wpf.Properties.Resources.TitlePackageTargetOtherHost.get -> string
static Dynamo.Wpf.Properties.Resources.ToastHyperlinkPathText.get -> string
static Dynamo.Wpf.Properties.Resources.TooltipCurrentIndex.get -> string
static Dynamo.Wpf.Properties.Resources.TourLabelProgressText.get -> string
static Dynamo.Wpf.Properties.Resources.TrustedLocationNotAccessible.get -> string
Expand Down
Loading
Loading