Page Index Toggle Pages: 1 Send TopicPrint
Normal Topic Problem with pasting clipboard data (Read 885 times)
ABCmmon
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 3
Joined: May 8th, 2024
Problem with pasting clipboard data
May 13th, 2024 at 12:13am
Print Post  
A strange bug has occurred that seems to work fine on earlier versions (6.5.4), we are using version 6.8.2.

The pasteToClipboard function fails, when copying newly created diagram items from the clipboard.
Exception in the mindfusion.diagramming.dll when creating pasted items from de-serialized stream, see PersistContext.createItem().

Seems like something is going wrong during de-serialization, its working fine when copying items loaded from xml. But fails when newly created items are de-serialized from the clipboard.
We are using version 6.8.2 , maybe this has been a known issue that is already solved in a later release?

Stack trace:
Code
Select All
at MindFusion.Diagramming.Diagram.CreateItem(int clsId)
at MindFusion.Diagramming.PersistContext.CreateObject(int clsId)
at MindFusion.Diagramming.PersistContext.TryLoadRemainingObjects()
at MindFusion.Diagramming.PersistContext.LoadRemainingObjects()
at MindFusion.Diagramming.Diagram.LoadFromStream(Stream stream, bool handleCorruption)
at MindFusion.Diagramming.WinForms.DiagramView.PasteFromClipboard(float dx, float dy, bool unconnectedLinks)
at OaSfcEditor.SfcEditorCtl.OnDiagramView_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
at System.Windows.Forms.Control.OnKeyDown(System.Windows.Forms.KeyEventArgs e)
at MindFusion.Diagramming.WinForms.DiagramView.OnKeyDown(System.Windows.Forms.KeyEventArgs e)
 



In the function MindFusion.Diagramming.Diagram.CreateItem(int clsId) clsId is 0 or larger then 57 causing it to throw an exception aborting the pasting operation.

We implemented the following code:

Custom Class Registration
Code
Select All
Diagram.RegisterItemClass( typeof( StepNode ), "cnStep", 1 );
Diagram.RegisterItemClass( typeof( TransNode ), "cnTrans", 1 );
Diagram.RegisterItemClass( typeof( ActionTableNode ), "cnActions", 1 );
Diagram.RegisterItemClass( typeof( ParBeginNode ), "cnParBegin", 1 );
Diagram.RegisterItemClass( typeof( ParEndNode ), "cnParEnd", 1 );
Diagram.RegisterItemClass( typeof( CommentNode ), "cnComment", 1 );
Diagram.RegisterItemClass( typeof( JumpLabelNode ), "cnJumpLabel", 1 );
Diagram.RegisterItemClass( typeof( JumpRefNode ), "cnJumpRef", 1 );
Diagram.RegisterItemClass( typeof( StepsLabelNode ), "cnStepsLabel", 1 );
 



In the function MindFusion.Diagramming.Diagram.CreateItem(int clsId) clsId is either 0 or larger then 56 causing it to throw an exception, and not succeed with the paste operation.

Save and load implementation
Code
Select All
protected override void SaveTo( BinaryWriter writer, PersistContext context )
{
    base.SaveTo( writer, context );
}
protected override void LoadFrom( BinaryReader reader, PersistContext context )
{
    base.LoadFrom( reader, context );

    // deserialized .Id is StepName. convert to stepnum
    string stepName = this.Id as string;
    this._stepNum = NameToNum( stepName );
}

 


This has been done for all the custom classes

« Last Edit: May 13th, 2024 at 6:03pm by ABCmmon »  
Back to top
 
IP Logged
 
Slavcho
YaBB Moderator
*****
Offline


tech.support

Posts: 3223
Joined: Oct 19th, 2005
Re: Problem with pasting clipboard data
Reply #1 - May 13th, 2024 at 11:42am
Print Post  
Please post the exception's full stack trace. What is the base class of your custom nodes?

Regards,
Slavcho
Mindfusion
  
Back to top
 
IP Logged
 
ABCmmon
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 3
Joined: May 8th, 2024
Re: Problem with pasting clipboard data
Reply #2 - May 13th, 2024 at 2:08pm
Print Post  
My bad I forgot the stack trace, here it is:

Code
Select All
at MindFusion.Diagramming.Diagram.CreateItem(int clsId)
at MindFusion.Diagramming.PersistContext.CreateObject(int clsId)
at MindFusion.Diagramming.PersistContext.TryLoadRemainingObjects()
at MindFusion.Diagramming.PersistContext.LoadRemainingObjects()
at MindFusion.Diagramming.Diagram.LoadFromStream(Stream stream, bool handleCorruption)
at MindFusion.Diagramming.WinForms.DiagramView.PasteFromClipboard(float dx, float dy, bool unconnectedLinks)
at OaSfcEditor.SfcEditorCtl.OnDiagramView_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
at System.Windows.Forms.Control.OnKeyDown(System.Windows.Forms.KeyEventArgs e)
at MindFusion.Diagramming.WinForms.DiagramView.OnKeyDown(System.Windows.Forms.KeyEventArgs e)
 



See the following table for the custom classes with their base class:
CustomClass
BaseClass
StepNode CompositeNode
TransNode CompositeNode
ActionTableNode TableNode
ParBeginNode CompositeNode
ParEndNode CompositeNode
CommentNode ShapeNode
JumpLabelNode ShapeNode
JumpRefNode ShapeNode
StepsLabelNode ShapeNode

We have a CodeNode class ontop of the CompositeNode base class that looks like this:
Code
Select All
public class CodeNode : CompositeNode
{
    public CodeNode( Diagram diagram )
        : base(diagram)
    {
    }

    public CodeNode( CodeNode src )
        : base( src )
    {
    }

    protected override bool SerializeComponents()
	{
		return false;
	}

    public virtual string MemberName
    {
        get { return null; }
    }

    public virtual string MethodName
    {
        get { return null; }
    }

    public virtual string MethodDef
    {
        get { return null; }
    }

    public SfcDiagram ParentSfcDiagram => this.Parent as SfcDiagram;
}
 



And for the Par*Node classes we have a ParallelNode class on top of CodeNode:
Code
Select All
    public enum ParType { Begin, End };

    // distinct classes for parallel begin & end nodes
    // (mostly just to allow easier "node is ..." tests)
    class ParBeginNode : ParallelNode
    {
        public ParBeginNode( Diagram diagram )
            : base(diagram)
        {
            LoadContentForParType( ParType.Begin );
        }
        public ParBeginNode( ParBeginNode src )
            : base( src )
        {
            LoadContentForParType( ParType.Begin );
        }
    }
    class ParEndNode : ParallelNode
    {
        public ParEndNode( Diagram diagram )
            : base( diagram )
        {
            LoadContentForParType( ParType.End );
        }
        public ParEndNode( ParEndNode src )
           : base( src )
        {
            LoadContentForParType( ParType.End );
        }
    }

    class ParallelNode : CodeNode
    {
        // interactive add of new parallel rail
        public static ParallelNode NewNode( SfcDiagram diagram, PointF pt, ParType parType, int parNum )
        {
            ParallelNode node;
            if( parType == ParType.Begin )
                node = new ParBeginNode( diagram );
            else
                node = new ParEndNode( diagram );

            node.Id = MakeName( parType, parNum );  // "PBxx" or "PExx"
            const float INITIAL_WIDTH = 60;
            var root = node.Components[0];    //outermost panel
            MindFusion.Drawing.IGraphics graphics = node.Parent.CreateMeasureGraphics();
            SizeF desired = root.GetDesiredSize( new SizeF(INITIAL_WIDTH, 6), graphics );
            graphics.Dispose();
            node.Bounds = new RectangleF( pt.X, pt.Y, INITIAL_WIDTH, desired.Height );
            diagram.Nodes.Add( node );

            if( parType == ParType.End )
            {
                // set refs to each other
                ParallelNode parbegin = node.FindOtherRail();
                node.OtherRail = parbegin;
                parbegin.OtherRail = node;
            }

            return node;
        }

        private ParType _parType;   // to simplify type testing in common code

        private static string MakeName( ParType partype, int parnum )
        {
            return (partype == ParType.Begin ? "PB" : "PE") + parnum.ToString( "D2" );
        }

        public ParallelNode OtherRail { get; set; }

        private ParallelNode FindOtherRail()
        {
            string id = this.Id as string;  // eg "PB01", "PE01" (or "xPB01" during copy/paste)
            string otherId;
            if( this._parType == ParType.Begin )
            {
                otherId = id.Replace( "PB", "PE" );
            }
            else
            {
                otherId = id.Replace( "PE", "PB" );
            }

            return this.Parent.FindNodeById( otherId ) as ParallelNode;
        }

        protected ParallelNode( Diagram diagram )
            : base( diagram )
        {
            this.EnabledHandles = AdjustmentHandles.ResizeMiddleLeft |
                                  AdjustmentHandles.ResizeMiddleRight |
                                  AdjustmentHandles.Move;

            this.HandlesStyle = HandlesStyle.RoundAndSquare;
        }


        protected override void SaveToXml( XmlElement xmlElement, XmlPersistContext context )
        {
            base.SaveToXml( xmlElement, context );
        }

        protected override void LoadFromXml( XmlElement xmlElement, XmlPersistContext context )
        {
            base.LoadFromXml( xmlElement, context );
            var other = this.FindOtherRail();
            if( other != null )
            {
                this.OtherRail = other;
                other.OtherRail = this;
            }
        }

        // "copy ctor" & binary serialization is required for copy/paste
        // (and is used ONLY then in the SfcEditor. normal diagram save/load uses Xml)
        public ParallelNode( ParallelNode src )
            : base( src )
        {
            this.Id = "x" + this.Id;
        }

        protected override void SaveTo( BinaryWriter writer, PersistContext context )
        {
            base.SaveTo( writer, context );
        }
        protected override void LoadFrom( BinaryReader reader, PersistContext context )
        {
            base.LoadFrom( reader, context );
        }

        // called after all pasted nodes have been created (apparently)
        internal void OnNodePasted()
        {
            if( this.OtherRail == null )
            {
                // other hasn't set us yet, so we're first
                var other = FindOtherRail();
                if( other != null )
                {
                    // found our mate. make sure he has us
                    this.OtherRail = other;
                    other.OtherRail = this;

                    // now generate a new Id for both us & our mate
                    int newNum = this.ParentSfcDiagram.GetUniqueNodeNumber( "PB" );
                    this.Id = MakeName( this._parType, newNum );
                    OtherRail.Id = MakeName( OtherRail._parType, newNum );
                }
            }
        }

        // content XML for each ParType
        // surely there's a simpler way to draw 2 lines & a triangle
        private static readonly string BeginContent = @"
			<StackPanel Orientation='Vertical' Spacing='0.0' VerticalAlignment='Top'>
                <Shape Name='TriAnchor' Shape='M0,100 L50,0 L100,100'
                    Height='1' Width='4' Pen='Black'  />
			    <StackPanel Orientation='Vertical' Spacing='1.0'>
                    <Shape Name='Line1' Shape='M0,0 L100,0'
                           Height='0.1' Pen='Black' />
                    <Shape Name='Line2' Shape='M0,0 L100,0'
                           Height='0.1' Pen='Black' />
                </StackPanel>
			</StackPanel>";

        private static readonly string EndContent = @"
			<StackPanel Orientation='Vertical' Spacing='0.0' VerticalAlignment='Bottom'>
			    <StackPanel Orientation='Vertical' Spacing='1.0'>
                    <Shape Name='Line1' Shape='M0,0 L100,0'
                           Height='0.1' Pen='Black' />
                    <Shape Name='Line2' Shape='M0,0 L100,0'
                           Height='0.1' Pen='Black' />
                </StackPanel>
                <Shape Name='TriAnchor' Shape='M0,0 L50,100 L100,0'
                    Height='1' Width='4' Pen='Black'  />
			</StackPanel>";

        // anchor points for each ParType
        // single in on top, outs along bottom
        static private AnchorPattern _beginAnchors = new AnchorPattern(
                new AnchorPoint[]
                {
                    // 1 incoming at middle top. outgoing from anywhere
                    new AnchorPoint(50, 0, true, false, MarkStyle.None)
                        { XUnit = UnitType.Percentage },
                }, "parbeginAnchors" );

        // ins on top, single out at bottom
        static private AnchorPattern _endAnchors = new AnchorPattern(
                new AnchorPoint[]
                {
                    // 1 outgoing at middle bottom. incoming to anywhere
                    new AnchorPoint(50, 100, false, true, MarkStyle.Custom)
                        { XUnit = UnitType.Percentage },
                }, "parendAnchors" );

        // gotta find a better way (to set pen color for the shapes)
        ShapeComponent _line1;
        ShapeComponent _line2;
        ShapeComponent _trianchor;
        static MindFusion.Drawing.Pen _normalPen;

        // called from subclass ctors
        protected void LoadContentForParType( ParType parType )
        {
            this._parType = parType;

            if( parType == ParType.Begin )
            {
                this.Components.Add( XmlLoader.Load( BeginContent, this ) );
                AnchorPattern = _beginAnchors;
            }
            else
            {
                this.Components.Add( XmlLoader.Load( EndContent, this ) );
                AnchorPattern = _endAnchors;
            }

            // things we want to change color of (to avoid repeated recursive traversal of content tree)
            _line1 = FindComponent( "Line1" ) as ShapeComponent;
            _line2 = FindComponent( "Line2" ) as ShapeComponent;
            _trianchor = FindComponent( "TriAnchor" ) as ShapeComponent;
            if( _normalPen == null )
                _normalPen = _line1.Pen;

        }

        // ParEnd "step" is active (when all parallel branch end steps are active
        // & we're waiting on an outgoing transition from the ParEnd)
        private bool _isStepActive;
        internal void ShowStepActive( bool isActive )
        {
            if( this._isStepActive == isActive )
                return;     // no change

            this._isStepActive = isActive;

            if( isActive )
            {
                // color the node & outgoing links
                _line1.Pen = StepNode.activePen;
                _line2.Pen = StepNode.activePen;
                _trianchor.Pen = StepNode.activePen;

                foreach( var link in this.OutgoingLinks )
                {
                    link.Pen = StepNode.activePen;
                    link.HeadBrush = StepNode.activeBrush;
                    //link.HeadPen = activePen;
                }
            }
            else
            {
                // reset node & outgoing links to default color
                _line1.Pen = _normalPen;
                _line2.Pen = _normalPen;
                _trianchor.Pen = _normalPen;

                foreach( var link in this.OutgoingLinks )
                {
                    link.Pen = null;
                    link.HeadBrush = null;
                    link.HeadPen = null;
                }
            }

            this.Repaint( true );   // repaint step node & connected things
        }

        // overrides for code generation
        public override string MemberName => (string)this.Id;


        // called on diagram.NodeModified event (after node moved/resized by mouse)
        internal void OnNodeModified()
        {
            this.StraightenInsideLinks();

            if( OtherRail != null )
            {
                var otherBounds = OtherRail.Bounds;
                otherBounds.X = this.Bounds.X;
                otherBounds.Width = this.Bounds.Width;
                OtherRail.Bounds = otherBounds;
                OtherRail.StraightenInsideLinks(); // other doesn't get OnNodeModified
            }
        }

        private void StraightenInsideLinks()
        {
            if( this._parType == ParType.Begin )
            {
                foreach( var link in this.OutgoingLinks )
                {
                    this.ParentSfcDiagram.StraightenUnanchoredLink( link );
                }
            }
            else
            {
                foreach( var link in this.IncomingLinks )
                {
                    this.ParentSfcDiagram.StraightenUnanchoredLink( link );
                }
            }
        }

        internal void OnNodeDeleted( NodeEventArgs e )
        {
            if( OtherRail != null )
            {
                Parent.Nodes.Remove( OtherRail );
            }
        }

        public override void DrawShadow( IGraphics graphics, RenderOptions options )
        {
            base.DrawShadow( graphics, options );

            if( this.Selected )
                ParentSfcDiagram.DrawSelectShadow( this, graphics, options );
        }
    }
 

 

Hopefully this will provide enough information.
  
Back to top
 
IP Logged
 
Slavcho
YaBB Moderator
*****
Offline


tech.support

Posts: 3223
Joined: Oct 19th, 2005
Re: Problem with pasting clipboard data
Reply #3 - May 14th, 2024 at 8:48am
Print Post  
Please let us know what we should change in attached project to reproduce.
  

ClipboardTest.zip ( 17 KB | 55 Downloads )
Back to top
 
IP Logged
 
ABCmmon
YaBB Newbies
*
Offline


I Love MindFusion!

Posts: 3
Joined: May 8th, 2024
Re: Problem with pasting clipboard data
Reply #4 - May 16th, 2024 at 6:23pm
Print Post  
Hey Slavcho thanks for your quick reply, I was comparing the solution from your post and the one from my company.

The problem occurs when we create newly added items only, copying them is successful but pasting will fail.

We are using .NET framework 4.8 for the application that started to fail. So im creating a simpler project based on the one you provided to reproduce the bug in and get back to you as soon as possible!
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint