Directly Access Object2D Property in a Dictionary

This a little arcane, but I will try and explain.

I have a canvas subclassed entity that has a dictionary as one of its properties. That dictionary contains as its values any number of Object2Ds. The Paint event of that canvas goes through all the entries in that dictionary and draws them on the canvas.

The purpose of this arrangement is largely to make my code easier to write and read. I can give each Object2D in the dictionary an understandable name (its Key) and refer to it in that way. For the most part it works fine.

But I find one thing puzzling. For example, let’s say I have a circle in the canvas that I am referring to as “SupplyDot”. The dictionary, that is a property of this canvas subclass, is dObject2D

Let’s say that I want to move this circle to a new location (newX as Integer, newY as Integer)
This sample code is within an event of the canvas instance living in some window. I would like to be able to write this:

Me.dObject2D.Value("SupplyDot").X = newX
Me.dObject2D.Value("SupplyDot").Y = newY

But this is not accepted. It is flagged as an error by the IDE. However, if I do the following, it works just fine.

Var anOval As OvalShape
anOval = Me.dObject2D.Value("SupplyDot")
anOval.X = newX
anOval.Y = newY

I am puzzled as to why this is. Why is the “easier/simpler” version not acceptable?

Why isn’t – Me.dObject2D.Value(“SupplyDot”) – treated like an OvalShape on its own.

Almost certainly because the value of .value is a variant, and a variant doesnt have an .X property.
Certainly not all the objects are OvalShapes

Use this:

Ovalshape(dObject2D.Value(“SupplyDot”)).X = newX

If you subclass the objects, you might add a method that takes both co-ordinates at once

MyOvalShape(dObject2D.Value(“SupplyDot”)).SetPos(newX,Newy)

That, in my hands, does not work. You seem to be casting the thing as an OvalShape, but the IDE still does not accept.

Your comments bring up the idea in my brain of creating methods in the subclassed canvas that can deal with the various types of Object2D along the lines of

// NewPosition -- Method of canvas subclass
// sObjectName As String, newX As Integer, newY As Integer
If Self.dObject2D.Value(sObjectName) IsA OvalShape Then
  Var anOval As New OvalShape
  anOval = OvalShape(Self.dObject2D.Value(sObjectName))
  anOval.X = newX
  anOval.Y = newY
End If
If Self.dObject2D.Value(sObjectName) IsA RectShape Then
  Var aRect As New RectShape
  aRect = RectShape(Self.dObject2D.Value(sObjectName))
  aRect.X = newX
  aRect.Y = newY
End If
// etc.

This method of the subclassed canvas (expanded to deal with other properties of Object2D like FillColor that I might want to change) or just a multitude of methods to deal with all these properties could put all this “complex/wordy” code in one location in method(s) of the subclassed canvas.

From the outside this is accepted, works, and simple to write and read.

Me.NewPosition("SupplyDot", newX, newY)

1 Like

What error does the IDE give you with this code?

Error Messages

Ovalshape(dObject2D.Value(“SupplyDot”)).X = newX
This item does not exist


dObject2D.Value("SupplyDot").X = newX
Type “Variant” has no member named “X”

A dictionary is a list of key-value pairs, both keys and values are Variants. When you write the code, Xojo does not know what you are going to put in that variant, so cant guess what properties are going to be available there.

This should work, you are doing something wrong and not having the dObject2D or the newX in there. Otherwise the error would be

IllegalCastException

1 Like

Worth checking the quote characters… those are matched curly quotes, and should be " … chr (34)

1 Like

In your other code, you use ‘self’ to access the dobject2D object. Add that into this code and see if it helps, as in:

Ovalshape(self.dObject2D.Value(“SupplyDot”)).X = newX

Incredible.

Those curly quotes got in there because I copy pasted the line of code directly from Jeff’s first post. They are the problem.

But eventually Jeff was the one who noticed that I was using curly quotes. So he is redeemed. :slightly_smiling_face:

I appreciate the suggestions and help from everyone.


I ended up writing a method in the subclassed canvas that had as a property the dictionary of Object2D. I am fairly happy with this. This line below shows this method in use:

Me.SetDictObject2D_Property("SupplyDot", "X", newX) Me.SetDictObject2D_Property("SupplyDot", "Y", newY)

As for the method, you pass the dictionary key, the property you want to set as a string and the value you want to set it to as a Double. You can optionally also pass a color.
Most of the properties can be set with a number. Occasionally with a color. And there are some rarer things like FontName with the TextShape that this method cannot address.

Basically, you pass a IsA statement to determine which kind of shape you are dealing with and then have the appropriate code for that shape. Each shape Type is handled slightly differently.

If Me.dObject2D.Value(oKey) IsA OvalShape Then...

The method is long and boring, but once written it is out of sight.


// PARAMETERS: oKey As Variant, oProperty As String, newValue As Double, newColor As Color = &c000000
// DESCRIPTION: Most commonly, just ignore newColor unless trying to change something like FillColor

If Not (Me.dObject2D.HasKey(oKey)) Then 
  MessageBox("Passed a non-existant key: " + CurrentMethodName)
  Return
End If

// ARC
If Me.dObject2D.Value(oKey) IsA ArcShape Then
  Var theShape As ArcShape
  theShape = Me.dObject2D.Value(oKey) 
  Select Case oProperty
  Case "ArcAngle"
    theShape.ArcAngle = newValue
  Case "BorderColor"
    theShape.BorderColor = newColor
  Case "BorderOpacity"
    theShape.BorderOpacity = newValue
  Case "BorderWidth"
    theShape.BorderWidth = newValue
  Case "FillColor"
    theShape.FillColor = newColor
  Case "FillOpacity"
    theShape.FillOpacity = newValue
  Case "Height"
    theShape.Height = newValue
  Case "Rotation"
    theShape.Rotation = newValue
  Case "Scale"
    theShape.Scale = newValue
  Case "StartAngle"
    theShape.StartAngle = newValue
  Case "Width"
    theShape.Width = newValue
  Case "X"
    theShape.X = newValue
  Case "Y"
    theShape.Y = newValue
  Else
    MessageBox("Unexpected result ARCSHAPE: " + CurrentMethodName)
  End Select
  
  Return
End If

// CURVE
If Me.dObject2D.Value(oKey) IsA CurveShape Then
  Var theShape As CurveShape
  theShape = Me.dObject2D.Value(oKey) 
  Select Case oProperty
  Case "BorderColor"
    theShape.BorderColor = newColor
  Case "BorderOpacity"
    theShape.BorderOpacity = newValue
  Case "BorderWidth"
    theShape.BorderWidth = newValue
  Case "ControlX"
    theShape.Scale = newValue
  Case "ControlY"
    theShape.Scale = newValue
  Case "FillColor"
    theShape.FillColor = newColor
  Case "FillOpacity"
    theShape.FillOpacity = newValue
  Case "Order"
    theShape.Order = newValue
  Case "Rotation"
    theShape.Rotation = newValue
  Case "Scale"
    theShape.Scale = newValue
  Case "X"
    theShape.X = newValue
  Case "X2"
    theShape.X2 = newValue
  Case "Y"
    theShape.Y = newValue
  Case "Y2"
    theShape.Y = newValue
  Else
    MessageBox("Unexpected result CURVESHAPE: " + CurrentMethodName)
  End Select
  
  Return
End If

// Etc. for all the different shape types