Custom Painting of Resources in Java Scheduler

In this blog post we will explain how to color cells and resources in the Resource view of the calendar based on a certain criteria. In our case we take the “Resource Table” sample from the Samples for the Java Swing Scheduler and we will edit its code to color the header and background of cells that correspond to given resources, in our sample it is an employee:

We will also add tooltips that show when the mouse is over cells that correspond to this employee:

I. General Settings

The code that we will demonstrate and explain is an extension to the “Resource Table” sample, which you can download from:

Download Resource Table Java Scheduler Sample

The sample uses the Scheduling Library for Java Swing, which is included as a Jar reference to the project.

We create an instance of the Calendar class and set its current time, date and the end date – this is the final date that will be visible in the resource view:

calendar = new Calendar();
calendar.setCurrentTime(DateTime.now());
calendar.setDate(new DateTime(2020, 6, 8));
calendar.setEndDate(new DateTime(2020, 7, 7));

The Calendar class exposes many methods for customizing the schedule. We first set the view to be CalendarView itemSettings and resourceViewSettings classes provide us with lots of options to customize the calendar look. We use them to twist the appearance of our resource table:

calendar.getItemSettings().getSelectedItemStyle().setHeaderFont(new Font("Verdana", Font.PLAIN, 9));
calendar.getItemSettings().getSelectedItemStyle().setHeaderTextAlignment(EnumSet.of(TextAlignment.MiddleLeft));
calendar.getItemSettings().getSelectedItemStyle().setHeaderTextShadowStyle(ShadowStyle.None);
...........................
...........................
calendar.getItemSettings().getStyle().setHeaderFont(new Font("Verdana", Font.PLAIN, 9));
calendar.getItemSettings().getStyle().setHeaderTextAlignment(EnumSet.of(TextAlignment.MiddleLeft));
calendar.getItemSettings().getStyle().setHeaderTextShadowStyle(ShadowStyle.None);
calendar.getItemSettings().getStyle().setHeaderTextShadowOffset(0);
...........................
...........................
calendar.getResourceViewSettings().getBottomTimelineSettings().setFormat("EEE (MM/dd)");
calendar.getResourceViewSettings().getBottomTimelineSettings().setSize(15);
calendar.getResourceViewSettings().getBottomTimelineSettings().getStyle().setHeaderBrush(Brushes.White);
calendar.getResourceViewSettings().getBottomTimelineSettings().getStyle().setHeaderFont(new Font("Verdana", Font.BOLD, 10));

The employees are instances of the Contact class. We add them to the contacts collection of the Calendar, once we’ve created them. It is important that we provide an id to each Contact, because that’s how we will identify them later in code:

Contact contact = new Contact();
contact.setFirstName("Mike");
contact.setId("IdMike");
contact.setName("Mike");
calendar.getContacts().add(contact);

The items that represent tasks for the resources are Appointment instances. We create them in code and add them to the items collection of the Calendar app;

app = new Appointment();
app.setStartTime(new DateTime(2006, 3, 27));
app.setEndTime(new DateTime(2006, 3, 28));
app.getContacts().add(calendar.getSchedule().getContacts().get("IdMike"));
app.setHeaderText("21965 Carbon Mesa Rd (1)");
app.setPriority(0);
calendar.getSchedule().getItems().add(app);

The resource view renders rows of cells that correspond to a given Resource, Location, Item, Task etc. The options available are members of the GroupType enumeration. In our sample we group the view by resources e.g. employees:

calendar.setGroupType(GroupType.GroupByContacts);

And with that we’ve finished with the general settings and we continue writing the code that will customize our application.

II. Custom Drawing

What we want to achieve as appearance of our resource table – selective coloring of the background of cells – can be done through custom drawing. Custom drawing provides us with means to color most elements of the calendar, depending on the view. The “Custom Draw Elements” sample gives us visual representation of the elements that correspond to the CustomDrawElements enumeration, which determines what is to be custom drawn in a schedule:

The sample is available from this link:

Java Swing Scheduler: Sample that Demonstrates the Custom Draw Elements according to the Calendar View

We want to color the resource header and the cells that correspond to this resource. So we use the setCustomDraw method to achieve that:

calendar.setCustomDraw(EnumSet.of(CustomDrawElements.ResourceViewRowHeader, CustomDrawElements.ResourceViewCellComplete));

The members of the CustomDrawElements enumeration allow bitwise combining. The drawing is done in an event handler for the draw event:

calendar.addCalendarListener(new CalendarAdapter() {
	public void draw(CalendarDrawEvent e) {
		onCalendarDraw(e);
	}
});

We use a CalendarAdapter to subscribe to the draw event, which we handle with the onCalendarDraw method. The CalendarDrawEvent class exposes many properties that give us information about the element that is being drawn. We use the getElement method to check, which element is being drawn – the cell or the header. If it is the cell, we get the resource that correspond to it and if it is the right one, we paint a rectangle, which represents the whole area of the element that is painted at the moment. We get it with the getBounds method:

if (e.getElement() == CustomDrawElements.ResourceViewCellComplete)
{
	Rectangle bounds = new Rectangle(e.getBounds());
	bounds.x += 1;

if (e.getResource().getId().equals("IdMike") ||
    e.getResource().getId().equals("IdChuck") ||
    e.getResource().getId().equals("IdTom") ||
    e.getResource().getId().equals("IdAlfredo"))
				
{
	g.fillRectangle(_brush3, bounds);
	g.drawString("Office", _font, _textBrush, bounds, f);
}

We will draw an outline to the resource header that correspond to the same resource, whose rows we colored with _brush3:

else if (e.getElement() == CustomDrawElements.ResourceViewRowHeader)
	{
		if (e.getResource().getId().equals("IdMike") ||
			e.getResource().getId().equals("IdChuck") ||
			e.getResource().getId().equals("IdTom") ||
			e.getResource().getId().equals("IdAlfredo"))
					
		{
			Brush _brush3 = new SolidBrush(new Color(254, 249, 207, 100));
			g.fillRectangle(_brush3, e.getBounds());
			g.drawRectangle(new Pen(new Color(163, 198, 134, 255), 2),
				e.getBounds().getMinX() + 1,
				e.getBounds().getMinY() + 1,
				e.getBounds().getMaxX() - 2,
				e.getBounds().getMaxY() - 2);
		}
			
	}

Here we check if the custom draw element is CustomDrawElements The CalendarDrawEvent class, which provides data for the event gives us sufficient information to recognize the exact element that is being painted.

III. Tooltips Over Selected Resources

By default the Calendar provides tooltips for items. The ItemTooltipEvent provides more information about this. However, we want to show tooltips when the user hovers over cells that correspond to the elements that we’ve painted in section II. We can do that by using a MouseMotionListener and subscribing to the mouseMove event. Note that these are standard Java Swing events:

calendar.addMouseMotionListener(new MouseAdapter() {
	public void mouseMoved(MouseEvent e) {
		onCalendarMouseMoved(e);
     }
		
});

In the event handler method we use the getResourceAt method of the Calendar to learn the resource, over which the mouse is hovering. Then we use the id-s that we’ve assigned to our resources and check if the mouse is over the resources that we want to render tooltips:

private void onCalendarMouseMoved(MouseEvent e) {
		
		Resource res = calendar.getResourceAt(e.getX(), e.getY());
		
		if (res.getId().equals("IdMike") ||
		    res.getId().equals("IdChuck") ||
		    res.getId().equals("IdTom") ||
		    res.getId().equals("IdAlfredo"))
		{				
			   calendar.setToolTipText(res.getId());				
				
		}
		else
		{
		     calendar.setToolTipText("");		    
		}			
		
}

When we detect that the mouse is over a resource that does not need to render tooltip, we set the tolltip text to be an empty string.

With that we’ve finished customizing the resource sample. You can download the extended version from this link:

Resource Table in Java Swing with Tooltips and Resource Coloring: Download

You are welcome to post your questions and comments at the Online Forum for Scheduling for Java Swing.

About MindFusion Scheduling for Java Swing: The library provides extensive feature-set for creating and customizing all sorts of calendars, task lists, time-management tables, resource allocation tables and other. It boasts various options for customizing appearance and numerous events for handling user actions. The distribution archive includes a lot of samples and detailed documentation. Learn more at https://mindfusion.eu/java-scheduler.html

Pan and Zoom Programmatically in a JavaScript Diagram

We will build a diagram with 50 random nodes and we will zoom and pan this diagram programmatically. Here is a screenshot of the final diagram, which is a link to the sample:

We will use the MindFusion Diagramming library for JavaScript.

I. Project Setup

We add a reference to the MindFusion.Diagramming.js and MindFusion.Common.js files. We also add a reference to another file called MouseEvents.js. This is our code-behind file.

<script src="MindFusion.Common.js" type="text/javascript"></script>
<script src="MindFusion.Diagramming.js" type="text/javascript"></script>
<script src="MouseEvents.js" type="text/javascript"></script>

In the BODY of the web page we create a Canvas element, to which we assign an id. This is important, because we will refer to the Canvas in code:

<div style="width: 100%; height: 100%; overflow: auto;">
			<canvas id="diagram_canvas" width="2100" height="2100">
			This page requires a browser that supports HTML 5 Canvas element.
		</canvas>
</div>

II. Diagram Settings

In the code-behind file that we called MouseEvents.js we use the DOMContentLoaded event to initialize the diagram.

document.addEventListener("DOMContentLoaded", function ()
{
	// create a Diagram component that wraps the "diagram_canvas" canvas
	diagram = MindFusion.AbstractionLayer.createControl(Diagram, null, null, null, document.getElementById("diagram_canvas"));
	diagram.setBounds(new Rect(5, 5, 2000, 1000));

We use the createControl method of the AbstractionLayer class to create an instance of the Diagram class. The setBounds method determines the size of the diagram’s drawing area. If this size is bigger than the size of the Canvas, the diagram automatically shows scrollbars. Note that only if the diagram’s area is larger than the canvas we can use panning.

We use some settings of the Diagram class to customize the application:

diagram.setDefaultShape("Rectangle");
diagram.setRouteLinks(true);
diagram.setRoundedLinks(true);
diagram.setShowGrid(false);

The links will be routed and rounded and no grid will be rendered.

III. Diagram Items

We create the diagram nodes with the createShapeNode method of the Factory class. The Factory class as an instance is available through the getFactory() method:
for(var i = 0; i < 50; i++)

	{
		var colorIndex = Math.floor(Math.random() * 3);  
		var shape = diagram.getFactory().createShapeNode(new Rect(136, 36, 20, 10));
		shape.setBrush({ type: 'SolidBrush', color: colors[colorIndex] });
		if(i % 3   == 0)
		   shape.setShape('Ellipse');
	     else 
		    shape.setShape('Rectangle');
	    if( i % 7 == 0)
		{
			shape.setBounds(new Rect(136, 36, 16, 8));	
		}
	
		
		shape.setText("Node " + (i + 1).toString());
		shape.setTextColor("white");
	}

We make each third shape Ellipse and we choose the brush on a random principle out of three brushes, that we initialized in an array. Each seventh shape is slightly smaller – that is set with the setBounds method, which takes as an argument a Rect, that is slightly smaller than the Rect instance that we use when we create the shape nodes.

The connectors among the nodes are created with the createDiagramLink method of Factory . We cycle through all 50 nodes and connect each one of them with a randomly taken node from the diagram nodes collection. This collection is available through the nodes proeprty of the Diagram class:

diagram.nodes.forEach(function(node){
    
	var nodeIndex = Math.floor(Math.random() * 50);  
	
	var node2 = diagram.nodes[nodeIndex];
	var link = diagram.getFactory().createDiagramLink(node, node2);
	link.setHeadShape("Circle");
	
  })

We customize the appearance of the link through the setHeadShape method. We choose the ‘Circle’ shape as a head to each link.

We have created the diagram items with the same bounds, which means they are on top of each other. The best way to arrange them is with one of the automatic layout algorithms, available with the JsDiagram. They are members of the MindFusion.Graphs namespace – you can check the rest. In our sample we’ve chosen the LayeredLayout ,which provides quite nice result. We set its direction to LayoutDirection .There a few other properties that we’ve set that regulate the node distance, the layer distance and more:

var layout = new MindFusion.Graphs.LayeredLayout();
layout.direction = MindFusion.Graphs.LayoutDirection.LeftToRight;
layout.siftingRounds = 0;
layout.nodeDistance = 8;
layout.layerDistance = 8;
diagram.arrange(layout);

All layouts are applies through the arrange method of the Diagram that takes an instance of the layout as an argument.

IV. Pan and Zoom

We will implement pan and zoom by handling standard DOM events. The first one is the “wheel” event, which we attach to the diagram canvas element:

var dgrm = document.getElementById('diagram_canvas');

dgrm.addEventListener('wheel', function(e)
{
	var zoom = diagram.getZoomFactor();
	zoom -= e.deltaY / 10;
	if (zoom &gt; 10)
		diagram.setZoomFactor(zoom);

	e.preventDefault(); // do not scroll
});

We use the getZoomFactor and setZoomFactor methods of the Diagram , to manipulate the zoom ratio. The zoom step is calculated based on the deltaY value of the event args. You can command the amount of zoom by dividing by a smaller or a larger number. It is important that we call preventDefault() on the event arguments, to surpass the default response of the canvas to the wheel event.

The panning is implemented by handling the mousedown and mouseup DOM events of the Canvas.

/* events fired on the draggable target */
dgrm.addEventListener('mousedown', function(e)
{
 if( e.ctrlKey)
	diagram.setBehavior(MindFusion.Diagramming.Behavior.Pan);
 
}, false);

dgrm.addEventListener('mouseup', function(e)
{
 if( e.ctrlKey)
	diagram.setBehavior(MindFusion.Diagramming.Behavior.LinkShapes);
 
}, false);

If we want to make the Diagram pan we need simply to change the diagram’s behavior with the setBehavior method. The options are members of the Behavior enumeration. When the user clicks on the Diagram and the Ctrl key is pressed, we change the diagram’s behavior to “Pan”. When the mouse is up, but the Ctrl key is pressed, we rest the behavior back to LinkShapes. This is the default behavior, where dragging with the mouse creates new shapes, while dragging between existing DiagramShape -s, creates DiagramLink -s.

With that our sample is ready. You can download the source code from this link:

Download the Mouse Events Sample with JavaScript Diagram

Technical support is available through MindFusion forum here.

About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree, BPMN diagrams and much more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Tree with Nodes that Have Multiple Parents

Most tree structures are built as hierarchies: the number of nodes increases at each level. In our sample we will look at a tree, where not all branches have a higher number of nodes than their predecessors. In our tree some nodes will have multiple parents e.g. there are nodes that have several ancestors.

You can try the sample online:

In order to build this application we use MindFusion Diagramming for JavaScript library.

I. General Settings

In a web page we add the code that initializes a Canvas. We give the Canvas an id:

<div style="overflow: visible; height:100%; margin: 1px; padding: 0px;">
                <canvas id="diagram" width="2100" height="2500">
                    This page requires a browser that supports HTML 5 Canvas element.
                </canvas>
</div>

We add references to the two JavaScipt files that provide the diagramming functionality: MindFusion.Diagramming and MindFusion.Common. We also add a reference to a code-behind file that contains the JavaScript code for our application:

<script src="Scripts/MindFusion.Common.js" type="text/javascript"></script>
<script src="Scripts/MindFusion.Diagramming.js" type="text/javascript"></script>
<script src="MultipleParentNodes.js" type="text/javascript"></script>

We have placed those files in a subfolder called Scripts, which is located in our main application folder.

II. Diagram Settings

We create the diagram in the window.onload event handler. We want to be sure that all scripts are loaded:

window.onload = function(e){ 

	var diagramEl = document.getElementById('diagram');
	// create a Diagram component that wraps the "diagram" canvas
	diagram = AbstractionLayer.createControl(Diagram, null, null, null, diagramEl);
	diagram.setAllowInplaceEdit(true);
	diagram.setRouteLinks(true);
	diagram.setShowGrid(true);
	diagram.setRoundedLinks(true);
	diagram.setBounds(new Rect(0, 0, 2000,2500));
}

We create the Diagram using a reference to the DOM element of the Canvas from the web page. We set its allowInplaceEdit property to true, which lets users edit interactively nodes and links. We use showGrid to render a background grid that helps to align nodes and links. We setRoundedLinks and give the diagram a big work are with the setBounds method.

II. Creating the Diagram Nodes

We create the DiagramNode -s in a separate method, which we call after the control is created and all settings are made:

function onLoaded()
{
	var nodes = {};
	
	for(var i = 0; i &lt; 5; i++)
	{
		nodes[i] = diagram.getFactory().createShapeNode(new Rect(20, 20, 20, 12));
		nodes[i].setShape('Rectangle');
	    nodes[i].setBrush({ type: 'SolidBrush', color: '#567939' });
	};

We initialize a list, where we will store dynamically those nodes that we want to have reference to. At first we create 5 ShapeNode -s that are the first level of the tree. We use the createShapeNode method of Factory to create the ShapeNode -s and DiagramLink -s. Note that we will create all nodes with equal bounds. We don’t have to worry about their location because we will apply an automatic layout at the end.

Factory is available through the getFactory method of Diagram You do not usually create the class but get an instance of it through the Diagram instance.

We use setShape to provide the id af the diagram shape that we want the node to take. A list with the diagram shapes available, together with their id-s can be found in the online help.

We also use setBrush to specify the fill of the ShapeNode . In our case we use a SolidBrush ,but there are other options to choose from.

We create then a single ShapeNode that will be the next level:

var node5 = diagram.getFactory().createShapeNode(new Rect(20, 20, 20, 12 ));
	node5.setShape('Rectangle');
	node5.setBrush({ type: 'SolidBrush', color: '#6f9c49' });

We color it in a slightly lighter shade of green than the nodes at the first level. We then use the Factory class once again to create the DiagramLink -s between the 5 nodes at the first level and this one single node at the second level:

	for(var i = 0; i &lt; 5; i++)
	{ 
        var link = diagram.getFactory().createDiagramLink(nodes[i], node5);	
		link.setHeadShape("Triangle");
		link.setText("20%");
	    link.setHeadShapeSize(3.0);
	    link.setHeadBrush({ type: 'SolidBrush', color: '#7F7F7F' });
	};

The setText and setHeadShape methods of the DiagramLink class allow us to specify the label of the link and its shape at the end. There is also setBaseShape that allows us the specify the shape at the start of the DiagramLink.

The lovely thing about the Factory class is that it adds automatically the newly created DiagramItem -s, such as nodes and links, to the items collection of the diagram. You can find the newly created DiagramNode -s and DiagramLink -s also as members of the nodes and links collections respectively.

Now we have 5 links coming from all 5 nodes from the first level that point to the second level:

Node with Multiple Parents

We go on doing the rest of the diagram in the same way. We create ShapeNode -s with Factory and then bind the nodes with Factory .

III. Layout

We use the LayeredLayout to arrange all nodes of the diagram. Since the diagram is not a typical tree, we prefer the LayeredLayout to the TreeLayout

var lLayout = new MindFusion.Graphs.LayeredLayout();
	diagram.arrange(lLayout);

It is really easy to apply any other algorithm on the diagram – you just need to create an instance of it and call the arrange method of your diagram to apply this instance. You can quickly change layouts and experiment to see which one provides the best result.

In our case the LayeredLayout looks fine and with this we are done building the tree.

You can download the sample together with all libraries used from the following link:

A JavaScript Graph with Nodes that Have Multiple Parents

Technical support is available through MindFusion forum here.

About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree, BPMN diagrams and much more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Organization Services Chart with the JavaScript Diagram Library

In this blog post we will use MindFusion JavaScript Diagram library to build an application that allows the users to create a diagram with nodes that hold icons and text. We have used this online diagram application to build an infogram for the services portfolio of an imaginary company. However, you can replace the icons and use it as a tool to build diagrams of whatever domain is of interest to you.

In the sample diagram we use SVG images from Google’s material design icons set. You can download them from https://github.com/google/material-design-icons.


Our sample application uses a Diagram and a NodeListView controls – both of them part of JsDiagram.

I. General Settings

We need to add two HTML Canvas elements to the web page where the sample application will be. One is for the NodeListView and the other is for the Diagram

<div style="width: 70px; height: 100%; overflow-y: auto; overflow-x: hidden; position: absolute;
				top: 5px; left: 0px; right: 0px; bottom: 0px;">
				<canvas id="nodeList" width="200">
				</canvas>
</div>

...........................

<div style="position: absolute; left: 70px; top: 5px; right: 0px; bottom: 0px; overflow: auto;">
                <canvas id="diagram" width="2100" height="2100">
                    This page requires a browser that supports HTML 5 Canvas element.
                </canvas>
</div>

We place the two Canvas elements using CSS next to each other. The NodeListView is in a long and narrow Canvas and next to it is the Diagram Canvas, which has a big size of 211 pixels. It is important that we provide the two Canvases with id-s: we will refer to them later in the JavaScript code using these id-s.

Then we add references to the two JavaScript files that we use from the Diagram library – MindFusion.Diagramming and MindFusion.Common:

<script src="Scripts/MindFusion.Common.js" type="text/javascript"></script>
<script src="Scripts/MindFusion.Diagramming.js" type="text/javascript"></script>
<script src="OrgInfoDiagram.js" type="text/javascript"></script>

The last JavaScript file that we reference is OrgInfoDiagram and there we keep the code for our application.

II. The Diagram

We create the Diagram object by using the reference to the Canvas that will render it:

// create a Diagram component that wraps the "diagram" canvas
diagram = Diagram.create(document.getElementById("diagram"));

Then we use an instance of the Style class to create a style that will determine how newly created –DiagramNode -s are styled:

//styling
var shapeNodeStyle = new Style();
shapeNodeStyle.setBrush({ type: 'SolidBrush', color: '#e0e9e9' });
shapeNodeStyle.setStroke("Orange");
shapeNodeStyle.setFontName("Verdana");
shapeNodeStyle.setFontSize(9);   
shapeNodeStyle.setNodeEffects([new GlassEffect()]);

The Style instance applies GlassEffect on the new nodes and places a thick orange border around them. We also allow users to edit diagram nodes by calling setAllowInplaceEdit .

diagram.setStyle(shapeNodeStyle);
diagram.setAllowInplaceEdit(true);

III. The NodeListView

The NodeListView control can hold both text and image nodes. We create it the same way we created the Diagram : by using the id of its Canvas element:

// create a NodeListView component that wraps the "nodeList" canvas
var nodeList = MindFusion.Diagramming.NodeListView.create(document.getElementById("nodeList"));
nodeList.setTargetView(document.getElementById("diagram"));
nodeList.setIconSize(new Size(36, 36));
nodeList.setDefaultNodeSize (new Size(18, 18));

It is important that we set the target of the NodeListView – this is the Canvas, where the newly created DiagramNode will be rendered. We also set the size of the icons in the NodeListView and the size that newly created nodes will have. For this we use the setIconSize and setDefaultNodeSize methods respectively.

We create the nodes for the NodeListView in a special method called initNodeList:

function initNodeList(nodeList, diagram)
{
	
	var node = new ShapeNode(diagram);
	node.setText("text");
	node.setShape("Ellipse");
	node.setPen("Gray");		
	node.setBrush("White");
	node.setBounds(new Rect(0, 0, 48, 48));
	node.setEnableStyledText(true);
	node.setFont(new Font("sans-serif", 12, true));
	node.setStrokeThickness(2);
	nodeList.addNode(node);
.......................................
.......................................

The first ShapeNode that we create is a standard text node with the difference that we enable styled text for it: that means the user can enter bold, italic or underline text using the standard HTML tags <b>, <i>, <u>. We specify this with the method setEnableStyledText . We also add the node to the NodeListView that we have provided to the method.

In order to create nodes with SVG images, we need to create instances of the SvgNode class:

// add some nodes to the NodeListView
	var svgImages = ["call", "reader", "computer", "data", "car", "text", "music", "movie", "nature", "calendar", "chart", "router",  "account", "alarm", "announcement", "book", "calls", "copyright", "event", "grade", "group_work", "info", "key", "list", "payment", "phone", "shop", "sign", "textsms", "theaters", "work"];
	for (var i = 1; i &lt;= svgImages.length; ++i)
	{
		node = new MindFusion.Diagramming.SvgNode(new Rect(0, i * 48, 48, 48));
		
	    node.setTransparent(true);
	    var content = new SvgContent();
	    content.parse(svgImages[i] + ".svg");
	    node.setContent(content);
	    nodeList.addNode(node);
	}

We have copied the desired icons in the directory of the application and we have listed their names in a list. Then we cycle through all members of the list and create instances of the SvgNode class. We set their background to be transparent and we render the images thanks to the SvgContent class. Finally we also add the newly created SvgNode -s to the node list.

We call the initNodeList method providing the nodeList and diagram instances:

initNodeList(nodeList, diagram);

IV. Events

Though the code so far is enough to provide the desired functionality of our application, we want to customize the newly created diagram nodes and links. This can be achieved through events. We handle two of them onNodeCreated and LinkCreated.

diagram.addEventListener(Events.nodeCreated, onNodeCreated);
diagram.addEventListener(Events.linkCreated, onLinkCreated);

In the NodeCreated event handler we get the ShapeNode that is being created and make its shape to a round one with the setShape method. We assign to it a thick orange border and add some padding to the images – in case it is an SvgNode:

function onNodeCreated(sender, args)
{
	var node = args.getNode();
	node.setTransparent(false);
	node.setShape("Ellipse");
	node.setStroke("orange");
	node.setStrokeThickness(3);
	node.setFont(new Font("sans-serif", 4));
	node.setImagePadding(new Thickness(1.5, 1.5, 1.5, 1.5));
}

The linkCreated event handler applies some styling, but to newly created DiagramLink instances”

function onLinkCreated(sender, e)
{
	var link = e.getLink();
	link.setBaseShape(null);
	link.setHeadShape(null);
	link.setStrokeThickness(3.0);
}

We remove the default base and head shapes of the diagram links and leave the connectors to be just straight lines. We also draw them with a thick orange pen.

And these were the last lines of code that we had to add to make the application complete. You can download the source code, together with all all Diagramming libraries used from this link:

Download Organigram Application in JavaScript Source Code

You can use MindFusion Diagramming for JavaScript forum to post your questions, comments and recommendations about this sample or the Js Diagram library.

About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree and more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Different-size Icons in the Js Diagram NodeListView

In this blog post we will learn how to add a NodeListView control to a Diagram and how to set its ShapeNode -s to a different size. When ShapeNode -s are dragged from the NodeListView the instances that will be created are proprtional in size to the size of the ShapeNode that was dragged. Here is a screenshot of the final application:

I. General Settings

We create an HTML page and add to it references to the MindFusion JavaScript files that represent the diagramming library:

<script src="MindFusion.Common.js" type="text/javascript"></script>
<script src="MindFusion.Diagramming.js" type="text/javascript"></script>
<script src="Controls.js" type="text/javascript"></script>

The last reference is to the Controls JavaScript file, where the code for the application is located.

Then we create two Canvas-es: one is for the NodeListView and the other is for the Diagram The NodeListView component is bound to the canvas element below:

<div style="width: 200px; height: 100%; overflow-y: auto; overflow-x: hidden; position: absolute; top: 201px; left: 0px; right: 0px; bottom: 0px;">
<canvas id="nodeList" width="200"></canvas></div>


......
<!-- The Diagram component is bound to the canvas element below -->


<div style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: auto;">
     <canvas id="diagram" width="2100" height="2100">This page requires a browser that supports HTML 5 Canvas element.</canvas></div>

Both the Diagram and NodeListView controls require Canvas elements to render themselves onto.

II. Initializing the Controls

We create the Diagram using the id that we’ve provided to its Canvas in the web page:

// create a Diagram component that wraps the "diagram" canvas
diagram = Diagram.create(document.getElementById("diagram"));
diagram.setBounds(new MindFusion.Drawing.Rect(0, 0, 500, 500));

We set a bigger size to the diagram in order to make it fill the web page.

We create the NodeListView instance the same way we created the diagram:

// create a NodeListView component that wraps the "nodeList" canvas
var nodeList = MindFusion.Diagramming.NodeListView.create(document.getElementById("nodeList"));
nodeList.setTargetView(document.getElementById("diagram"));

Now we need to add the settings that will make the ShapeNode -s different in size when rendered onto the list:

nodeList.setIconSize(null);

The setIconSize method is used to specify the default size of nodes in the NodeListView When we set the size to null, the control draws each node in the NodeListView with the size that was assigned to it:

function initNodeList(nodeList, diagram)
{
	// add some nodes to the NodeListView
	var shapes = ["Actor", "RoundRect", "Triangle", "Decision"];
	for (var i = 0; i &lt; shapes.length; ++i)
	{
		var node = new MindFusion.Diagramming.ShapeNode(diagram);
		node.setText(shapes[i]);
		node.setShape(shapes[i]);
        node.setBounds(new MindFusion.Drawing.Rect(0, 0, (i+1)*10, (i+1)*10));
		nodeList.addNode(node, shapes[i]);
	}
}

Here we increase the size of wach ShapeNode with 10 points on each itereation. This makes the icons with various size but does not create them with different size when dropped on the diagram. In order to do this we must set:

nodeList.setDefaultNodeSize (null);

setDefaultNodeSize specifies the size of those nodes that are created when a ShapeNode is dropped on the Diagram area. By setting this size to null we tell the control to read the size of the new ShapeNode from the instance in the NodeListView control.

With that our sample is ready. You can download the source code from this link:

JavaScript NodeListView with Various Size Nodes: Download Sample

About Diagramming for JavaScript: This native JavaScript library provides developers with the ability to create and customize any type of diagram, decision tree, flowchart, class hierarchy, graph, genealogy tree and more. The control offers rich event set, numerous customization options, animations, graph operations, styling and themes. You have more than 100 predefined nodes, table nodes and more than 15 automatic layout algorithms. Learn more about Diagramming for JavaScript at https://mindfusion.eu/javascript-diagram.html.

Multi-tabs Form in JavaScript with Validation

In this blog post we will create a form that spans over several tabs. Each tab contains input fields for a specific type of information. There are placeholders and restrictions for each input field. If the provided data is not correct we display a guiding message and select the first tab that has wrong data. All incorrect fields are outlined with red.

I. General Settings

We will use two files for our form – one is a web page and the other a JavaScript file that will hold all JavaScript code for the sample. They both are names TabForm.

In the web page we add references to the css file that holds the theme we’ve chosen:

 	<link rel="stylesheet" type="text/css" href="themes/light.css">

This is the CSS for the light theme of the MindFusion JavaScript UI controls. There are various themes provided out-of-the-box with the library and you can choose another one.

Then, at the bottom of the web page, before the closing BODY tag we add references to the three JavaScript files that we want to use:

<script src="../Scripts/MindFusion.Common.js" type="text/javascript"></script>
<script src="MindFusion.UI.js" type="text/javascript"></script>
<script src="TabForm.js" type="text/javascript"></script>

The first two of them point to the libraries of the UI controls: MindFusion.Common and MindFusion.UI. The other is a reference to the JS code-behind file.

In order to render the TabControl we need a DIV element. So, we create one and give it an id – that is important:

<div style="position: absolute; top: 0px; left: 0; right: 0; bottom: 0;">
        



<div id="host">
</div>


</div>

We also add a paragraph with an id and red stroke – it will render text, that describes the error fields and the validation message for each one – if the user has provided wrong data.

II. Creating the Tab Control and the Tab Pages

In the code-behind file we first add mappings to the two namespaces we want to use:

var ui = MindFusion.UI;
var d = MindFusion.Drawing

Then we create an instance of the TabControl in code this way:

// Create a new TabControl.
var host = new ui.TabControl(document.getElementById("host"));
host.width = new ui.Unit(70, ui.UnitType.Percent);
host.height = new ui.Unit(70, ui.UnitType.Percent);
host.theme = "light";

We use the id of the DIV element in the constructor of the TabControl Then we use the theme property to refer to the theme that we want to use. The value of the theme property should be the same as the name of the CSS ile that we referenced in the web page. It can point to a custom theme that you have created as long as the names of the file and the property value match.

We create the first TabPage as an instance of the TabPage class:

// Create four templated tab pages and add them to the host's tabs collection.
var tab1 = new ui.TabPage("Owner Details");
// The HTML of the specified page will be set as the innerHTML of a scrollable div inside the tab content element.
tab1.templateUrl = "page1.html";
host.tabs.add(tab1);

We provide the string that will render in the title of the TabPage in the constructor. Then we set the content that the TabPage will have as a url to the web page that contains it e.g. the TabPage loads a content from a page specified with templateUrl Here is the code for the first page:

<div style="padding: 10px;">
        <label for="fname">First Name: </label>
        <input type="text" id="fname" name="First name" placeholder="John" title="Your first name" required"="">
</div>




<div style="padding: 10px;">
        <label for="lname">Last Name: </label>
        <input type="text" name="Last name" id="lname" placeholder="Smith" title="Your family name" required="">
</div>




<div style="padding: 10px;">
        <label for="citizen_id">Citizen Id:</label><input id="citizen_id" type="number" name="Citizen number" size="10" pattern="[0-9]{10}" min="1000000000" max="9000000000" placeholder="0123456789" title="10 digits" required="">
</div>


In terms of HTML, we have provided each input element with an id, a placeholder value and the necessary restrictions that will validate its content. We strictly use and follow the Validation API of JavaScript, which you can check here: https://www.w3schools.com/js/js_validation_api.asp and here https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation.

III. Data Submission and Validation

On the last tab of the form, we have placed a submit button:

<div style="padding: 10px;">
        <button id="submit">Send Data</button>
</div>

We wire the event handler of the click action for this button in the contentLoad event of the fourth tab, where the button is:

tab4.contentLoad.addEventListener(tabLoad);
..........................
..........................
function tabLoad(sender, args) {

    let current_datetime = new Date();
    let formatted_date = current_datetime.getFullYear() + "-" + (current_datetime.getMonth() + 1) + "-" + current_datetime.getDate();
    sender.element.querySelector("#start").value = formatted_date;
    sender.element.querySelector("#submit").addEventListener("click", function () {
    submitData(sender);
    });
}

In the event handler we get the current date and format it the way the default DateTime picker HTML control expects to get it. We get each input control through its id and the querySelector of the HTML Document object. The sender in this case is the fourth tab or tab4.

The method that validates the content is submitData:

function submitData(sender) {
    var txt = "";

    var inputObj = tab4.element.querySelector("#start");

    if (!inputObj.checkValidity()) {
        txt += inputObj.name + ": ";
        txt += inputObj.validationMessage + "
";
        inputObj.style["border-color"] = "red";
        host.selectedItem = tab4;
        dataIsCorrect = false;
    } else
        inputObj.style["border-color"] = "gray";
        ....................................

We use querySelector once again to get the input fields on each page one by one. For each one we see if the validity check has failed. If yes, we outline this field in red and append the validation message to a text variable.

We walk through all tabs and all input fields in this same manner and in reverse order. Our aim is that the first tab with error gets selected, even if there are errors in fields further in the form.

Note that if the field is OK we set its border to the default color. This way we reset the appearance of fields that were previously wrong but the user has corrected.

Finally, we assign the text to the content of the paragraph that renders text in red:

...............................
document.getElementById("error").innerHTML = txt;  

    if (txt.length === 0)
        confirmData();

If no errors have been detected – that means the error text is an empty string – we submit the data. The data submission is handled by the confirmData method:

function confirmData() {

    //first tab
    tab1.element.querySelector("#fname").value = "";
    tab1.element.querySelector("#lname").value = "";
    tab1.element.querySelector("#citizen_id").value = "";
    ........................................
    ........................................
     //fourth tab
    tab4.element.querySelector("#duration").value = "";
    let current_datetime = new Date();
    let formatted_date = current_datetime.getFullYear() + "-" + (current_datetime.getMonth() + 1) + "-" + current_datetime.getDate();
    tab4.element.querySelector("#start").value = formatted_date;

    ui.Dialogs.showInfoDialog("Confirmation", "Your info has been submitted!", null, host.element, host.theme);

}

We reset the values of all input fields and we show an instance of MindFusion InfoDialog to inform the user that their data has been successfully collected.

You can download the source code of the sample and all MindFusion themes and libraries used from this link:

http://mindfusion.eu/samples/javascript/ui/TabForm.zip

You can ask technical question about MindFusion JavaScript developer tools at the online forum at https://mindfusion.eu/Forum/YaBB.pl.

About MindFusion JavaScript UI Tools: MindFusion UI libraries are a set of smart, easy to use and customize JavaScript UI components. Each control boasts an intuitive API, detailed documentation and various samples that demonstrate its use. The rich feature set, multiple appearance options and numerous events make the UI controls powerful tools that greatly facilitate the programmers when building interactive JavaScript applications. MindFusion UI for JavaScript is part of MindFusion JavaScript Pack. You can read details at https://mindfusion.eu/javascript-pack.html.

The Different Ways to Style a Schedule

In this blog post we will look at the different levels of sty‌ling the elements and items of a schedule made with a MindFusion scheduling library. In our sample we use the Java Scheduling Library, but the API members that we use and the cascading levels of styling a schedule are universal across all MindFusion Scheduling components.

I. Top Level: Theme

At the top level of styling you have themes. The scheduling library has a set of predefined themes: Light, Lila, Silver, Standard, Vista, Windows2013. You apply one of the predefined themes this way:

calendar.setTheme(ThemeType.Vista);

Here is what the calendar looks like styled with the vista theme:

In JavaScript, the themes are defined in CSS styles and you must reference the desired file and set the name of the theme to the calendar instance. In the other libraries, they are built-in and usually are available as members of an enum.

You have the option to create a custom theme, which you can save and apply this way:

calendar.setCustomTheme(new MyTheme());

Another way to apply the custom theme in Java Scheduling is:

calendar.setCustomTheme(new MyTheme());

Here is the look of the calendar with the custom theme:

The custom theme has changed some of the colors, widened the header so that the week days could be seen and set some borders on the cells.

Creating a custom Theme requires that you override a strict set of methods. They are the public members of the Theme class. You should override all of them. Among the methods that style the calendar for each view – getMonthRangeSettings getMonthSettings etc. You could override in detail only the method that is responsible for styling the view you would use – if it is only one. For the rest of the methods you could just write:

private void initialize_MyTheme_ListViewSettings()
{
	_listViewSettings = super.createListViewSettings();
}

Every method in the Theme class must be dutifully implemented and all settings set. That comes from the fact that a Theme is the last and topmost styling instrument and it must know how to style any element that might not be explicitly styled down the tree.

The online and offline documentations of the Java Scheduling library come with topics that list in details the styling settings for each of the predefined themes. Our advice is that you get the code of the Theme that looks closest to what you want to have as a structure and modify it.

The sample project that you can download at the bottom of this post implements a custom Theme based on the Vista theme and lists all members in a theme that you must set with all details.

II. View and Item Settings

One level down the tree are the view settings properties. They are available for any view. You can access those settings with the getMonthSettings getMonthRangeSettings etc. methods. Each one of those methods returns the styling settings of a particular view. You should use the one that corresponds to the view you’ve chosen:

//set the view to SingleMonth
calendar.setCurrentView(CalendarView.SingleMonth);
//get the styling settings for SingleMonth view
calendar.getMonthSettings().getDaySettings().setTodayFillColor(Color.green);

You can style the items, regardless of the view used, with the ItemSettings object:

calendar.getItemSettings().setPadding(20);

The *Settings properties define the appearance of items in terms of alignment, spacing, padding, shadow, date format. The coloring of the elements is left to Style instances. Thus, if you want to change the color of items, you will use:

//customize just the items through the itemSettings field
calendar.getItemSettings().setPadding(20);
		
Style itemStyle = new Style();
itemStyle.setBrush(new SolidBrush(Color.white));
itemStyle.setHeaderTextColor(Color.DARK_GRAY);		
itemStyle.setHeaderTextShadowStyle(ShadowStyle.None);
calendar.getItemSettings().setStyle(itemStyle);

This styles all items on the calendar. For styling a particular item, you should use on of the methods listed underneath.

Our calendar now has green header on the current day, the background of events is white and there is a bit of a padding added to the events.

III. Using Events to Style Particular Items

When you want to select items that you want to style based on some distinct characteristics, you can use events. In our sample we handle the itemCreated event, where we check if the start date of an appointment happens to be during the weekend:

// Listen for item creation and for draw events
calendar.addCalendarListener(new CalendarAdapter(){
	//apply custom styling to selected items
	public void itemCreated(ItemEvent e) {
		onItemCreated(e);
	}				
});

The Java Scheduler provides various events, which are accessible through a CalendarListener and CalendarAdapter instances. We handle the itemCreated event this way:

//color in red events that are scheduled to start on weekends
protected void onItemCreated(ItemEvent e) {

	Item item = e.getItem();
	if(item.getStartTime().getDayOfWeek() == DayOfWeek.Saturday || 
		item.getStartTime().getDayOfWeek() == DayOfWeek.Sunday)
		{
			item.getStyle().setBrush(new SolidBrush(new Color(213, 28, 32)));
			item.getStyle().setHeaderTextColor(Colors.White);
			item.getPointedStyle().setBrush(new SolidBrush(new Color(100, 100, 100)));
		}	
}

The ItemEvent class provides the item that was created and you can use the instance to apply any particular styling to the item.

Here is our scheduler, which now colors the items on weekends in red:

In JavaScript, the items have a special field that allows you to assign to them a custom CSS style that you’ve defined. The style will be applied to the particular item only. The field is called ‘cssClass’.

IV. Custom Drawing

When you need to style in a very unique way calendar elements and nothing else helps, you have the option to draw them the way you want. Custom drawing can be made for many parts of the calendar. The available elements are identified as members of the CustomDrawElements enumeration.

You tell the control that you want to use custom drawing this way:

//specify that we will use custom drawing	
calendar.setCustomDraw(EnumSet.of(CustomDrawElements.CellContents));

The custom drawing must be part of the draw method, which is also a member of CalendarListener:

// Listen for item creation and for draw events
calendar.addCalendarListener(new CalendarAdapter(){
				
			
//add custom drawing to CellContents
@Override()
public void draw(CalendarDrawEvent e) {
	onDraw(e);
	} 
			
});

The event handler method looks like this:

//apply custom drawing to selected items
private void onDraw(CalendarDrawEvent e)
{
	if (e.getElement() == CustomDrawElements.CellContents)
	{
		DateTime date = e.getDate();		
		
		//color in light yellow the background of the first 10 days of a month
		if (date.getDay() &lt; 11)
		{
			// Do the custom drawing
			Rectangle2D bounds = new Rectangle2D.Double(
			e.getBounds().getX(), e.getBounds().getY(),
			e.getBounds().getWidth() - 1, e.getBounds().getHeight() - 1);
			new AwtGraphics(e.getGraphics()).fillRectangle(Brushes.LightYellow, bounds);
		}
	}
}

The Calendar’s drawEvent class gives us useful methods to learn more about the item that is being drawn. In our case we want to draw the cell contents, so we check if draw was called for the cell contents, and if yes, we get the bounds of the element. We need the check, because draw is called for all elements that support custom drawing and we need to identify which one is drawn at the moment.

Thanks to the custom drawing, the monthly schedule now has a light yellow background on the first ten days of the month:

With this our review of the methods to style a schedule is finished. You can download the complete source code of the sample together with all MindFusion libraries used from this link:

How to Style a Java Schedule: Download Project Source Code

You can post questions about Mindusion Scheduling components at MindFusion online forums.

About MindFusion Scheduling Components MindFusion Scheduling components are available for a variety of platforms for web, mobile and desktop programming. All of them include a robust feature set that includes 6 calendar views, predefined themes, various events, predefined forms for creating appointments and recurrence. The components are fully interactive, easy to customize and style and are the ideal choice for any application that needs to implement time management features. You can learn more about MindFusion Scheduling tools at https://mindfusion.eu/scheduling-pack.html.

Cost Meter Gauge in JavaScript

In this post we will look at the steps we need to make if we want to create this beautiful gauge below:

The gauge is done with MindFusion Charts and Gauges for JavaScript library. You can download the sample together with all needed libraries from this link.

I. Project Setup

We will build the gauge using the OvalGauge library from MindFusion JS Charts and Gauges control. We add two references, needed for the control to work properly:

<script type="text/javascript" src="Scripts/MindFusion.Common.js"></script>
<script type="text/javascript" src="Scripts/MindFusion.Gauges.js"></script>

We have placed those files in a Scripts folder. We will write the JavaScript code for the gauge in a separate file, which we call ValueGauge.js. This file is at the same directory where the web page is. We add a reference to it as well:

<script type="text/javascript" src="ValueGauge.js"></script>

The web page with our sample contains a table. We use the table to place the control together with a text box. The text box is not needed, but we will use it to give the user the option to set the value of the gauge by typing it not only by dragging the pointer.

<table cellpadding="10">
<tbody>
<tr>
<td>Project Cost (in thousands)</td>
</tr>
<tr>
<td><canvas id="value_meter" width="300" height="300"></canvas></td>
</tr>
<tr>
<td>Cost <input id="cost" style="width: 80px"></td>
</tr>
</tbody>
</table>

The gauge will be rendered through an HTML Canvas element. The location and size of the Canvas determine the location and the size of the gauge. It is important that we add an id the Canvas – this way we can reference it in the JavaScript code page, which will be necessary.

II. The Control

Now we start editing the ValueGauge.js file and there we first add mappings to the namespaces of Mindusion.Gauges.js that we will use:

/// <reference path="Scripts/MindFusion.Charting-vsdoc.js">
var Gauges = MindFusion.Gauges;

var d = MindFusion.Drawing;
var OvalScale = Gauges.OvalScale;
var Length = Gauges.Length;
var LengthType = Gauges.LengthType;
var Thickness = Gauges.Thickness;
var Alignment = Gauges.Alignment;
var LabelRotation = Gauges.LabelRotation;
var TickShape = Gauges.TickShape;
var PointerShape = Gauges.PointerShape;</reference>

The first line is a reference to the Intellisense file that allows us to use code completion of the API members, if supported by our JavaScript IDE.

Now we need to get the DOM Element that corresponds to the gauge Canvas and use it to create an instance of the OvalGauge class:

var value_meter = Gauges.OvalGauge.create(document.getElementById('value_meter'), false);

III. Gauge Scales

Gauge scales depend on the type of the gauge. For oval gauges we use OvalScale The OvalScale needs to be associated with a gauge and here is how we create it:

var valueScale = new Gauges.OvalScale(value_meter);
valueScale.setMinValue(0);
valueScale.setMaxValue(100);
valueScale.setStartAngle(120);
valueScale.setEndAngle(420);

The OvalScale class offers the full set of properties needed to customize the scale. We use the setMinValue and setMaxValue methods to specify the value range o the gauge. The setStartAngle and setEndAngle specify the arc of the gauge and we set them to 120 and 420 respectively. You see that the arc is 300 degrees, which is less than a full circle – exactly how we want it to be.

We continue our customization by setting the fill and stroke of the scale. We actually do not want the default scale to be rendered at all, so we use setFill and setStroke to specify transparent colors:

valueScale.setFill('Transparent');
valueScale.setStroke('Transparent');
valueScale.setMargin(new Gauges.Thickness(0.075, 0.075, 0.075, 0.075, true));

Now we can continue with the ticks. Each gauge can have major, middle and minor ticks. Those ticks are not rendered by default.

var majorSettings = valueScale.majorTickSettings;
majorSettings.setTickShape(Gauges.TickShape.Line);
majorSettings.setTickWidth(new Gauges.Length(10, Gauges.LengthType.Relative));
majorSettings.setTickHeight(new Gauges.Length(10, Gauges.LengthType.Relative));
majorSettings.setFontSize(new Length(14, LengthType.Relative));
majorSettings.setNumberPrecision(0);
majorSettings.setFill('rgb(46, 52, 66)');
majorSettings.setStroke('rgb(46, 52, 66)');
majorSettings.setLabelForeground('rgb(175, 175, 175)');
majorSettings.setLabelAlignment(Alignment.InnerCenter);
majorSettings.setLabelRotation(LabelRotation.Auto);
majorSettings.setLabelOffset(new Length(6, LengthType.Relative));
majorSettings.setStep(20);
majorSettings.setTickAlignment (Alignment.OuterOutside);

We start the customization with the majorTickSettings They will render labels and want to have one tick with a tep of 20. So, we use setStep to specify 20 as an interval and use setTickWidth and setTickHeight to set the size of the ticks. Those properties can be set to an absolute or relative value – see the LengthType enumeration. We also need to change the default shape of the pointer – we use TickShape rest of the settings are intuitive – setFill and setStroke specify how the ticks are colored. We also use setLabelAlignment to position the labels outside the ticks. setTickAlignment is also an important property -it allows us to change the alignment of the ticks, so they can be drawn inside the scale.

The TickSettings object is similar to MajorTickSettings

var middleSettings = valueScale.middleTickSettings;
middleSettings.setTickShape(TickShape.Line);
middleSettings.setTickWidth(new Gauges.Length(10, Gauges.LengthType.Relative));
middleSettings.setTickHeight(new Gauges.Length(10, Gauges.LengthType.Relative));
middleSettings.setTickAlignment (Alignment.OuterOutside);
middleSettings.setShowTicks(true);
middleSettings.setShowLabels(false);
middleSettings.setFill('rgb(46, 52, 66)');
middleSettings.setStroke('rgb(46, 52, 66)');
middleSettings.setCount(5);

We should note here that setShowLabels is false because we want the labels to appear only at intervals of 20. We also use setCount to specify how many ticks we want between each two adjacent major ticks. The rest of the settings are the same as for MajorTickSettings.

IV. Custom Painting

The painting of the colored sections at the edge of the gauge is custom code. The gauges library provides various events that allow the developer to replace the default gauge drawing with custom drawing – see the Events section of the OvalGauge class.

In our sample we will handle two events:

value_meter.addEventListener(Gauges.Events.prepaintBackground, onPrepaintBackground.bind(this));
value_meter.addEventListener(Gauges.Events.prepaintForeground, onPrepaintForeground.bind(this));

prepaintBackground is raised before the background is painted. We can cancel the default painting or add some custom drawing to it. The same is true for prepaintForeground

function onPrepaintBackground(sender, args)
{
	args.setCancelDefaultPainting(true);

	var context = args.getContext();
	var element = args.getElement();
	var bounds = new d.Rect(0, 0, element.getRenderSize().width, element.getRenderSize().height);
        ..................................
}

In the prepaintBackground event handler we first get the handler to the CanvasRenderingContext2D instance. Then we get the bounds of the painted element. This is the inside of the gauge. Each o the colored segments is pained as an arc. We do not create a path figure to fill – instead we set a very thick lineWidth of the stroke:

context.lineWidth = 25;
var correction = context.lineWidth / 2;
	
//light green segment
context.beginPath();
context.strokeStyle = 'rgb(0, 205, 154)';
context.arc(bounds.center().x, bounds.center().y, bounds.width / 2-correction, 0.665*Math.PI, 1*Math.PI, false);	
context.stroke();

We go on painting this way all colored sections of the gauge, only changing the start and end angles. When we are ready we paint the inside of the gauge. We do it with a full arc:

context.beginPath();
bounds.x += 25;
bounds.y += 25;
bounds.width -= 50;
bounds.height -= 50;
context.fillStyle = '#2e3442';

context.arc(bounds.center().x, bounds.center().y, bounds.width / 2, 0*Math.PI, 2*Math.PI, false);
context.fill();

The complete drawing is done inside the prepaintBackground event handler. So, in the prepaintForeground handler we only need to cancel the default painting:

function onPrepaintForeground(sender, args)
{
    args.setCancelDefaultPainting(true);

};

V. The Gauge Pointer

We need to add a Pointer to the OvalScale of the gauge instance if we want to show one:

var pointer = new Gauges.Pointer();
pointer.setFill('white');
pointer.setStroke("#333333");

pointer.setPointerWidth(new Gauges.Length(90, Gauges.LengthType.Relative));
pointer.setPointerHeight(new Gauges.Length(20, Gauges.LengthType.Relative));

pointer.setShape(Gauges.PointerShape.Needle2);
pointer.setIsInteractive(true);

valueScale.addPointer(pointer);

The size of the pointer is also set in LengthType units. This allows us to have the same pointer size relative to the size of the gauge even if we change the size of the Canvas. We use the PointerShape enumeration to specify the type of pointer we want and then we make it interactive with setIsInteractive As an addition to the default needle of the pointer we want to render a circle at the base of the pointer. We do it with custom drawing:

value_meter.addEventListener(Gauges.Events.prepaintPointer, onPrepaintPointer.bind(this));

First we need to handle the prepaintPointer event. In the event handling code we do the drawing:

function onPrepaintPointer(sender, args)
{	
	//args.setCancelDefaultPainting(true);

	var context = args.getContext();
	var element = args.getElement();
	var size = element.getRenderSize();
	var psize = new d.Size(0.2 * size.width, size.height);

	context.save();
	context.transform.apply(context, element.transform.matrix());

	context.beginPath();
	context.arc(psize.width / 2, psize.height / 2, psize.height*0.75, 0, 2 * Math.PI, false);
	var fill = element.getFill();
	context.fillStyle = Gauges.Utils.getBrush(context, fill, new d.Rect(0, 0, size.width, size.height), false);
	context.fill();
	context.strokeStyle = '#333333';
	context.stroke();

	context.restore();
};

Note that in this case we do not cancel the default painting – we will add to it, rather than replace it. Then we get the CanvasRenderingContext2D and size of the rendered element. What is new here is the transform of the CanvasRenderingContext2D object to the center of the gauge. Then we get the Brush that is used to paint the rest of the pointer and use it to fill the custom part as well. We can set the brush directly, but we prefer to take it from the base element – the Pointer This way if we change settings of the Pointer the color of the custom drawn circle will change automatically as well.

VI. Data Binding

What we would like to do now is bind a change in the text field to the value of the gauge scale. We add a method that does it:

function valueChanged(id)
{
	if (isNaN(this.value)) return;
	var gauge = Gauges.OvalGauge.find(id);
	var pointer = gauge.scales[0].pointers[0];
	pointer.setValue(+this.value);
};

When we call the valueChanged method with the instance of the OvalGauge as an argument, we can get its pointer and set its value to the value of ‘this’. We call the valueChanged in such way, that the ‘this’ reference will be the text field:

var cost = document.getElementById('cost');
cost.onchange = valueChanged.bind(cost, ['value_meter']);

Now when the value changes, the event handler takes the pointer and set its value to the value the user has types.

That is the end of this tutorial. You can download the source code of the sample, together with all MindFusion libraries used from the following link:

Download Value Gauge in JavaScript Source Code

You can use the discussion forum to post your questions, comments and recommendations about the sample or MindFusion charts and gauges.

About MindFusion JavaScript Gauges: A set of two gauge controls: oval and rectangular, with the option to add unlimited nuber of scales and gauges. All gauge elements support complete customization of their appearance. Custom drawing is also possible, where you can replace the default rendering of the gauge element or add to it. The gauge controls include a variety of samples that offer beautiful implementations of the most popular applications of gauges: thermometer, car dashboard, functions, compass, clock, cost meter and more.
Gauges for JavaScript is part of MindFusion charts and Dashboards for JavaScript. Details at https://mindfusion.eu/javascript-chart.html.

Pie Chart with Custom Labels in WinForms

In this tutorial we will build the following pie chart using MindFusion Charts and Gauges for WinForms library:

This is a PieChart with a custom Series class that extends the capabilities of a standard PieSeries We need to create a custom Series class because we want the inner labels to follow a special format and we want the outer labels to be rendered as legend items not near pie pieces.

You can download the source code of the sample together with the MindFusion libraries used from the link at the bottom of the post.

I. General Setup

We create an empty WinForms application in C# with Visual Studio. We create a folder called References and there we copy the MindFusion.*.dll -s that we will need. They are:

MindFusion.Charting.dll
MindFusion.Charting.WinForms.dll
MindFusion.Common.dll

We reference those files in our project: we right-click the References folder in the Solution Explorer and click on “Add Reference”. Then we browse to our local References folder and add the 3 dll-s.

We have installed MindFusion Charts and Gauges from the website: http://mindfusion.eu/ChartWinFormsTrial.zip and now we need only to find the PieChart control, drag and drop it onto the WinForms Form of our application.

II. The Custom Pie Series

When you create a custom series you need to implement the Series interface. You need to declare several methods and properties, and one event. Let’s start with the constructor.

We need our chart to use one array with data and one array with labels. We declare two class variables for that and assign to them the values that we receive for them in the constructor. We name the new class CustomPieSeries:

public class CustomPieSeries : Series
{
	public CustomPieSeries(IList data, IList legendLabels )
	{
		values = data;		
		_legendLabels = legendLabels;

		//sum up all pie data
		total = 0.0;
		for (int i = 0; i &lt; data.Count; i++)
			total += data[i];
	}

	IList values;	
	IList _legendLabels;
        double total = 0L;
}

We have added a new class variable called total. It is needed to keep the sum of all data for the chart. We will use this variable when we calculate the percent value of each pie piece.

The SupportedLabels property of type LabelKinds is the way to set which labels will be rendered by the new series. We want tooltips, inner labels and legend labels. There is no special enum field for legend labels. You just set which labels form the series should be rendered as legend items. We decide to use for this the ZAxisLabel, because it is not drawn anywhere on the pie chart and we won’t see it doubled by the legend labels. So, we say that the chart supports ZAxisLabels and we will tell the series that the ZAxisLabels must be rendered in the legend. We will do this later.

public LabelKinds SupportedLabels
{
	get { return LabelKinds.InnerLabel | LabelKinds.ToolTip | LabelKinds.ZAxisLabel; }
}

The GetLabel method is the place where we must return the correct label according to the type and position of the label, which are provided as arguments.

public string GetLabel(int index, LabelKinds kind)
{
	double percent = (values[index] / total) * 100;
	if (kind == LabelKinds.InnerLabel)
		return percent.ToString("F2") + "%\n" + values[index].ToString();
	if (kind == LabelKinds.ToolTip)
		return "Number of interns: " + values[index].ToString("F0") + 
			"\nPercent of total: " + percent.ToString("F2") + "%";

	if (kind == LabelKinds.ZAxisLabel)
		return _legendLabels[index].ToString();

	return null;
}

Here we have the chance to work over the raw data that we have for the series and return the desired labels as a string. We want the inner label to appear as the data value together with its percent representation. We calculate the percent thanks to the total variable and format it in an appropriate way:

 if (kind == LabelKinds.InnerLabel)
	return percent.ToString("F2") + "%\n" + values[index].ToString();

We do the same with the tooltips. We add an explanation text to the tooltip of each piece:

if (kind == LabelKinds.ToolTip)
	return "Number of interns: " + values[index].ToString("F0") + 
		"\nPercent of total: " + percent.ToString("F2") + "%";

The ZAxisLabel is the easiest to do. It will be used by the legend and we perform no special formatting upon it – we just return the label corresponding to the given index.

if (kind == LabelKinds.ZAxisLabel)
		return _legendLabels[index].ToString();

Among the other notable members of the Series interface are the Dimensions and Title properties. Dimensions is the property that specifies how many data dimensions the series has. They are 1 for charts that use one array of data, 2 for axes that use X and Y data, and 3 for 3D charts, which need X, Y and Z data. In our case we return 1 as property value because pie charts, just like radar charts, use only one data array.

public int Dimensions
{
	get { return 1; }
}

The Title property returns the Series title. This is an important property but in our case we will not use it. A common use case for Title is to be rendered in legends. We will not render the Series title in the legend, so we return an empty string.

public string Title
{
	get { return ""; }
}

Building the CustomPieSeries is an easy task:

var values = new List { 23, 54, 17, 9 };
pieChart1.Series = new CustomPieSeries(
	values,				
	new List()
	{
		" &lt;1 month", " &lt;=3 months", " &lt;=6 months", " &gt;6 months"
	}
);

We create a new instance of our new class and assign it to the Series property of the PieChart control. We provide a list with the data and the labels that we want to appear as a legend.

III. The Legend

The legend in the PieChart control is regulated by the LegendRenderer property. We set the ShowSeriesElements property to true to let the chart know that we want the data from the series to be rendered as labels and not the title:

pieChart1.LegendRenderer.ShowSeriesElements = true;

Then we use the ElementLabelKind property to specify which labels we want to use in the legend. These are the ZAxisLabels:

pieChart1.LegendRenderer.ElementLabelKind = LabelKinds.ZAxisLabel;

The other properties are self-explanatory. We use the Title property to set the legend title and set a transparent brush for both the Background and the BorderStroke = “Duration”;

pieChart1.LegendRenderer.Background = new SolidBrush(Color.Transparent);
pieChart1.LegendRenderer.BorderStroke = new SolidBrush(Color.Transparent);

IV. Styling

Styling the chart is done through the Theme property and through styles. There are different style classes available and in our case we will use the PerElementSeriesStyle class. This class accepts as arguments for the brushes and strokes lists with lists that contain the brushes. Then, it colors each element in each series with the corresponding brush in the array at the corresponding index. Our PieChart needs just one list with brushes and strokes. The stroke thicknesses are also added as nested arrays:

pieChart1.Plot.SeriesStyle = new PerElementSeriesStyle()
{
	Fills = new List&lt;List&gt;()
	{
		new List()
		{
			new SolidBrush(Color.FromArgb(158, 212, 224)),
			new SolidBrush(Color.FromArgb(187, 236, 247)),
			new SolidBrush(Color.FromArgb(212, 204, 196)),
			new SolidBrush(Color.FromArgb(245, 210, 179))					         }
	},

	Strokes = new List&lt;list&gt;()
	{
		new List()
		{
			new SolidBrush(Color.White)
						
		}
	},
	StrokeThicknesses = new List&lt;list&gt;()
	{
		new List()
		{
			3.0
		}
	}
};

We also set some appearance properties through the Theme field:

pieChart1.Theme.DataLabelsFontSize = 10;
pieChart1.Theme.HighlightStroke = new SolidBrush(Color.FromArgb(237, 175, 120));
pieChart1.Theme.HighlightStrokeThickness = 4;

The HighlightStroke is used to mark the selected chart element by mouse hover. The DataLabelsFontSize is used not only by painting the inner labels but also by painting the labels in the legend.

Finally we set the Title of the chart:

pieChart1.Title = "Internship Statistics";

And that is the end of this tutorial. You can download the code for the chart together with the MindFusion.*.dll libraries used from this link:

Pie Chart with Custom Labels Source Code Download

You can refer to MindFusion helpful support team with any technical questions regarding the WinForms Charts & Gauges control. Use the forum at: https://mindfusion.eu/Forum/YaBB.pl?board=mchart_disc

About MindFusion Charts and Gauges for WinForms: MindFusion Charts and Gauges for WinForms offers a long list of features that enables developer to build any type of chart, gauge or dashboard. The smart API of the library provides the option different chart elements like plots, axes, labels, and series to be combined and arranged in order to build any type of custom looking chart. The library also supports out of the box the most common chart types together with a set of their widely used properties. The gauge control is indispensable part of the library and offers a linear and oval gauge together with a variety of samples that provide you with the most common types of gauges used: clock, compass, thermometer, car dashboard and more. More about MindFusion Charts and Gauges for WinForms at: https://mindfusion.eu/winforms-chart.html.

Appointment Scheduler in JavaScript

In this blog post we will build from scratch an appointment schedule for 4 practitioners. Each appointment is logged with the patient name and contact details. Each appointment can be scheduled in one of 4 rooms. We will also implement a check to see if the room that we want to assign to an appointment is free at the particular time.

You can run the sample online from the link below:

I. Project Setup

The first thing we’ll do is to create a DIV element and assign it an id. The JS Schedule library needs and HTML div element where the timetable will be rendered. We create one:

<div id="calendar" style="height: 100%;width: 100%"></div>

You can position the div element wherever you wish. It’s location and size determine the location and the size of the schedule.

Next, we need to reference the Schedule library file. It is called MindFusion.Scheduling. We reference it at the end of the web page, right after the closing BODY tag:

<script src="scripts/MindFusion.Scheduling.js" type="text/javascript"></script>
<script src="AppointmentSchedule.js" type="text/javascript"></script>

We will write the JavaScript code for the appointment schedule in a separate JS file, which we call AppointmentSchedule. We have added a reference to it as well.

II. Schedule Settings

In the JavaScript code behind file we first create a mapping to the MindFusion.Scheduling namespace and a reference to the Intellisense file:

/// <reference path="MindFusion.Scheduling-vsdoc.js">

var p = MindFusion.Scheduling;</reference>

Next, we use the id of the DIV element to create an instance of the Calendar class:

// create a new instance of the calendar
calendar = new p.Calendar(document.getElementById("calendar"));

We set the currentView property of the calendar to CalendarView Timetable:

// set the view to Timetable, which displays the allotment 
// of resources to distinct hours of a day
calendar.currentView = p.CalendarView.Timetable;

We use the timetableSettings property to specify the time range for each day. The starttime and endTime properties set exactly the begin and end of the time interval rendered by the timetable columns. They are measured in minutes, from midnight of the day they refer to. We want the schedule to start from 7 A that is why we set 420 as value to the startTime property – the minutes in 7 hours.

calendar.timetableSettings.startTime = 420;
calendar.timetableSettings.endTime = 1260;

The titleFormat property specifies how the date at each timetable column will be rendered. The format string follows the standard date and time pattern for JavaScript:

calendar.timetableSettings.titleFormat = "dddd d MMM yyyy";
calendar.itemSettings.tooltipFormat = "%s[hh:mm tt] - %e[hh:mm tt] %h (Contact: %d)";

The tooltipFormat follows a custom formatting pattern, used by Js Scheduler. It supports special format strings like:

  • %s for start time
  • %e for end time
  • %h for header e.g. the text of the item header
  • %d for description: the text that was assigned as a description of the appointment.

III. Contacts, Locations and Grouping

The 4 practitioners are instances of the Contact class:

resource = new p.Contact();
resource.firstName = "Dr. Lora";
resource.lastName = "Patterson";
resource.tag = 2;
calendar.schedule.contacts.add(resource);

It is important to add them to the contacts property of the schedule. The rooms where the appointments take place are Location instances:

resource.name = "Room 112";
calendar.schedule.locations.add(resource);

The grouping of the data that is rendered by the timetable is done is a method called group:

function group(value) {
	calendar.contacts.clear();
	if (value == p.GroupType.GroupByContacts) {
		// add the contacts by which to group to the calendar.contacts collection
		calendar.contacts.addRange(calendar.schedule.contacts.items());
	}
	calendar.locations.clear();
	if (value == p.GroupType.GroupByLocations) {
		// add the locations by which to group to the calendar.locations collection
		calendar.locations.addRange(calendar.schedule.locations.items());
	}
	calendar.groupType = value;
}

When we’ve created the locations and contacts, we added them to the locations and contacts collections of the schedule property of the Calendar . Grouping of the appointments is done based on the contacts and locations collections of the Calendar (not the schedule ). That is why in the group method we clear the data from the respective collection and add to it all data from the corresponding collection in the schedule Of course, we must set the groupType property to the appropriate GroupType value.

IV. Appointments

When the user selects a range of cells the new Appointment dialog appears automatically. There they can enter all necessary data. We want to implement check if a given room is free when the user tries to create a new appointment in this room. We will do the check in the handler of the itemCreating event. The itemCreating event is raised when the new item has not been ready yet and the ItemModifyingEventArgs object that is provided to the event handler gives the opportunity to cancel the event:

calendar.itemCreating.addEventListener(handleItemCreating);

function handleItemCreating(sender, args)
{
	var appLocation = args.item.location;
	
	if(appLocation != null )
	{
		if(appLocation.name != "")
		{
			var items = calendar.schedule.items.items();
			for(var i = 0; i &lt; calendar.schedule.items.count(); i++)
			{
				if( items[i].location == null)
					continue;
				
				//if the location is the same as the location of another appointment
				//at that time we cancel the creating of the appointment
				if( items[i].location.name == appLocation.name &amp;&amp; 
				overlappingAppointments (args.item, items[i]))
				{
					args.cancel = true;
					alert("The room is already taken");
				}
	
			}
		}
	}
}

We use a helper method called overlappingAppointments, whose only task is to compare the time range of two items and return true if their time span overlaps – entirely or partially.

/* checks if the time allotted to two different appointments overlaps */
function overlappingAppointments(item1, item2)
{
	if( item1.startTime &lt; item2.startTime &amp;&amp;
	    item1.endTime &lt; item2.endTime )
		  return false;
		  
	if( item1.startTime &gt; item2.endTime &amp;&amp;
	    item1.endTime &gt; item2.endTime )
		  return false;	
		  
		  return true;	  		
}

V. Timeline

Our timetable renders one day at a time. When the user wants to add an appointment that is due in 10 days, they will need to do a lot of scrolling. We can solve the problem by adding a date list at the top o the timetable. The list is another Calendar instance and but its currentView is set to CalendarView List.

We first need to add another DIV element that will be used by the constructor of the new Calendar:

<div id="datePicker" style="height: 25px; display: inline-block; margin-bottom: 10px"></div>

Then we create new Calendar object and make it render a list with dates:

datePicker = new p.Calendar(document.getElementById("datePicker"));
datePicker.currentView = p.CalendarView.List;

By default each Calendar renders the current date when it starts. We make it display a succession of 30 days. We want each day to have a prefix that indicates its week day. In addition, we hide the header of the calendar and stop the default “New Appointment” form from rendering when the user clicks on a cell:

datePicker.listSettings.visibleCells = datePicker.listSettings.numberOfCells = 30;
datePicker.listSettings.headerStyle = p.MainHeaderStyle.None;
datePicker.listSettings.generalFormat = "ddd d";
datePicker.useForms = false;

How do we “wire” the selected date in the timeline to the active date in the timetable? We handle the selectionEnd event and there we assign the selected date from the timeline as the active date of the timetable:

function handleSelectionEnd(sender, args) {
	var startDate = args.startTime;
	var endDate = args.endTime;

	// show the selected date range in the timetable
	calendar.timetableSettings.dates.clear();
	while (startDate &lt; endDate) {
		calendar.timetableSettings.dates.add(startDate);
		startDate = p.DateTime.addDays(startDate, 1);
	}
}

A timetable renders those dates, that are added to its dates property. We add just one date – the date that was selected in the list.

Let’s not forget to call the render method once we’ve finished all customizations on both Calendar render the calendar control

calendar.render();
//render the timeline control
datePicker.render();

VI. Styling

The general styling of the two Calendar instances is done with one of the predefined themes of the Js Scheduler library. First, we need to add a reference to the CSS file, where it is defined. We’ve chosen the “pastel” theme, which is defined in pastel.css:

 	 	<link rel="stylesheet" type="text/css" href="themes/pastel.css">

Then we need only to set its name as a plain string to the theme property of the two Calendar instances:

calendar.theme = "pastel";
datePicker.theme = "pastel";

There is one more styling that we want to do: we want the appointments of each practicioner to be colored in a different color. We inspect the styling o the appointment element in the browser and find out that the styling of the items is set by the class mfp-item. We create 4 different sub-classes of mfp-item for the 4 practitioners:

.itemClass0 .mfp-item
		{
			background-color: #03738C !important;
			color: #fff !important;
		}
		.itemClass1 .mfp-item
		{
			background-color: #03A6A6 !important;
			color: #fff !important;
		}
..............

Then we need to assign the correct class to the appointments. We will do this with the cssClass property of Item We handle the itemCreated event where we get information for the appointment that was created:

calendar.itemCreated.addEventListener(handleItemCreated);

function handleItemCreated(sender, args)
{
	var contact = args.item.contacts.items()[0];
	
	if(contact != null )
		args.item.cssClass = "itemClass" + contact.tag;

}

The easiest way to assign the correct CSS class to the item is to assign data that will help us generate the correct style name. We use the tag property of Contact and assign each practitioner an id that mirrors the last letter in the CSS class we should assign to the appointments associated with this contact.

With that our appointment application is finished. You can download the full source code with the libraries and styles used from this link:

Appointment Schedule in JavaScript: Source Code Download

About MindFusion JavaScript Scheduler: MindFusion Js Scheduler is the complete solution for all applications that need to render interactive timetables, event schedules or appointment calendars. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. Find out more at https://mindfusion.eu/javascript-scheduler.html