{"id":1431,"date":"2015-12-30T11:11:13","date_gmt":"2015-12-30T11:11:13","guid":{"rendered":"http:\/\/mindfusion.eu\/blog\/?p=1431"},"modified":"2021-01-20T17:12:38","modified_gmt":"2021-01-20T17:12:38","slug":"containernode-fold-unfold-animations","status":"publish","type":"post","link":"https:\/\/mindfusion.dev\/blog\/containernode-fold-unfold-animations\/","title":{"rendered":"ContainerNode fold \/ unfold animations"},"content":{"rendered":"<p>In this post we\u2019ll show how to animate container&#8217;s fold and unfold operations using some event handling and custom drawing. You can download the complete project here:<\/p>\n<p><a href=\"https:\/\/mindfusion.dev\/_samples\/AnimatedFold.zip\">AnimatedFold.zip<\/a><\/p>\n<p>The sample code will demonstrate several features of the Diagram control and .NET:<\/p>\n<ul>\n<li>use LINQ to collect contained items<\/li>\n<li>handle fold\/unfold events<\/li>\n<li>custom draw from DrawForeground event<\/li>\n<li>draw items from custom drawing code<\/li>\n<\/ul>\n<p>Let&#8217;s start by creating some items and containers when the form loads:<\/p>\n<pre>private void Form1_Load(object sender, EventArgs e)\n{\n    var ctr = diagram.Factory.CreateContainerNode(20, 20, 100, 100, true);\n    var node1 = diagram.Factory.CreateShapeNode(30, 35, 15, 15);\n    var node2 = diagram.Factory.CreateShapeNode(80, 45, 15, 15);\n    diagram.Factory.CreateDiagramLink(node1, node2);\n\n    ctr.Add(node1);\n    ctr.Add(node2);\n\n    var ctr2 = diagram.Factory.CreateContainerNode(20, 20, 100, 100, true);\n    ctr2.Add(ctr);\n}\n<\/pre>\n<p>We&#8217;ll use LINQ extensions methods to find all items within a ContainerNode, including ones contained recursively in child containers:<\/p>\n<pre>List GetDescendents(ContainerNode container)\n{\n    var nodes = diagram.Nodes.Where(\n        container.ContainsRecursively);\n\n    var links = diagram.Links.Where(l =&gt;\n        nodes.Contains(l.Origin) ||\n        nodes.Contains(l.Destination));\n\n    return\n        nodes.Cast().Concat(\n        links.Cast()).ToList();\n}\n<\/pre>\n<p>Add handlers for ContainerFolded and ContainerUnfolded events that will start animation for the container:<\/p>\n<pre>void OnContainerFolded(object sender, NodeEventArgs e)\n{\n    var container = (ContainerNode)e.Node;\n    StartAnimation(container, true);\n}\n\nvoid OnContainerUnfolded(object sender, NodeEventArgs e)\n{\n    var container = (ContainerNode)e.Node;\n    StartAnimation(container, false);\n}\n<\/pre>\n<p>The StartAnimation method stores a list of items that should be redrawn during animation and a few other animation attributes:<\/p>\n<pre>void StartAnimation(ContainerNode container, bool fold)\n{\n    var bounds = container.Bounds;\n    var scaleCenter = new PointF(\n        (bounds.Left + bounds.Right) \/ 2, bounds.Top);\n\n    \/\/ collect items that will be unfolded\n    animatedItems = GetDescendents(container);\n\n    \/\/ animation will also draw this rectangle as background\n    ctrBounds = bounds;\n    ctrBounds.Size = container.UnfoldedSize;\n    ctrBounds.Y += container.CaptionHeight;\n    ctrBounds.Height -= container.CaptionHeight;\n\n    \/\/ start animation timers\n    Animate(scaleCenter, fold);\n\n    if (!fold)\n    {\n        \/\/ temporarily fold back when animating unfold operation\n        \/\/ so that contained items stay invisible\n        container.Folded = true;\n        toUnfold = container;\n    }\n}\n<\/pre>\n<p>The Animate method starts a timer whose Tick event invalidates the DiagramView and stops the timer when final frame has been reached:<\/p>\n<pre>void Animate(PointF scaleCenter, bool scaleDown)\n{\n    if (scaleDown)\n    {\n        frameCounter = maxFrames;\n        frameIncrement = -1;\n    }\n    else\n    {\n        frameCounter = 0;\n        frameIncrement = +1;\n    }\n    this.scaleCenter = scaleCenter;\n\n    animationTimer = new Timer();\n    animationTimer.Tick += OnAnimationTimer;\n    animationTimer.Interval = duration \/ maxFrames;\n    animationTimer.Start();\n}\n\nvoid OnAnimationTimer(object sender, EventArgs e)\n{\n    frameCounter += frameIncrement;\n    diagramView.Invalidate();\n    if (frameCounter == 0 || frameCounter == maxFrames)\n    {\n        animationTimer.Stop();\n        animationTimer.Dispose();\n        animationTimer = null;\n        animatedItems = null;\n\n        if (toUnfold != null)\n        {\n            toUnfold.Folded = false;\n            toUnfold = null;\n        }\n    }\n}\n<\/pre>\n<p>Add a DrawForeground event handler that applies scale transform proportional to current frame of animation and draws the container&#8217;s descendants stored in animatedItems list:<\/p>\n<pre>void OnDrawForeground(object sender, DiagramEventArgs e)\n{\n    if (animatedItems != null &amp;&amp; frameCounter &gt; 0)\n    {\n        var options = new RenderOptions();\n        var g = e.Graphics;\n\n        \/\/ apply scale corresponding to current frame\n        var scale = (float)frameCounter \/ maxFrames;\n        g.TranslateTransform(scaleCenter.X, scaleCenter.Y);\n        g.ScaleTransform(scale, scale);\n        g.TranslateTransform(-scaleCenter.X, -scaleCenter.Y);\n\n        \/\/ draw container background\n        g.FillRectangle(Brushes.White, ctrBounds);\n        g.DrawRectangle(Pens.Black, ctrBounds);\n\n        \/\/ draw contained items\n        foreach (var item in animatedItems)\n            item.Draw(e.Graphics, options);\n    }\n}\n<\/pre>\n<p>Same technique can be applied to animate collapse and expand operations on tree branches. To implement that, handle NodeExpanded and NodeCollapsed events instead, and collect items reachable recursively from the branch&#8217; root by following outgoing links.<\/p>\n<p>The code above uses MindFusion\u2019s .NET API and can be used with Windows Forms, WPF, Silverlight and ASP.NET diagramming components. The Java API for Android and desktop Swing application will look similar, with setter method calls instead of property assignments.<\/p>\n<p>You can download the trial version of any MindFusion.Diagramming component from <a href=\"http:\/\/mindfusion.dev\/download-diagramming-pack.html\">this page<\/a>.<\/p>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post we\u2019ll show how to animate container&#8217;s fold and unfold operations using some event handling and custom drawing. You can download the complete project here: AnimatedFold.zip The sample code will demonstrate several features of the Diagram control and &hellip; <a href=\"https:\/\/mindfusion.dev\/blog\/containernode-fold-unfold-animations\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[95,74],"tags":[],"class_list":["post-1431","post","type-post","status-publish","format-standard","hentry","category-diagramming-2","category-sample-code"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3RlKs-n5","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1431","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/comments?post=1431"}],"version-history":[{"count":2,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1431\/revisions"}],"predecessor-version":[{"id":2576,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/posts\/1431\/revisions\/2576"}],"wp:attachment":[{"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/media?parent=1431"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/categories?post=1431"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mindfusion.dev\/blog\/wp-json\/wp\/v2\/tags?post=1431"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}