Mike if you find the time could you give us a break down on what that C# code is doing and where the problem is that it is correcting. I have an idea but want to make sure that I’m clear on it.
Hi Paul, here’s my take on it. Looks like the problem is with maxscript. the GetValue method of the TreeListNode returns a System.Object, which obviously doesn’t exist in maxscript so it’s being incorrectly cast to an integer. Mike’s code simply casts the Object to a float (Single) before returning, which maxscript can handle correctly. He also added a bunch of error checking. Please correct me if I’m wrong / missed anything Mike.
EDIT: i just realised after posting that it still doesn’t quite make sense since the you can use GetValue correctly until you enter a value…
Yup, that what’s happening.
Maxscript mishandles the conversion from a .NET Object to a float so my snippet removes any ambiguity. I chose to use reflection and do a method invoke on a function called “get_Item” so that (hopefully!) it will work for any derived object that exposes an Item[x] property (not just XtraTreeListNodes).
I’m not 100% sure why modifying the value causes MXS to change the way it interprets the Object.
If you change the logging printout to
format “Value: % is a % DText: % FValue: %
” val (classof val) dtxt fval
Then immediately after initialization you get :
Value: 0.5 is a Float DText: 0.5 FValue: 0.5
But then after a spinner update it’s:
Value: 1 is a Integer DText: 0.6 FValue: 0.6
It’s odd.
In my spare time I’ve been trying to tweak the mxsdotnet.dll so that it doesn’t need quite as much hand holding …it’s a long term labour of love. My thinking is that if you know what the type is that you want, you provide it as a named argument.
Instead of typing:
x = foo.get_value()
and hoping for the best, you could explicitly state:
x = foo.get_value returntype:float
Well that is what I’m confused about. You can get the value until the repository item is changed.
Now here is the next problem, I can’t get the value as it is changing. What I’m getting is the last value. So as long as the repository spinner is active the cell isn’t getting the value updated. Only once I’m out of the edit mode do I see the correct value. Does this mean that we need to try and pull the value from the repository spinner instead, I don’t see a way yet to do that.
Edit: I should note as well that the CellValueChanging event is being called but the value isn’t not updating.
I keep getting memory errors Mike. I will see if I can lock it down and I will pass on the message that it is reporting to me.
Here is an update.
Changes:
I made the 2nd arg of GetFloatItem an Object (was an Int32) to so you can pass an XtraTreeListNodeColumn (and not have to dereference that column’s .AbsoluteIndex). This made it simpler to call when handling a CellValueChanging(ed) event.
Added a GetFloatValue helper that will extract the “Value” property of an Object as a float.
if gItemHelperAssembly == undefined then
(
global gItemHelperAssembly
)
-- Build up a snippet of code that will access the "Item[x]" pseudo-property of a dotNet object and return the value as a float.
fn newItemHelpers forceRecompile:false =
(
if (forceRecompile or
(classof gItemHelperAssembly) != dotNetObject or
((gItemHelperAssembly.GetType()).ToString()) != "System.Reflection.Assembly") do
(
sb = "using System;
"
sb += "using System.Text;
"
sb += "using System.Collections;
"
sb += "using System.Reflection;
"
sb += "class ItemHelpers
"
sb += "{
"
sb += " public Single GetFloatItem(Object tln, Object colId)
"
sb += " {
"
sb += " Single ret = Single.MinValue;
"
sb += " Object retObj;
"
sb += " try {
"
sb += " Type tlnType = tln.GetType();
"
sb += " MethodInfo mi = tlnType.GetMethod(\"get_Item\");
"
sb += " Object[] parms = new Object[1];
"
sb += " parms[0] = colId;
"
sb += " retObj = mi.Invoke(tln,parms);
"
sb += " }
"
sb += " catch {
"
sb += " throw new System.Exception(\"GetFloatItem Error: Failed to invoke \\\"get_Item()\\\" method on input object\");
"
sb += " }
"
sb += " try {
"
sb += " ret = Convert.ToSingle(retObj);
"
sb += " }
"
sb += " catch {
"
sb += " StringBuilder err = new StringBuilder();
"
sb += " err.AppendFormat(\"GetFloatItem Error: Value in column {0} will not convert to a float! Value: {1} Type: {2}\", colId, retObj, retObj.GetType() );
"
sb += " throw new System.Exception(err.ToString());
"
sb += " }
"
sb += " return ret;
"
sb += " }
"
sb += " public Single GetFloatValue(Object tln)
"
sb += " {
"
sb += " Single ret = 0;
"
sb += " Object retObj;
"
sb += " try {
"
sb += " Type tlnType = tln.GetType();
"
sb += " MethodInfo mi = tlnType.GetMethod(\"get_Value\");
"
sb += " Object[] parms = new Object[0];
"
sb += " retObj = mi.Invoke(tln,parms);
"
sb += " }
"
sb += " catch {
"
sb += " throw new System.Exception(\"GetFloatValue Error: Failed to invoke \\\"get_Value()\\\" method on input object\");
"
sb += " }
"
sb += " try {
"
sb += " ret = Convert.ToSingle(retObj);
"
sb += " }
"
sb += " catch {
"
sb += " StringBuilder err = new StringBuilder();
"
sb += " err.AppendFormat(\"GetFloatValue Error: Value will not convert to a float! Value: {1} Type: {2}\", retObj, retObj.GetType() );
"
sb += " throw new System.Exception(err.ToString());
"
sb += " }
"
sb += " return ret;
"
sb += " }
"
sb += "}
"
csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider"
compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters"
-- Need to add referenced assemblies that the code snippet is 'using'
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.GenerateInMemory = true
compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(sb)
if (compilerResults.Errors.Count > 0 ) then
(
errs = stringstream ""
for i = 0 to (compilerResults.Errors.Count-1) do
(
err = compilerResults.Errors.Item[i]
format "Error:% Line:% Column:% %
" err.ErrorNumber err.Line \
err.Column err.ErrorText to:errs
)
MessageBox (errs as string) title: "Errors encountered while compiling C# code"
format "%
" errs
gItemHelperAssembly = undefined
return undefined
)
else
(
gItemHelperAssembly = compilerResults.CompiledAssembly
)
)
gItemHelperAssembly.CreateInstance "ItemHelpers"
)
try(destroyDialog testR)catch()
rollout testR "Test"
(
dotNetControl xtl "DevExpress.xtraTreeList.treeList" height:200
local ItemHelpers
fn initXtl xtl=
(
ItemHelpers = newItemHelpers() --forceRecompile:true
xtl.OptionsSelection.MultiSelect = True
xtl.OptionsView.ShowRoot =true
xtl.OptionsView.ShowIndicator = false
xtl.OptionsView.AutoWidth = true
col=xtl.columns.add()
col.visible=true
col.width=120
col.caption="Items"
xtl.columns.item[0].fixed = xtl.columns.item[0].fixed.left
local xTraSpinner=dotNetObject "DevExpress.XtraEditors.Repository.RepositoryItemSpinEdit"
xTraSpinner.SpinStyle=xTraSpinner.SpinStyle.vertical
xTraSpinner.minValue=0.0
xTraSpinner.maxValue=1.0
xTraSpinner.Increment=.1
xTraSpinner.UseCtrlIncrement=true
xTraSpinner.isFloatValue=true
xTraSpinner.AllowNullInput=xTraSpinner.AllowNullInput.false
xTraSpinner.nulltext="--- "
xtl.RepositoryItems.add xTraSpinner
col=xtl.columns.add()
col.visible=true
col.caption="Val:"
col.columnEdit=xTraSpinner
col.UnboundType=col.UnboundType.Decimal
)
fn addTargetNamesToEtl xtl theNodes:#("Test1","Test2","Test3")=
(
xtl.ClearNodes()
for i = 1 to theNodes.count do
(
n0=xtl.appendNode #(theNodes[i],0.5) -1
pos=xtl.appendNode #("Sub 1:",0.0) n0
rot=xtl.appendNode #("Sub 2:",0.0) n0
)
)
on xtl SelectionChanged senderArg arg do
(
-- formatProps senderArg
for i = 0 to senderArg.selection.count-1 do
(
valItm = senderArg.selection.item[i].getValue 1
txtItm = senderArg.selection.item[i].GetDisplayText 1
fltItm = ItemHelpers.GetFloatItem senderArg.selection.item[i] 1
format "SelectionChanged: Index: % Value: % is a % Display Text: % Float Value: %
" i valItm (classof valItm) txtItm fltItm
)
)
on xtl CellValueChanging senderArg arg do
(
format "CellValueChangING: NodeId % Col % - " arg.node.id arg.column.AbsoluteIndex
currVal = ItemHelpers.GetFloatItem arg.node arg.column
newVal = ItemHelpers.GetFloatValue arg
format "currval: % newval: %
" currVal newVal
)
on xtl CellValueChanged senderArg arg do
(
format "CellValueChangED: NodeId % Col % - " arg.node.id arg.column.AbsoluteIndex
currVal = ItemHelpers.GetFloatItem arg.node arg.column
newVal = ItemHelpers.GetFloatValue arg
format "currval: % newval: %
" currVal newVal
)
on testR open do
(
initXtl xtl
addTargetNamesToEtl xtl
)
)
createDialog testR width:300 height:220
Check this thread, where I had the same issue with NumericUpDown controls. There are a couple more solutions in there (the simplest being instead of getting the .value get “<control>.text as float”)…
http://forums.cgsociety.org/showthread.php?f=98&t=763277&highlight=updown
“.text as float” is not a solution because I can (or want to) use special Display Format for any “float” cell.
I’m fighting with this problem almost a day and could find any other then c# solution. !!!KCUF
This is a MXS issue, not a .NET …
Yeah, I didn’t say it was the best, just the simplest to implement, especially for a non-programmer like myself.
“decimal == integer” <sigh>
I was thinking about using the getProperty asDotNetObject:true and convert trick, but I don’t think you can use it for array-like properties (like Item[]) as you need to pass the array index as a parameter.
It’s probably the best way to get the .value property – but I already had all that C# in there for arrays and I couldn’t resist extending it =)
Why bother with the keys when the Jaws Of Life are available?
Mike you saved my a$$. Thanks a million and if there is anything that I can do for you, please just ask.
I tried do to same thing on MXS as biddle did using c#
node = <TreeListNode>
t = node.getType()
s = t.getmethod “get_Item”
v = s.Invoke node #(1)
– result is still “integer”
ea = (dotnetclass “System.Reflection.BindingFlags”).ExactBinding
db = (dotnetobject “System.DefaultBinder”) – ??? how to set in to “convert to Single”
ci = (dotnetclass “System.Globalization.CultureInfo”).CurrentCulture
v = s.Invoke node ea db #(1) ci
– result is still “integer”
in c# [color=silver]<DevExpress.XtraTreeList.Nodes.[size=2]TreeListNode>.GetValue() returns an Object of cause…
[/size][/color]
I think it would have worked if you could specify that you want the dotnetobject returned from the call to invoke().
Something like:
v = s.Invoke node #(1) asdotnetObject:true
Then you’d be free to convert the decimal .NET object to the float you need.
But if you can do that, you could remove all the method lookup cruft and just call:
v = node.get_item 1 asdotnetObject:true
In the end, I’m not sure why the asdotnetObject keyword option is limited to the getProperty call. Would dearly love to have something like it for .NET methods too.
Well Mike I think that i would have ended up in the same boat. I would not have been getting the real time value. And you right, you can’t use getProperty as there is no property name to pass. I tried shoe horning that in there and it didn’t work at all. What you have created now is working real time in the modifier panel.
I will try and find the time after this project to document all of this on my site.