diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index 9622724..5d5ad4d 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -4,11 +4,19 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:TuneUp" + xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf" xmlns:componentmodel="clr-namespace:System.ComponentModel;assembly=WindowsBase" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Width="500" Height="100"> - + + + + + + + + @@ -27,11 +35,59 @@ + + - @@ -185,12 +246,12 @@ - @@ -200,7 +261,6 @@ - diff --git a/TuneUp/TuneUpWindow.xaml.cs b/TuneUp/TuneUpWindow.xaml.cs index fc096c9..1fd729e 100644 --- a/TuneUp/TuneUpWindow.xaml.cs +++ b/TuneUp/TuneUpWindow.xaml.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -117,6 +118,34 @@ private void RecomputeGraph_Click(object sender, RoutedEventArgs e) (NodeAnalysisTable.DataContext as TuneUpWindowViewModel).ResetProfiling(); } + /// + /// Handles the sorting event for the NodeAnalysisTable DataGrid. + /// Updates the SortingOrder property in the view model based on the column header clicked by the user. + /// + private void NodeAnalysisTable_Sorting(object sender, DataGridSortingEventArgs e) + { + var viewModel = NodeAnalysisTable.DataContext as TuneUpWindowViewModel; + if (viewModel != null) + { + viewModel.SortingOrder = e.Column.Header switch + { + "#" => "number", + "Name" => "name", + "Execution Time (ms)" => "time", + _ => viewModel.SortingOrder + }; + + // Set the sorting direction of the datagrid column + e.Column.SortDirection = viewModel.SortDirection == ListSortDirection.Descending + ? ListSortDirection.Descending + : ListSortDirection.Ascending; + + // Apply custom sorting to ensure total times are at the bottom + viewModel.ApplySorting(); + e.Handled = true; + } + } + private void ExportTimes_Click(object sender, RoutedEventArgs e) { (NodeAnalysisTable.DataContext as TuneUpWindowViewModel).ExportToCsv(); diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index 42e17ca..5a4ace5 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -28,11 +28,17 @@ public enum ProfiledNodeState [Display(Name = "Executed On Current Run")] ExecutedOnCurrentRun = 1, + [Display(Name = "Executed On Current Run")] + ExecutedOnCurrentRunTotal = 2, + [Display(Name = "Executed On Previous Run")] - ExecutedOnPreviousRun = 2, + ExecutedOnPreviousRun = 3, + + [Display(Name = "Executed On Previous Run")] + ExecutedOnPreviousRunTotal = 4, [Display(Name = "Not Executed")] - NotExecuted = 3, + NotExecuted = 5, } /// @@ -52,6 +58,9 @@ public class TuneUpWindowViewModel : NotificationObject, IDisposable private Dictionary nodeDictionary = new Dictionary(); private SynchronizationContext uiContext; private bool isTuneUpChecked = false; + private ListSortDirection sortDirection; + private string sortingOrder; + /// /// Name of the row to display current execution time @@ -96,10 +105,48 @@ private HomeWorkspaceModel CurrentWorkspace } } } + + /// + /// Gets or sets the sorting order and toggles the sort direction. + /// + public string SortingOrder + { + get => sortingOrder; + set + { + if (sortingOrder != value) + { + sortingOrder = value; + SortDirection = ListSortDirection.Ascending; + } + else + { + SortDirection = SortDirection == ListSortDirection.Ascending + ? ListSortDirection.Descending + : ListSortDirection.Ascending; + } + } + } + + /// + /// Gets or sets the sort direction and raises property change notification if the value changes. + /// + public ListSortDirection SortDirection + { + get => sortDirection; + set + { + if (sortDirection != value) + { + sortDirection = value; + } + } + } + #endregion #region Public Properties - + /// /// Is the recomputeAll button enabled in the UI. Users should not be able to force a /// reset of the engine and re-execution of the graph if one is still ongoing. This causes...trouble. @@ -294,14 +341,8 @@ private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Mod ProfiledNodesCollection.Dispatcher.Invoke(() => { - ProfiledNodesCollection.SortDescriptions.Clear(); - // Sort nodes into execution group - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); - - // Sort nodes into execution order and make sure Total execution time is always bottom - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), ListSortDirection.Descending)); - if (ProfiledNodesCollection.View != null) - ProfiledNodesCollection.View.Refresh(); + ApplySorting(); + ProfiledNodesCollection.View.Refresh(); }); } @@ -321,13 +362,33 @@ private void UpdateExecutionTime() var totalSpanExecuted = new TimeSpan(ProfiledNodes.Where(n => n.WasExecutedOnLastRun).Sum(r => r.ExecutionTime.Ticks)); var totalSpanUnexecuted = new TimeSpan(ProfiledNodes.Where(n => !n.WasExecutedOnLastRun).Sum(r => r.ExecutionTime.Ticks)); ProfiledNodes.Add(new ProfiledNodeViewModel( - CurrentExecutionString, totalSpanExecuted, ProfiledNodeState.ExecutedOnCurrentRun)); + CurrentExecutionString, totalSpanExecuted, ProfiledNodeState.ExecutedOnCurrentRunTotal)); ProfiledNodes.Add(new ProfiledNodeViewModel( - PreviousExecutionString, totalSpanUnexecuted, ProfiledNodeState.ExecutedOnPreviousRun)); + PreviousExecutionString, totalSpanUnexecuted, ProfiledNodeState.ExecutedOnPreviousRunTotal)); }, null); RaisePropertyChanged(nameof(TotalGraphExecutiontime)); } + /// + /// Applies the sorting logic to the ProfiledNodesCollection. + /// + public void ApplySorting() + { + ProfiledNodesCollection.SortDescriptions.Clear(); + + // Sort nodes into execution group + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); + + // Sort nodes into execution order and make sure Total execution time is always bottom + var sortDescription = sortingOrder switch + { + "time" => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection), + "name" => new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection), + _ => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection), + }; + ProfiledNodesCollection.SortDescriptions.Add(sortDescription); + } + internal void OnNodeExecutionBegin(NodeModel nm) { var profiledNode = nodeDictionary[nm.GUID];