{"record":{"id":"6e7233d9795de35c","repo":"dotnet/wpf","slug":"sr-invalidstyluspointdescriptionbuttonsmustbelast","errorCode":null,"errorMessage":"SR.InvalidStylusPointDescriptionButtonsMustBeLast","messagePattern":"SR\\.InvalidStylusPointDescriptionButtonsMustBeLast","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Stylus/Common/StylusPointDescription.cs","lineNumber":82,"sourceCode":"            seenIds.Add(StylusPointPropertyIds.NormalPressure);\n\n            int buttonCount = 0;\n            for (int x = RequiredCountOfProperties; x < infos.Count; x++)\n            {\n                if (seenIds.Contains(infos[x].Id))\n                {\n                    throw new ArgumentException(SR.InvalidStylusPointDescriptionDuplicatesFound, nameof(stylusPointPropertyInfos));\n                }\n                if (infos[x].IsButton)\n                {\n                    buttonCount++;\n                }\n                else\n                {\n                    //this is not a button, make sure we haven't seen one before\n                    if (buttonCount > 0)\n                    {\n                        throw new ArgumentException(SR.InvalidStylusPointDescriptionButtonsMustBeLast, nameof(stylusPointPropertyInfos));\n                    }\n                }\n                seenIds.Add(infos[x].Id);\n            }\n            if (buttonCount > MaximumButtonCount)\n            {\n                throw new ArgumentException(SR.InvalidStylusPointDescriptionTooManyButtons, nameof(stylusPointPropertyInfos));\n            }\n\n            _buttonCount = buttonCount;\n            _stylusPointPropertyInfos = infos.ToArray();\n        }\n\n        /// <summary>\n        /// StylusPointDescription\n        /// </summary>\n        /// <param name=\"stylusPointPropertyInfos\">stylusPointPropertyInfos</param>\n        /// <param name=\"originalPressureIndex\">originalPressureIndex - does the digitizer really support pressure?  If so, the index this was at</param>","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Stylus/Common/StylusPointDescription.cs#L64-L100","documentation":"The StylusPointDescription constructor enforces that all button properties are placed last in the collection, after every non-button property. Once a button has been seen in the iteration, any subsequent non-button property triggers this ArgumentException, because the internal layout expects data axes followed by button bits.","triggerScenarios":"Calling new StylusPointDescription(infos) where a non-button StylusPointPropertyInfo (IsButton == false) appears after any button property (IsButton == true) in the collection.","commonSituations":"Appending custom axis properties (e.g. tilt, twist) after button properties when building a description; concatenating a custom-axis list onto a device list that already ended with buttons; re-sorting the collection alphabetically which moves buttons before some axes.","solutions":["Reorder so all non-button StylusPointPropertyInfos come first (after the mandatory X, Y, NormalPressure) and all buttons come last.","Partition the collection with LINQ: nonButtons.Concat(buttons) before constructing.","Check each info's IsButton flag; WPF's TipButton/BarrelButton properties have IsButton true, custom axes false."],"exampleFix":"// before\nvar desc = new StylusPointDescription(new[]{ x, y, pressure, tipButton, tiltX });\n// after\nvar desc = new StylusPointDescription(new[]{ x, y, pressure, tiltX, tipButton });","handlingStrategy":"validation","validationCode":"bool buttonsLast = !infos.Skip(3).Select((i, idx) => (i, idx))\n    .SkipWhile(t => !t.i.IsButton)\n    .Any(t => !t.i.IsButton); // any non-button after first button is invalid","typeGuard":null,"tryCatchPattern":"try { var desc = new StylusPointDescription(infos); }\ncatch (ArgumentException) { var ordered = infos.Take(3).Concat(infos.Skip(3).Where(i => !i.IsButton)).Concat(infos.Skip(3).Where(i => i.IsButton)).ToArray(); desc = new StylusPointDescription(ordered); }","preventionTips":["Partition properties into axes and buttons, then concatenate axes-then-buttons","Never sort the collection alphabetically after building it","Check IsButton when appending new properties"],"tags":["wpf","stylus","ordering","argument-exception"],"backgroundTag":"invalid-argument-value","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}