Index: src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeView.cs =================================================================== diff -u -rf1713196b96f96ed637286a1d8c6236a133268ae -r2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb --- src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeView.cs (.../TreeView.cs) (revision f1713196b96f96ed637286a1d8c6236a133268ae) +++ src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeView.cs (.../TreeView.cs) (revision 2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb) @@ -213,6 +213,16 @@ return controller.CanRenameNode(SelectedNode); } + public void EnableDataEventListeners() + { + controller.EnableDataEventListeners(); + } + + public void DisableDataEventListeners() + { + controller.DisableDataEventListeners(); + } + public void EnsureVisible(object item) { } public ITreeNodePresenter GetTreeViewNodePresenter([NotNull] object nodeData, ITreeNode node) @@ -401,6 +411,15 @@ controller.OnTreeViewHandleCreated(); } + protected override void OnHandleDestroyed(EventArgs e) + { + if (controller != null) + { + controller.OnTreeViewHandleDestroyed(); + } + base.OnHandleDestroyed(e); + } + /// /// Custom drawing. /// @@ -423,6 +442,8 @@ { if (disposing && controller != null) { + controller.OnTreeViewHandleDestroyed(); + controller.Dispose(); controller = null; } @@ -711,7 +732,9 @@ if (node.IsOnCheckBox(point)) { + DisableDataEventListeners(); node.Checked = !node.Checked; + EnableDataEventListeners(); } if (node.IsOnExpandButton(point)) Index: src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeViewController.cs =================================================================== diff -u -r0f4f3090613131cc878aeff213dd14e7658e0a7b -r2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb --- src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeViewController.cs (.../TreeViewController.cs) (revision 0f4f3090613131cc878aeff213dd14e7658e0a7b) +++ src/Common/DelftTools.Controls.Swf/TreeViewControls/TreeViewController.cs (.../TreeViewController.cs) (revision 2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb) @@ -1,14 +1,16 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Windows.Forms; using DelftTools.Utils.Collections; using DelftTools.Utils.Collections.Generic; using log4net; +using Timer = System.Windows.Forms.Timer; namespace DelftTools.Controls.Swf.TreeViewControls { - public class TreeViewController + public class TreeViewController : IDisposable { private static readonly ILog Log = LogManager.GetLogger(typeof(TreeViewController)); @@ -26,16 +28,37 @@ this.treeView = treeView; nodePresenters.CollectionChanged += NodePresentersCollectionChanged; + + // for a time being we use timer here to perform full refresh to avoid double refresh + fullRefreshTimer.Tick += (sender, args) => + { + fullRefreshTimer.Stop(); + treeView.Refresh(); + }; } + // use timer in order to combine property and collection change + // note that timer is active only when there is a full refresh taking place + readonly Timer fullRefreshTimer = new Timer { Interval = 250 }; + public void WaitUntilAllEventsAreProcessed() { - while (treeView.IsUpdateSuspended) + while (treeView.IsUpdateSuspended || (treeView.Visible && fullRefreshTimer.Enabled)) { Application.DoEvents(); } } + private void FullRefreshEventHandler(object sender, EventArgs eventArgs) + { + if (fullRefreshTimer.Enabled) + { + fullRefreshTimer.Stop(); // Reset timer when already started before + } + + fullRefreshTimer.Start(); // schedule full refresh + } + /// /// List of registered node presenters /// @@ -52,12 +75,18 @@ get { return data; } set { + if (data != null) + { + DisableDataEventListeners(); + } + treeView.Nodes.Clear(); data = value; if (data == null) return; CreateRootNode(); + EnableDataEventListeners(); treeView.SelectedNode = treeView.Nodes.Count > 0 ? treeView.Nodes[0] : null; } @@ -332,6 +361,36 @@ }, false); } + internal void EnableDataEventListeners() + { + var notifyPropertyChanged = data as INotifyPropertyChanged; + if (notifyPropertyChanged != null) + { + (notifyPropertyChanged).PropertyChanged += DataPropertyChanged; + } + + var notifyCollectionChange = data as INotifyCollectionChange; + if (notifyCollectionChange != null) + { + (notifyCollectionChange).CollectionChanged += DataCollectionChanged; + } + } + + internal void DisableDataEventListeners() + { + var notifyPropertyChanged = data as INotifyPropertyChanged; + if (notifyPropertyChanged != null) + { + (notifyPropertyChanged).PropertyChanged -= DataPropertyChanged; + } + + var notifyCollectionChange = data as INotifyCollectionChange; + if (notifyCollectionChange != null) + { + (notifyCollectionChange).CollectionChanged -= DataCollectionChanged; + } + } + private bool HasChildren(ITreeNode treeNode) { return GetChildNodeObjects(treeNode).Any(); @@ -353,6 +412,55 @@ return nodePresenterFunction(nodePresenter); } + private void DataCollectionChanged(object sender, NotifyCollectionChangingEventArgs e) + { + if(SkipEvents) + { + return; + } + + DataCollectionChangedSynchronized(sender, e); + + FullRefreshEventHandler(sender, e); + } + + private void DataCollectionChangedSynchronized(object sender, NotifyCollectionChangingEventArgs e) + { + var nodePresenter = ResolveNodePresenterForData(e.Item); + if (nodePresenter == null) + { + return; + } + + nodePresenter.OnCollectionChanged(sender, e); + } + + public static bool SkipEvents; // for debugging purposes, find a better way (see usage) + + private void DataPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if(SkipEvents) + { + return; + } + + var nodePresenter = ResolveNodePresenterForData(sender); + + if (nodePresenter == null) return; + var node = treeView.GetNodeByTag(sender); + + // HACK: bug in WaterFlowModel1DBoundaryDataNodeDataNodePresenter, in some cases event somes with a sender which is child of actual node.Tag object - find out how to fix it + var actualSender = sender; + if(node != null && sender != node.Tag) + { + actualSender = node.Tag; + } + + nodePresenter.OnPropertyChanged(actualSender, node, e); + + FullRefreshEventHandler(sender, e); + } + private void CreateRootNode() { var rootNode = new TreeNode(treeView) {Tag = data}; @@ -424,9 +532,23 @@ nodePresenter.OnDragDrop(nodeDragging.Tag, parentNode.Tag, nodeDropTarget.Tag, dragOperation, dropAtLocation); } + #region Implementation of IDisposable + + public void Dispose() + { + DisableDataEventListeners(); + } + + #endregion + public void OnTreeViewHandleCreated() { treeView.Refresh(); // Ensure the treeview is always up to date after creating handle (data is set and might be changed before enabling the delayed event handlers) } + + public void OnTreeViewHandleDestroyed() + { + fullRefreshTimer.Stop(); + } } } Index: src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorer.cs =================================================================== diff -u -rf1713196b96f96ed637286a1d8c6236a133268ae -r2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb --- src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorer.cs (.../ProjectExplorer.cs) (revision f1713196b96f96ed637286a1d8c6236a133268ae) +++ src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorer.cs (.../ProjectExplorer.cs) (revision 2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb) @@ -106,5 +106,15 @@ public Image Image { get; set; } public void EnsureVisible(object item) { } public ViewInfo ViewInfo { get; set; } + + public void EndWaitMode() + { + ProjectTreeView.EnableEvents(); + } + + public void BeginWaitMode() + { + ProjectTreeView.DisableEvents(); + } } } Index: src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs =================================================================== diff -u -rf1713196b96f96ed637286a1d8c6236a133268ae -r2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb --- src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs (.../ProjectExplorerGuiPlugin.cs) (revision f1713196b96f96ed637286a1d8c6236a133268ae) +++ src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectExplorerGuiPlugin.cs (.../ProjectExplorerGuiPlugin.cs) (revision 2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb) @@ -140,12 +140,12 @@ private void ApplicationProjectSaved(Project project) { - + projectExplorer.EndWaitMode(); } private void ApplicationOnProjectSaving(Project project) { - + projectExplorer.BeginWaitMode(); } private void ApplicationProjectClosed(Project project) Index: src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectTreeView.cs =================================================================== diff -u -rf1713196b96f96ed637286a1d8c6236a133268ae -r2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb --- src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectTreeView.cs (.../ProjectTreeView.cs) (revision f1713196b96f96ed637286a1d8c6236a133268ae) +++ src/DeltaShell/DeltaShell.Plugins.ProjectExplorer/ProjectTreeView.cs (.../ProjectTreeView.cs) (revision 2ace0d3a03d5bf9e372a9c1032f996488fe2c4eb) @@ -299,6 +299,16 @@ treeView.StartLabelEdit(); } + public void EnableEvents() + { + treeView.EnableDataEventListeners(); + } + + public void DisableEvents() + { + treeView.DisableDataEventListeners(); // HACK: dangerous, all changes in the tree nodes during save won't be shown + } + private void buttonsPropertiesClick(object sender, EventArgs e) { gui.CommandHandler.ShowProperties();