Is there any possibility to make use of windows-environment-variables in AX?
Example:
Property NormalImage on a MenuItem. I'd like to use sth. like %USERNAME% instead of the explicit username. In Classes for example I can use the WINAPI macro and refer to a user-folder-variable, eg CSIDL_MYPICTURES, to access the path per user. In AOT-object-properties there's no possibility to reference to macros...
Any way to achieve this?
No, you can't do this on the AOT. You can change some properties on runtime through X++ code, but you can't dynamically change the image on a MenuItem as far as I know.
You can make visible/invisible some menuitems. May be can simulate what you are trying to do this way, although this is not quite aligned with the AX design patterns.
Yes you can use .Net framework to get environment variables or you can use built in AX functions. See this example I typed up:
static void Job85(Args _args)
{
System.String systemString;
str marshalString;
;
// Built in AX function
info(strfmt("%1, %2, %3", xUserInfo::find().networkAlias, xUserInfo::find().networkDomain, xUserInfo::find().name));
// .Net Framework
systemString = System.Environment::GetEnvironmentVariable('username');
marshalString = systemString; // Marshal it
info(strfmt("%1", marshalString));
}
See http://msdn.microsoft.com/en-us/library/system.environment.getenvironmentvariable.aspx
An example of getting the client name and machine name
static void testGetClientMachineName(Args _args)
{
str localClientname, terminalServerName;
;
localClientname = System.Environment::GetEnvironmentVariable('CLIENTNAME') ;
info(localClientname);
terminalServerName = System.Environment::get_MachineName();
info(terminalServerName);
}
Related
does anybody know a way to configure NJsonSchema to use CamelCase property naming durching code generation? I've a JSON schema with property names like message_id which lead to C# property name 'Message_id' where i.e. 'MessageId' whould be a more C#-like way.
With an attribute like '[JsonProperty("message_id"]' it would be no problem to specified the connection between the different names.
So, you asked about code generation. I was having trouble with the schema it generated not matching what was getting sent to my Angular app. So, while this isn't exactly what you were looking for, perhaps it helps you find an answer (maybe?).
To generate the schema with the camel case property names, I'm setting the Default Property Name Handling to CamelCase, but this is using the deprecated call to set these settings directly. There should be some way to use the SerializerSettings directly, but I wasn't quite able to make that work. This isn't production code for me, so it will do.
internal class SchemaFileBuilder<T>
{
public static void CreateSchemaFile()
{
CreateSchemaFile(typeof(T).Name);
}
public static void CreateSchemaFile(string fileName)
{
JsonSchemaGeneratorSettings settings = new JsonSchemaGeneratorSettings();
settings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
var schema = NJsonSchema.JsonSchema.FromType<T>(settings);
var json = schema.ToJson();
Directory.CreateDirectory("Schemas");
File.WriteAllText($"Schemas\\{fileName}.schema.json", json);
}
}
I set this up as a generic function so I could pass multiple schemas in to either createSchemaFile functions. Here's are some example calls which would generate a Person.schema.json file and a Persons.schema.json file:
SchemaFileBuilder<Person>.CreateSchemaFile();
SchemaFileBuilder<Dictionary<string, Person>>.CreateSchemaFile("Persons");
Is there any way for me to get, by code, a list of all the methods in a given form in Dynamics AX 2012?
I am working in developing a small tool that will insert some comments in all of the methods of custom objects through using the Editor class. However, to do so, I need the full path of each method (ex: \Classes\MyClass\CustomMethod), but I simply can't find any way to get this working for Forms.
Thanks in advance!
Thanks for sending me suggestions. I actually just finished writing some code to get me the information. Here is the code, for anyone who may be interested:
//I used the BatchJobHistory form as a test, since I called this static method from a job
public static void findAllChildNodes(str _nodeName = "\\Forms\\BatchJobHistory", boolean _isMethod = NoYes::No)
{
TreeNode treeNode;
TreeNodeIterator treeNodeIterator;
treeNode methodsNode;
str treePath;
boolean containsMethod;
treeNode = TreeNode::findNode(_nodeName);
treeNodeIterator = treeNode.AOTiterator();
methodsNode = treeNodeIterator.next();
while(methodsNode)
{
treePath = methodsNode.treeNodePath();
containsMethod = strScan(treePath, 'Methods', 1, strLen(treePath));
if (methodsNode.AOTchildNodeCount())
{
//TestClass is the class containing this method
TestClass::findAllChildNodes(treePath, containsMethod);
}
else if (_isMethod)
{
info(strFmt("%1", treePath));
}
methodsNode = treeNodeIterator.next();
}
}
Here's a question that should point you in the right direction, albeit you'll need to expand on it.
AX2009 Loop through all the controls in the form on init
The general theme you'll probably need to work with is recursion (https://en.wikipedia.org/wiki/Recursion_(computer_science)), reflection, and use of the TreeNode and derivatives of the class to do Tree Node Traversal (https://en.wikipedia.org/wiki/Tree_traversal)
See https://learn.microsoft.com/en-us/dynamicsax-2012/developer/performing-reflection-with-the-treenode-class
I'm also imagining you'll need to use some of the layer-aware methods if you're trying to decorate methods with comments. It sounds fun what you're trying to do, but a little bit of a pain. I'd expect it to take at least half a day to do properly.
That's it, I'll step on my pride!
I'm using MRTK v2 and working fine except that at some point I want to turn off the line extending from the Motion Controller to the object and that provides input. After looking around and trying to find it in the MRTK documentation (it's gotta be easy, right?), I'm still banging my head on the wall and it's starting to hurt....
The MRTK documentation explains quite well how to configure it here:
https://github.com/microsoft/MixedRealityToolkit-Unity/blob/mrtk_release/Documentation/Input/Pointers.md
But I'm looking to do this in script, enabling and disabling it as I need it in my application.
Any clue how to do this?
Many thanks!
Answered my own question. Simple:
PointerUtils.SetMotionControllerRayPointerBehavior(PointerBehavior.AlwaysOff);
See documentation here: https://github.com/microsoft/MixedRealityToolkit-Unity/blob/mrtk_release/Documentation/Input/Pointers.md
Great question! Here's one way to do this that has worked for me. You can see my solution at this repository: https://github.com/julenka/MixedRealityToolkit-Unity/tree/so/linepointer_off. Open the scene Assets/TurnOffLinePointerTest.unity and then use hand simulation to press the buttons. The code to turn the pointers on/off is in Assets/PointerConfigurationExample.cs.
Note: the reason you need to use this approach of modifying the mediator instead of directly setting myPointer.IsActive = false is because the default mediator overwrites these values every frame. Luckily, you can customize this behavior.
Step 1: Update MRTK a bit so that PointerMediator can be accessed
Apply the changes from this commit to your MRTK clone. This change updates the FocusProvider in MRTK to make the PointerMediator publicly accessible, and makes the DefaultPointerMediator extensible by updating fields to be protected instead of private, and making methods virtual. See this pull request that implements this change directly into MRTK.
Step 2: Create a custom PointerMediator that will turn off far pointers
Create a custom Pointer Mediator like the one from this commit.
using System.Collections.Generic;
using Microsoft.MixedReality.Toolkit.Input;
public class CustomPointerMediator : DefaultPointerMediator
{
public bool FarPointersDisabled {get; set;}
public override void UpdatePointers()
{
base.UpdatePointers();
if (FarPointersDisabled)
{
foreach(var pointer in farInteractPointers)
{
pointer.IsActive = false;
}
}
}
}
Note that this mediator extends DefaultPointerMediator so it adapts almost all the default mediator logic. Make sure you have fully applied the changes from the first commit otherwise you will not be able to extend DefaultPointerMediator.
Step 3: Tell MRTK to use your custom pointer mediator
In your pointer profile, configure MRTK to use the custom pointer mediator instead of the default pointer mediator. Notice in the picture that I've created a custom pointer profile as part of a custom input system (don't modify the default profile otherwise your changes might get overwritten when you update MRTK).
Step 4: Component that uses custom mediator to turn line pointers on / off
You can now write your component which will use your custom mediator to turn the line pointers on and off.
public class PointerConfigurationExample : MonoBehaviour
{
/* Turns off all far interaction pointers */
public void TurnOffFarPointers()
{
Debug.Log("Line pointers off");
SetFarPointersDisabled(true);
}
public void TurnOnFarPointers()
{
Debug.Log("Line pointers on");
SetFarPointersDisabled(false);
}
private void SetFarPointersDisabled(bool isDisabled)
{
FocusProvider focusProvider = (FocusProvider) MixedRealityToolkit.InputSystem.FocusProvider;
if (focusProvider != null)
{
foreach(var mediator in focusProvider.PointerMediators)
{
// Note: you could check here to make sure you only disable pointers for hands
CustomPointerMediator myMediator = (CustomPointerMediator) (mediator.Value);
if (myMediator != null)
{
myMediator.FarPointersDisabled = isDisabled;
}
}
}
}
}
Where can I find the properties of the BaseEnum DatabaseLogType in AX?
It is used in (Sys)DatabaseLog?
I know there is a MSDN-Link, but is there any way to get those properties in AX itself?
Open the AOT and browse to Data Dictionary -> Base Enums.
Expand the type, and the element are listed.
You can then view the element properties.
Edit: This doesn't work for DatabaseLogType as it is a System Enum and not a Base Enum. I will leave the answer for future reference when people are searching for Base Enums
Edit 2: Work around for system enums!
As system Enums cannot be viewed in the AOT you can use the following job to view them (works on any Enum);
static void DisplaySystemEnum(Args _args)
{
DictEnum dblt;
int i;
;
dblt = new DictEnum(enumName2Id("DatabaseLogType"));
for (i=0; i < dblt.values(); i++)
{
info(strfmt("Value:%1 Name:%2 Label:%3",
int2str(dblt.index2Value(i)),
dblt.index2Name(i),
dblt.index2Label(i)));
}
}
Also take a look at MSDN DictEnum Class to see what else you could do to customise the job for your own needs.
So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet or DataTable. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows and the like. But what I cannot seem to figure out or find online is how to go the other direction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod] something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
FlourineFX(FOSS)
WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person table in a database, and have a typed DataSet called PeopleDataSet that has PersonTableAdapter and PersonDataTable with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}