I want to auto-update the Format field of the NumberSequenceTable form. The purpose is to help the user while manually entering the data.
For example this field should preferably be updated with concatenated values of the fields Number sequence code and Largest.
After thinking for a while I decided to change the underlying table NumberSequenceTable. I changed its "modifiedField" method by adding another case to the already existing switch statement. I think this is the accepted best practice in such cases:
public void modifiedField(fieldId _fieldId)
{
#Define.DefaultCleanInterval(24)
#Define.DefaultCacheSize(10)
str sequenceFormat;
super(_fieldId);
switch (_fieldId)
{
....
//BEGINING
case fieldnum(NumberSequenceTable, NumberSequence):
if (this.NumberSequence && this.Highest && !this.Format)
{
sequenceFormat = System.Text.RegularExpressions.Regex::Replace(int2str(this.Highest), "[0-9]", "#");
this.Format = strFmt("%1_%2",this.NumberSequence, sequenceFormat);
}
break;
//END
default :
break;
}
}
My previous solution involved overriding the "enter" method on the form field. However this would not be in line with best practices. For information purposes I am pasting here the initial solution.
public void enter()
{
str sequenceNode;
str sequenceValue;
str maxValue;
str formatValue;
;
super();
//Get required fields value
sequenceNode = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,NumberSequence)).getValue();
maxValue = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Highest)).getValue();
formatValue = numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Format)).getValue();
if(sequenceNode && maxValue && !formatValue) {
//Match regex pattern
maxValue = System.Text.RegularExpressions.Regex::Replace(maxValue, "[0-9]", "#");
sequenceValue = strFmt("%1_%2",sequenceNode,maxValue);
//Change field value
numberSequenceTable_ds.object(fieldnum(NumberSequenceTable,Format)).setValue(sequenceValue);
this.update();
}
}
Related
I am using custom lookup code on a form string control. It works great, unless there is any text in the field. Then, it always returns blank unless what is entered in the field is an exact match for a value in the list. My preference would be for the text in the field to serve as a startsWith filter (and already addressed when I make the value list), but at this point, I would be happy with even the full list appearing, instead of blanks.
public static client void lookupList(FormStringControl _formStringControl, List _valueList)
{
ListIterator valueIterator;
myLookupTempTable tempLookupTable;
SysTableLookup sysTableLookup;
if (_formStringControl && _valueList && _valueList.typeId() == Types::String)
{
valueIterator = new ListIterator (_valueList);
while (valueIterator.more())
{
tempLookupTable.Value = valueIterator.value();
tempLookupTable.insert();
valueIterator.next();
}
sysTableLookup = SysTableLookup::newParameters(tableNum(myLookupTempTable), _formStringControl);
sysTableLookup.addLookupfield(fieldNum(myLookupTempTable, Value), true);
sysTableLookup.parmTmpBuffer(tempLookupTable);
sysTableLookup.performFormLookup();
//dispose of the temp table
tempLookupTable = null;
}
}
After reading this article:
http://blogs.msdn.com/b/tilovell/archive/2009/12/29/the-trouble-with-system-activities-foreach-and-parallelforeach.aspx
I have defined the ForEachFactory as follows:
public class ForEachFactory<T> : IActivityTemplateFactory
{
public Activity Create(DependencyObject target)
{
return new ForEach<T>
{
DisplayName = "ForEachFromFactory",
Body = new ActivityAction<T>
{
Argument = new DelegateInArgument<T>("item")
}
};
}
}
All works well but is it possible to check how that DelegeateInArgument in my case named "item" changes its value ?
So if i have defined an array in the variables section and initialized with
{1, 2, 3} i need a way to check how the "item" takes value 1, 2 and then 3.
To be more accurate, i've added this pic, with a breakpoint on the WriteLine activity inside the foreach. When the execution will stop there, is there a way to find out what the value of item is ?
EDIT 1:
Possible solution in my case:
After struggling a bit more i found one interesting thing:
Adding one of my custom activities in the Body of the ForEach, i am able to get the value of the item like this :
So, my activity derives from : CodeActivity
Inside the protected override String[] Execute(CodeActivityContext context) i am doing this job.To be honest, this solves the thing somehow, but it is doable only in my custom activities. If i would put a WriteLine there for example, i would not be able to retrieve that value.
you can access the DelegeateInArgument of a ForEach activity by inspecting the ModelItem trees parent and checking for DelegeateInArgument's. If you need a specific code example to achieve this I may need a some time to code the example. As it has been a long time since I did this, see my question i asked over on msdn
So basically where your break point is, you can access the variable values as these are defined with n the scope of your activity as 'variables'. However the 'item' variable is actually only accessible from the parent loop activity. So you have to get the model item of the current executing activity and then traverse up the tree to find the parent containing the desired DelegateInArgument.
Can you flesh out exactly what you want to achieve? Is it that when your debugging the workflow in the re-hosted designer you want to display the variable values to the user as they change in the UI?
Edit - added tracking example
So as your wanting to display the variable values during execution of the workflow we need to use tracking to achieve this. In the example your using the author has already implemented some basic tracking. So to achieve the extended variable tracking you want you will need to alter the tracking profile.
Firstly amend the WorkflowDesignerHost.xaml.cs file alter the RunWorkflow method to define the SimulatorTrackingParticipant as below.
SimulatorTrackingParticipant simTracker = new SimulatorTrackingParticipant()
{
TrackingProfile = new TrackingProfile()
{
Name = "CustomTrackingProfile",
Queries =
{
new CustomTrackingQuery()
{
Name = all,
ActivityName = all
},
new WorkflowInstanceQuery()
{
**States = {all },**
},
new ActivityStateQuery()
{
// Subscribe for track records from all activities for all states
ActivityName = all,
States = { all },
**Arguments = {all},**
// Extract workflow variables and arguments as a part of the activity tracking record
// VariableName = "*" allows for extraction of all variables in the scope
// of the activity
Variables =
{
{ all }
}
}
}
}
};
This will now correctly capture all workflow instance states rather than just Started/Completed. You will also capture all Arguments on each activity that records tracking data rather than just the variables. This is important because the 'variable' were interested in is actually (as discussed earlier) a DelegateInArgument.
So once we have changed the tracking profile we also need to change the SimulatorTrackingParticipant.cs to extract the additional data we are now tracking.
If you change the OnTrackingRecordReceived method to include the following sections these will capture variable data and also Argument data during execution.
protected void OnTrackingRecordReceived(TrackingRecord record, TimeSpan timeout)
{
System.Diagnostics.Debug.WriteLine(
String.Format("Tracking Record Received: {0} with timeout: {1} seconds.", record, timeout.TotalSeconds)
);
if (TrackingRecordReceived != null)
{
ActivityStateRecord activityStateRecord = record as ActivityStateRecord;
if (activityStateRecord != null)
{
IDictionary<string, object> variables = activityStateRecord.Variables;
StringBuilder vars = new StringBuilder();
if (variables.Count > 0)
{
vars.AppendLine("\n\tVariables:");
foreach (KeyValuePair<string, object> variable in variables)
{
vars.AppendLine(String.Format(
"\t\tName: {0} Value: {1}", variable.Key, variable.Value));
}
}
}
if (activityStateRecord != null)
{
IDictionary<string, object> arguments = activityStateRecord.Arguments;
StringBuilder args = new StringBuilder();
if (arguments.Count > 0)
{
args.AppendLine("\n\tArgument:");
foreach (KeyValuePair<string, object> argument in arguments)
{
args.AppendLine(String.Format(
"\t\tName: {0} Value: {1}", argument.Key, argument.Value));
}
}
//bubble up the args to the UI for the user to see!
}
if((activityStateRecord != null) && (!activityStateRecord.Activity.TypeName.Contains("System.Activities.Expressions")))
{
if (ActivityIdToWorkflowElementMap.ContainsKey(activityStateRecord.Activity.Id))
{
TrackingRecordReceived(this, new TrackingEventArgs(
record,
timeout,
ActivityIdToWorkflowElementMap[activityStateRecord.Activity.Id]
)
);
}
}
else
{
TrackingRecordReceived(this, new TrackingEventArgs(record, timeout,null));
}
}
}
Hope this helps!
I am trying to make this question sound as clear as possible.
Basically, I have created a report, and it now exists as a menuitem button so that the report can run off the form.
What I would like to do, is be able to multi-select records, then when I click on my button to run my report, the current selected records are passed into the dialog form (filter screen) that appears.
I have tried to do this using the same methods as with the SaleLinesEdit form, but had no success.
If anyone could point me in the right direction I would greatly appreciate it.
Take a look at Axaptapedia passing values between forms. This should help you. You will probably have to modify your report to use a form for the dialog rather than using the base dialog methods of the report Here is a good place to start with that!
Just wanted to add this
You can use the MuliSelectionHelper class to do this very simply:
MultiSelectionHelper selection = MultiSelectionHelper::createFromCaller(_args.caller());
MyTable myTable = selection.getFirst();
while (myTable)
{
//do something
myTable = selection.getNext();
}
Here is the resolution I used for this issue;
Two methods on the report so that when fields are multi-selected on forms, the values are passed to the filter dialog;
private void setQueryRange(Common _common)
{
FormDataSource fds;
LogisticsControlTable logisticsTable;
QueryBuildDataSource qbdsLogisticsTable;
QueryBuildRange qbrLogisticsId;
str rangeLogId;
set logIdSet = new Set(Types::String);
str addRange(str _range, str _value, QueryBuildDataSource _qbds, int _fieldNum, Set _set = null)
{
str ret = _range;
QueryBuildRange qbr;
;
if(_set && _set.in(_Value))
{
return ret;
}
if(strLen(ret) + strLen(_value) + 1 > 255)
{
qbr = _qbds.addRange(_fieldNum);
qbr.value(ret);
ret = '';
}
if(ret)
{
ret += ',';
}
if(_set)
{
_set.add(_value);
}
ret += _value;
return ret;
}
;
switch(_common.TableId)
{
case tableNum(LogisticsControlTable):
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrLogisticsId = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, LogisticsId));
fds = _common.dataSource();
for(logisticsTable = fds.getFirst(true) ? fds.getFirst(true) : _common;
logisticsTable;
logisticsTable = fds.getNext())
{
rangeLogId = addrange(rangeLogId, logisticsTable.LogisticsId, qbdsLogisticsTable, fieldNum(LogisticsControlTable, LogisticsId),logIdSet);
}
qbrLogisticsId.value(rangeLogId);
break;
}
}
// This set the query and gets the values passing them to the range i.e. "SO0001, SO0002, SO000£...
The second methods is as follows;
private void setQueryEnableDS()
{
Query queryLocal = element.query();
;
}
Also on the init method this is required;
public void init()
{
;
super();
if(element.args() && element.args().dataset())
{
this.setQueryRange(element.args().record());
}
}
Hope this helps in the future for anyone else who has the issue I had.
I have a table named Orders that has a column named OrderCode that stores a string that I randomly generate at the time of creation.
I want to make sure this string (OrderCode) is unique before I save it to my table.
How I have attempted to do this:
bool isUnique = false;
var order = new Order();
var code = RandomCode.Generate();
while (isUnique == false) // checks to see if the code we generated is unique among all generated codes, if not, will generate another code
{
var activeOrders = storeDB.Orders.Find("OrderCode", code);
if (activeOrders == null)
{
isUnique = true;
}
else
{
code = RandomCode.Generate();
}
}
order.OrderCode = code;
The problem appears to be that the DbSet<TEntity>.Find Method is actually used to search through primary keys - but I am needing to search for a string that is not a primary key.
What is a correct approach to this situation?
if (context.Orders.Any(o => o.OrderCode == code))
{
// key found
}
if (context.Orders.FirstOrDefault(o => o.OrderCode == code) != null)
{
// key found
}
May I ask why you don't just use a System.Guid though (Guid.NewGuid().ToString())? This would virtually eliminate this kind of issue.
I have following Linq query/function in my MVC3 application.
public AuditTrail GetNamesAddressesEmployers(long registryId , int changedField) {
var otherNameAndAddress = (from a in context.AuditTrails
where a.ChangedField == changedField
&& a.RegistryId == registryId
select a).FirstOrDefault();
return otherNameAndAddress;
}
I want that if otherNameAndAddress = null then its properties should be assigned some values.
otherNameAndAddress has Name and description property. This GetNamesAddressesEmployers is being used at 3 places. I want to assign different values to name and description when otherNameAndAddress = null at all three locations.
You're already using FirstOrDefault() so why not specify the Default:
public AuditTrail GetNamesAddressesEmployers(long registryId, int changedField)
{
return context.AuditTrails
.Where(a => a.ChangedField == changedField
&& a.RegistryId == registryId)
.DefaultIfEmpty(new AuditTrail { /* fill properties here */ })
.FirstOrDefault();
}
Well, you could change the return statement to:
return otherNameAndAddress ?? new AuditTrail { Name = "Default",
Description = "Default };
or something like that... but you say you want to assign different default values for different calls. That means you'll either need to pass the default in, or perform the defaulting (e.g. in the same way, via the null-coalescing operator) at the call site.
For example:
public AuditTrail GetNamesAddressesEmployers(long registryId, int changedField,
AuditField defaultValue) {
var otherNameAndAddress = (from a in context.AuditTrails
where a.ChangedField == changedField
&& a.RegistryId == registryId
select a).FirstOrDefault();
return otherNameAndAddress && defaultValue;
}
or keep it as it currently is, and use this at the call site:
var auditTrail = GetNamesAddressesEmployers(registryId, changedField) ??
new AuditTrail { Name = "Foo", Description = "Bar" };
It's not really clear which is best based on your description.
EDIT: As mentioned by Justin, you could use DefaultIfEmpty instead (just before FirstOrDefault). That means you have to pass the value in rather than doing it at the call site, but other than that they're very similar solutions.