Hello,
In my Diagram I have some nodes marking rooms, called RoomNodeData. There’s a ClickCreatingTool, which creates those nodes upon double click. These room nodes contain a property RoomSize, which is supposed to be measured in the diagram by drawing a polygon and upon closing the polygon, the size is calculated and set in the room node.
Therefore I have a button, which changes the diagram’s model and (de)activates or changes certain tools upon clicked. Now every node other than room nodes gets deactivated and the user can select one of the existing room nodes. After doing so, a new ClickCreatingTool gets activated, with which the user can set polygon nodes via single click. Upon clicking the first drawn polygon node, the ClickCreatingTool gets deactivated and the currently selected room deselected, so the user can select another room to measure. He can also deactivate the tool and if he does, the Diagram’s previous state gets restored so the user can insert room nodes again.
You can see the whole process in the following pictures.
1. room node created via double click
2. measure room size selected after more rooms have been created and linked
All room nodes are now selectable, no more room nodes can be created via double click, no other node or link can be selected.
private void OnToggleStateChanged(BarCheckItem barCheckItem) {
switch (barCheckItem.BarItemName) {
case "MeasureRoomLayout":
if (barCheckItem.IsChecked.HasValue) {
// Set cursor
...
if (barCheckItem.IsChecked.Value) {
// Switch layers for newly created nodes
DiagramHandler.SetActiveLayers(base.Diagram.Panel.Children.OfType<ILayer>().Where(layer => layer.NavigationStep == NavigationStep.Rooms && layer.IsForeground && layer.IsBlendLayer));
// Disable drag selecting tool
base.Diagram.DragSelectingTool.MouseEnabled = false;
// Set new tools
base.Diagram.ClickSelectingTool = new MeasureRoomLayoutSelectingTool(base.Diagram);
base.Diagram.ClickCreatingTool = new MeasureRoomLayoutCreatingTool() { MouseEnabled = false };
base.Diagram.DraggingTool = new MeasureRoomDraggingTool(CalculateRoomSize);
// Set new event handlers
base.Diagram.SelectionChanged += OnSelectionChanged;
base.Diagram.NodeCreated += OnPolygonPointNodeCreated;
// This part is not relevant yet (see later)
if (this.ReopenSettingsNode != null) {
base.Diagram.Select(reopenSettingsNode);
}
} else {
// Do things upon deactivating measuring tool (see 6.)
...
}
}
break;
}
}
3. room node selected
All other nodes are now unselectable in addition to all previously disabled nodes. Now the user can start drawing the polygon.
private new void OnSelectionChanged(object sender, SelectionChangedEventArgs e) {
Northwoods.GoXam.Diagram diagram = (Northwoods.GoXam.Diagram)sender;
IEnumerable<AstaNodeData> selectedNodeData = diagram.SelectedParts.Select(part => part.Data).OfType<AstaNodeData>().ToList();
if (diagram.ClickSelectingTool is MeasureRoomLayoutSelectingTool) {
// Check if room was selected and set selected room
if (selectedNodeData.OfType<RoomNodeData>().Any()) {
((MeasureRoomLayoutSelectingTool)diagram.ClickSelectingTool).SelectedRoom = selectedNodeData.OfType<RoomNodeData>().First();
// Activate click creating tool for polygon nodes
diagram.ClickCreatingTool.MouseEnabled = true;
}
// Calculate area (see 5.)
else {
...
}
}
}
4. creating polygon nodes via single click
Polygon nodes are automatically linked and only drawn when clicked on free space, hence created nodes can be moved afterwards.
public void OnPolygonPointNodeCreated(object sender, DiagramEventArgs e) {
Northwoods.GoXam.Diagram diagram = (Northwoods.GoXam.Diagram)sender;
// Get current and last polygon nodes
PolygonPointNodeData currentPolygonPoint = diagram.SelectedParts.Select(part => part.Data).OfType<PolygonPointNodeData>().First();
PolygonPointNodeData previousPolygonPoint = diagram.NodesSource.OfType<PolygonPointNodeData>().Where(polygonPoint => polygonPoint.AssociatedRoom == currentPolygonPoint.AssociatedRoom).OrderByDescending(polygonPoint => polygonPoint.CreationTime).FirstOrDefault(polygonPoint => polygonPoint != currentPolygonPoint);
// Draw a link between the polygon points
if (previousPolygonPoint != null) {
((ObservableCollection<AstaLinkData>)diagram.LinksSource).Add(new PolygonPointLinkData() { From = currentPolygonPoint.Key, FromId = currentPolygonPoint.Guid, To = previousPolygonPoint.Key, ToId = previousPolygonPoint.Guid, AssociatedRoom = currentPolygonPoint.AssociatedRoom });
}
}
5. Click the first polygon node to close the polygon
Polygon nodes and links marked grey but are still moveable to enable room size changes after drawing. All room nodes become selectable again so the user can measure other rooms.
private new void OnSelectionChanged(object sender, SelectionChangedEventArgs e) {
Northwoods.GoXam.Diagram diagram = (Northwoods.GoXam.Diagram)sender;
IEnumerable<AstaNodeData> selectedNodeData = diagram.SelectedParts.Select(part => part.Data).OfType<AstaNodeData>().ToList();
if (diagram.ClickSelectingTool is MeasureRoomLayoutSelectingTool) {
// Check if room was clicked and set selected room (see 3.)
if (selectedNodeData.OfType<RoomNodeData>().Any()) {
...
} else {
RoomNodeData selectedRoom = ((MeasureRoomLayoutSelectingTool)diagram.ClickSelectingTool).SelectedRoom;
// Check if first polygon node was clicked on, thus close polygon
if (diagram.NodesSource.OfType<PolygonPointNodeData>().Count(polygonPoint => polygonPoint.AssociatedRoom == selectedRoom) > 1 && diagram.SelectedParts.Select(node => node.Data).OfType<PolygonPointNodeData>().Any(selectedPolygonPoint => diagram.NodesSource.OfType<PolygonPointNodeData>().Where(polygonPoint => polygonPoint.AssociatedRoom == selectedRoom).MinBy(polygonPoint => polygonPoint.CreationTime) == selectedPolygonPoint)) {
// Call link drawing function without creating new node
OnPolygonPointNodeCreated(sender, new DiagramEventArgs());
// Calculate area
((MeasureRoomLayoutSelectingTool)diagram.ClickSelectingTool).SelectedRoom.Size.SquareMeter = CalculateRoomSize(base.Diagram.NodesSource.OfType<PolygonPointNodeData>().Where(polygonPoint => polygonPoint.AssociatedRoom == ((MeasureRoomLayoutSelectingTool)diagram.ClickSelectingTool).SelectedRoom).OrderBy(polygonPoint => polygonPoint.CreationTime).Select(polygonPoint => polygonPoint.Location).ToList());
((MeasureRoomLayoutSelectingTool)diagram.ClickSelectingTool).SelectedRoom = null;
// Disable polygon node creating tool
diagram.ClickCreatingTool.MouseEnabled = false;
// This part is not relevant yet (see later)
if (this.ReopenSettingsNode != null) {
this.ReopenSettingsNode = null;
EventAggregator.GetEvent<BarCheckItemResetStateEvent>().Publish(string.Empty);
}
}
}
}
6. Deactivate measure room size tool
When the user has measured the rooms, he can disable the tool by clicking the button again, thus he’s able to create other rooms afterwards.
private void OnToggleStateChanged(BarCheckItem barCheckItem) {
switch (barCheckItem.BarItemName) {
case "MeasureRoomLayout":
if (barCheckItem.IsChecked.HasValue) {
// Set cursor
...
if (barCheckItem.IsChecked.Value) {
// Do things upon activating measuring tool (see 2.)
} else {
// Switch back layers for room nodes
DiagramHandler.SetActiveLayers(base.Diagram.GetAstaLayers(layer => layer.Key == new Guid((string)this.NavigationContext.Parameters[NavigationParameter.Key]) && layer.NavigationStep == NavigationStep.Rooms && layer.Floor == DiagramHandler.ActiveNodeLayer.Floor && layer.IsForeground && !layer.IsBlendLayer));
base.Diagram.DragSelectingTool.MouseEnabled = true;
// Set tools to the regular ones
base.Diagram.ClickSelectingTool = new ClickSelectingTool();
base.Diagram.ClickCreatingTool = new AstaClickCreatingTool() { PrototypeData = new RoomNodeData(base.Diagram.NodesSource.OfType<RoomNodeData>().Count().ToString()) };
base.Diagram.DraggingTool = new DraggingTool();
// Remove event handlers
base.Diagram.SelectionChanged -= OnSelectionChanged;
base.Diagram.NodeCreated -= OnPolygonPointNodeCreated;
// Remove polygon points and links
base.Diagram.NodesSource.OfType<PolygonPointNodeData>().ToList().ForEach(polygonPoint => ((ObservableCollection<AstaNodeData>)base.Diagram.NodesSource).Remove(polygonPoint));
base.Diagram.LinksSource.OfType<PolygonPointLinkData>().ToList().ForEach(polygonLink => ((ObservableCollection<AstaLinkData>)base.Diagram.LinksSource).Remove(polygonLink));
// Reactivate all other nodes
base.Diagram.NodesSource.OfType<ApartmentNodeData>().ForEach(apartment => apartment.Status = PartStatus.Default);
base.Diagram.LinksSource.OfType<RoomLinkData>().ForEach(link => link.Status = PartStatus.Default);
}
}
break;
}
}
Now this is working very well so far. However, there’s another way of measuring. If the user double clicks a room node, a popup opens with various options for the user to insert data. In that popup is another measure room size button with which the user can only measure the size for this very room. In this case the popup data gets cached, the popup gets closed, the measure room size button automatically selected, which throws its event, thus room selection becomes active and the room, whose settings were opened gets automatically selected. Now the user can draw his polygon and upon finishing the button gets automatically deactivated and the popup opens up again and fills itself with the cached data, so the user can finish the form.
Here’s an image of it and the following happening:
public void OnSettingsRequest(RoomNodeData nodeData, MouseButtonEventArgs eventArgs)
{
RoomSettingsRequest.Raise(new RoomSettingsNotification(/* Pass various values */) { Title = $"{Resources.RoomViewModel_PropertiesForRoom} {nodeData.Key}" }, response => {
if (response == null) { return; }
if (response.Confirmed) {
// Apply data
...
} else if (response.Delayed) {
// Trigger the button click
EventAggregator.GetEvent<BarCheckItemSetStateEvent>().Publish("MeasureRoomLayout");
// Remember the currently selected room node
this.ReopenSettingsNode = nodeData;
}
});
}
Now, there were two parts in the code above I marked as irrelevant yet. These become relevant now.
There’s this one when the measure room size button’s event gets handled, which automatically selects the proper room node:
private void OnToggleStateChanged(BarCheckItem barCheckItem) {
...
if (this.ReopenSettingsNode != null) {
base.Diagram.Select(reopenSettingsNode);
}
...
}
And there’s this one when the polygon gets closed, calling the measure room size’s event to disable room measuring:
private new void OnSelectionChanged(object sender, SelectionChangedEventArgs e) {
...
if (this.ReopenSettingsNode != null) {
this.ReopenSettingsNode = null;
// Trigger the button click once again to deactivate it
EventAggregator.GetEvent<BarCheckItemResetStateEvent>().Publish(string.Empty);
}
...
}
Now the problem is, I can measure the room one time properly, but when the popup opens back up after measuring and I try to measure the room again I can’t create any polygon nodes anymore. Also, if I deactivate the room measuring via the button in the ribbon bar I can’t create any more room nodes, or select and move any node (double click still works).
I suspect the problem is caused by calling the measure room size button still within the OnSelectionChanged method, thus the OnSelectionChanged still being active while I actually remove it from the diagram when deactivating the room measuring, and thus still being active when I reopen the popup and still being active when I click the measure room size button in the popup a second time and still being active when the OnSelectionChanged handler gets added back to the diagram. And only then it steps out of the method. So this might cause issues, but I’m not sure how to handle this problem. What I actually need is a method called after the OnSelectionChanged method has finished when the polygon was closed. I’ve tried creating a transaction and listening to model changes but ModelChanged never gets thrown when I call CommitTransaction("MeasureRoomSize")
. I’ve also tried listening to LayoutCompleted but it’s being called so often and I have no idea how to identify that one single call from my CommitTransaction("MeasureRoomSize")
.
What can I do?