Mike Torrettinni 198 Posted December 29, 2018 For Sorting in VirtualTreeview I use CompareNodes method, which compares column values and sorts lines. In CompareNodes I use CompareValues method to compare Integer or String values - 2 overloaded methods: function CompareValues(const aValue1, aValue2: string): integer; overload; begin Result := AnsiCompareText(aValue1, aValue2); end; function CompareValues(const aValue1, aValue2: integer): integer; overload; begin Result := CompareValue(aValue1, aValue2); end; This works as expected. But now I need to compare Variant values which can hold Integer or String, so I setup this method: function CompareValues(const aValue1, aValue2: Variant): integer; overload; var vVarType1, vVarType2: integer; Begin Result := 0; vVarType1 := VarType(aValue1); vVarType2 := VarType(aValue2); // if ANY of the values is string, should compare as strings! if (vVarType1 = varString) or (vVarType1 = varUString) or (vVarType2 = varString) or (vVarType2 = varUString) then Result := CompareValues(String(VarAsType(aValue1, varString)), String(VarAsType(aValue2, varString))) // <- without String cast if thinks the values are Variant and calls itself! else if VarType(aValue1) = varInteger then Result := CompareValues(Integer(VarAsType(aValue1, varInteger)), Integer(VarAsType(aValue2, varInteger))); // <- have to cast as Integer! End; I set it up like this because when trying to sort values in column that have integers and strings, like: var vVariant1, vVariant2: Variant; begin vVariant1 := 5; vVariant2 := 'Yes'; if CompareValues(vVariant1, vVariant2) = 0 then Showmessage('Variant values are equal.') else Showmessage('Variant values are NOT equal.') it works. But, why does it: a. String 'Yes' recognizes as varUString and not varString, so I need to check Variant for both, just in case b. If I don't cast values when calling CompareValues to Integer or String, it calls as Variant types again and I get Stack overflow error, of course. So, I have the following questions: 1) Can I force VarType to return varString for varString or varUString? So I will only have 1 comparison, like VarType(aValue1) = varString, and not need to compare with 2 different string types... 2) There are far more number types of Variant (varByte, varWord, varShortInt, varInteger...)... how can I be sure that all numbers will always be recognized as varInteger? 3) Why do I need to cast VarAsType? String(VarAsType(aValue1, varString)) and Integer(VarAsType(aValue1, varInteger)), even though I specify I want varString or varInteger... Any advice is welcome! Thanks! Share this post Link to post
David Heffernan 2345 Posted December 30, 2018 You are working with Unicode strings so you will always get varUString. I don't know what you have against varUString. Surely you don't want to go back to ANSI strings which is what varString represents. VarAsType returns a variant. That's why you need a typecast to obtain other types. The entire question seems massively over complicated. In fact this is a common theme of the questions you ask. Don't take that the wrong way. But my most important advice to you is to try to simplify. You seem regularly to get tied in knots because you don't reduce the complexity in your thinking. In this instance, surely, you should be working with the user input as text. No need at all for variants in my opinion. Simply use TryStrToInt to check if a string value can be treated as an integer. And if there really is some need for variants due to motivations we cannot see then take care to understand what types they can hold. Make sure that you only ever add Unicode strings and integers and therefore only need to deal with those two var types. Raise assertion exceptions if you encounter other types. Make sure that you have good testing in place. Now, you probably think that you haven't got time for testing but what many people don't realise is that testing saves you time. Testing is what allows you to make changes to code without fear of breaking it. 1 Share this post Link to post
Uwe Raabe 2057 Posted December 30, 2018 There are several functions to simplify your task. You can use VarIsStr to check for string variants and VasIsOrdinal for the Integer case. Also there is VarToStr to convert a Variant to string. With this you can strip down your compare function to function CompareValues(const aValue1, aValue2: Variant): integer; overload; Begin Result := 0; // if ANY of the values is string, should compare as strings! if VarIsStr(aValue1) or VarIsStr(aValue2) then Result := CompareValues(VarToStr(aValue1), VarToStr(aValue2)) else if VarIsOrdinal(aValue1) and VarIsOrdinal(aValue2) then // Make sure that the following cast succeeds! Result := CompareValues(Integer(aValue1), Integer(aValue2)); End; 1 Share this post Link to post
Mike Torrettinni 198 Posted December 30, 2018 @Uwe Raabe Works good on my data. Very rarely I use Variants, so I didn't see these methods. Thanks! @David Heffernan Thank you, I see now that VarAsType returns a new Variant and not as the type you specify, odd. (I should read documentation more precise) As for the why I need this: I have a VirtualTreeview report that shows 20+ different categories of Customer data and 3-30 different columns, depending on the selected category. So, I need at least OnGetText, OnCompareNodes to show and sort data, then I have my own Copy, Search.. and Export data methods. So instead of assigning data to column in each method (like CellText := Name; or CellText := IntToStr(UnpaidInvoices)), I'm testing with only ONCE specifying what data is in each column of the view, so I have: function ReturnColumnData(Category, ColumnNo): Variant; and I can use it any of the methods that needs data for each column, And Export method can create Excel file and I need to export numbers as numbers to avoid that annoying warning indication in Excel that value is integer and not string. and the CompareValues are used in OnCompareNodes: Result := CompareValues(ReturnColumnData(Index1, Column), ReturnColumnData(Index2, Column)); So I need all in Variants as it seems that all methods that need data already know how to handle Variant types correctly for their purpose - show data, sort (compare) data, export to csv, excel.,, as I tested even sort is fast as before using Variants. Share this post Link to post
David Heffernan 2345 Posted December 30, 2018 I don't see that you have to work with variants. I think you can work with text. I work with text when I use VTT. Share this post Link to post
Mike Torrettinni 198 Posted December 30, 2018 Hm, so you have different fields for display and data? Like 'Customer Order no' or 'Sum of invoice amounts'... these are all numbers, do you have a display layer where you convert all to text and then show in VTT? How do you then deal with sorting of numeric columns? Do you keep information about which column data is numeric and sort accordingly (convert text to number and then sort)? Well, me asking you usually means I'm lacking some experience and understanding, but this is really interesting. Share this post Link to post
David Heffernan 2345 Posted December 30, 2018 If it's a numeric column then surely it's always a number. Share this post Link to post