SPItemEvent: abort the event from the EventHandler - sharepoint

How can I abort an item even (in my case, ItemDeleting) so that it doesn'r get executed? I want the deletion not to take place if certain conditions are matched and do it silently for the use (no messages, no exceptions). Thanks
EDIT:
SP 2010

public override void ItemDeleting(SPItemEventProperties properties) {
properties.Cancel = true;
properties.ErrorMessage = "Something went wrong!";
}
If you cancel it though, it will be reported back to the user, nothing you can do about that.
UPDATE
For use the Status property
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.speventpropertiesbase.status.aspx
public override void ItemDeleting(SPItemEventProperties properties) {
properties.Status = SPEventReceiverStatus.CancelNoError;
}

Related

How can I keep a user field from coming through on a Journal Entry release function?

I have a customization to the Journal Transactions screen where I've added a user field to the grid (GLTran). When the Release function is initiated, it adds lines as it should - but it populates the user field with the values I had in the original lines, and I don't want this to happen. How can I intercept this process (I've looked at the source code and I can't see any functions I can use) to prevent this from happening?
I've tried the RowInserting / RowInserted events to set the field to null or blank, but this does nothing.
Thanks much -
From Acumatica support - an override of the Release process needs to be added to the Journal Entry BLC extension code, adding Event handlers as follows:
public delegate IEnumerable ReleaseDelegate(PXAdapter adapter);
[PXOverride]
public IEnumerable Release(PXAdapter adapter, ReleaseDelegate baseMethod)
{
PXGraph.InstanceCreated.AddHandler<PostGraph>((graph) =>
{
graph.RowPersisting.AddHandler<GLTran>((sender, e) =>
{
var gltran = e.Row as GLTran;
if (gltran != null)
{
var gltranext = PXCache<GLTran>.GetExtension<GLTranExt>(gltran);
gltranext.UsrProject = null;
}
});
});
return baseMethod(adapter);
}
Thanks much, Cesar!

Cannot trigger cancel button action after processing results returned

Within the Acumatica 19.201.0070 framework I have created a custom processing page that utilizes PXFilteredProcessing with the old style processing UI public override bool IsProcessing => false; I have defined a cancel button (below) that will clear the graph and set some values of the processing filter.
public PXCancel<NPMasterSubGeneratorFilter> Cancel;
[PXCancelButton()]
protected virtual IEnumerable cancel(PXAdapter adapter)
{
NPMasterSubGeneratorFilter row = Filter.Current;
if (row != null)
{
this.Clear();
Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentID>(Filter.Current, row.SegmentID);
if (!(row.NewSegment ?? false)) Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentValue>(Filter.Current, row.SegmentValue);
}
return adapter.Get();
}
This works perfectly fine except for a single use case, after processing results are shown if the user then presses the cancel button the corresponding action is never hit. ( My fellow office devs state that core Acumatica processing pages seem to operate the same. )
Setting of the processing delegate is within the filter RowSelected event.
GeneratedSubs.SetProcessDelegate(list => CreateSubaccounts(list, row));
I have implemented a few iterations of my processing method but the current is below.
protected virtual void CreateSubaccounts(List<NPGeneratedSub> subs, NPMasterSubGeneratorFilter filter)
{
if (filter.NewSegment ?? false)
{
try
{
SegmentMaint segGraph = PXGraph.CreateInstance<SegmentMaint>();
segGraph.Segment.Update(segGraph.Segment.Search<Segment.dimensionID, Segment.segmentID>(AADimension.Subaccount, filter.SegmentID.Value));
SegmentValue value = segGraph.Values.Insert(new SegmentValue() { Value = filter.SegmentValue, Descr = filter.Description });
segGraph.Actions.PressSave();
}
catch
{
throw new PXOperationCompletedSingleErrorException(NonProfitPlusMessages.SegmentValueCannotCreate);
}
}
SubAccountMaint subGraph = PXGraph.CreateInstance<SubAccountMaint>();
NPSubAccountMaintExtension subGraphExt = subGraph.GetExtension<NPSubAccountMaintExtension>();
subGraphExt.save.ConfirmSaving = false;
Sub newSub;
bool errored = false;
foreach (NPGeneratedSub sub in subs)
{
PXProcessing<NPGeneratedSub>.SetCurrentItem(sub);
try
{
newSub = subGraph.SubRecords.Insert(new Sub() { SubCD = sub.SubCD, Description = sub.Description });
subGraph.Save.Press();
subGraph.Clear();
PXProcessing<NPGeneratedSub>.SetProcessed();
}
catch (Exception e)
{
PXProcessing<NPGeneratedSub>.SetError(e);
errored = true;
}
}
if (errored)
{
throw new PXOperationCompletedWithErrorException();
}
}
What needs to be adjusted to allow the buttons action to be triggered on press after processing results have been returned?
After stepping through the javascript I discovered that it wasn't sending a request to the server when you click the cancel button on this screen after processing. The reason is because SuppressActions is getting set to true on the Cancel PXToolBarButton. I compared what I was seeing on this screen to what was happening on screens that work correctly and realized that Acumatica is supposed to set SuppressActions to true on the Schedule drop down PXToolBarButton but for some reason, on this screen, it is incorrectly setting it to true on whatever button is after the Schedule drop down button.
I looked through the code in PX.Web.UI and it looks like they set SuppressActions to true when a drop down button is disabled and PXProcessing adds a FieldSelecting event to the Schedule button which disables the button after you click process. However, I didn't notice any obvious issues as to why the code would be setting it on the wrong PXToolBarButton so someone will likely need to debug the code and see what's going on (we are unable to debug code in PX.Web.UI.dll).
I tried commenting out the other grids in the aspx file that aren't related to the PXProcessing view and this resolved the issue. So my guess would be that having multiple grids on the PXProcessing screen somehow causes a bug where it sets SuppressActions on the wrong PXToolBarButton. However, since the multiple grids are a business requirement, removing them is not a solution. Instead, I would suggest moving all buttons that are after the schedule button to be before the schedule button. To do this, just declare the PXActions before the PXFilteredProcessing view in the graph.
Please try this
Override IsDirty property
Use PXAction instead of PXCancel
Add PXUIField attribute with enable rights
action name should start from lowercase letter
delegate name should start from uppercase letter
see code below
public override bool IsDirty => false;
public override bool IsProcessing
{
get { return false;}
set { }
}
public PXAction<NPMasterSubGeneratorFilter> cancel;
[PXUIField(MapEnableRights = PXCacheRights.Select)]
[PXCancelButton]
protected virtual IEnumerable Cancel(PXAdapter adapter)
{
NPMasterSubGeneratorFilter row = Filter.Current;
if (row != null)
{
this.Clear();
Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentID>(Filter.Current, row.SegmentID);
if (!(row.NewSegment ?? false)) Filter.SetValueExt<NPMasterSubGeneratorFilter.segmentValue>(Filter.Current, row.SegmentValue);
}
return adapter.Get();
}

Intellij Idea Live Template to create field and method at same time

How to create field variable automatically when I create method used that field. I've create template like this:
void $METHOD_NAME$() {
$FIELD_NAME$ = true;
}
when I type field name (e.g. mState) in method will create field as:
private boolean mState = false;
Hope someone help. Sorry my bad.
Given the screenshot of your template, you can also create a field with the following live template:
private boolean $param$ = false;
#Override
public void onBackPressed() {
if ($param$) super.onBackPressed();
android.widget.Toast.makeText(this, "$message$",
android.widget.Toast.LENGTH_SHORT).show();
$param$ = true;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
$param$ = false;
}
}, 100);
}
Where $param$ and $message$ are regular variables without anything special.
However, like I said in the comment on your question, I suggest to split it up in several smaller templates.
Consider to split it up in:
field + method with just:
private boolean $param$ = false;
#Override
public void onBackPressed() {
if ($param$) super.onBackPressed();
$param$ = true;
}
Then create a template for the message:
android.widget.Toast.makeText(this, "$message$", android.widget.Toast.LENGTH_SHORT).show();
And last but not least, create a template for the postDelayed:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
$END$
}
}, $delay$);
Note: the $delay$ as a bonus you can even give it a default value or create a list of predefined values for ease of use.
Note2: Instead of $param$ = false; I've replaced it with $END$. This will position your cursor here once you've selected the delay. Now you can type mState = false manually here, or whatever code you need in the context at that moment. This makes the template much more flexible and easier to use.
PS. I suppose you want to call super.onBackPressed() only when the value is false (on the first invocation). In that case use if (!$param$) instead.
// Update:
In order to group the newly added field with the other fields and not halfway somewhere in your class between other methods, rearrange the code
via the menu with: Code -> rearrange code.
To customise this, check your arrangement settings under: settings -> code style -> <language> -> arrangement

Update UI using Parallel Task

I have method
public override void InitializeRow(object sender, InitializeRowEventArgs e)
{
if (!e.ReInitialize)
Task.Factory.StartNew(() =>
{
AfterInitializeRow(sender, e);
});
}
public override void AfterInitializeRow(object sender, InitializeRowEventArgs e)
{
foreach (UltraGridColumn ugc in e.Row.Band.Columns)
{
if (IsNumeric(ugc.Key))
{
e.Row.Cells[ugc].DroppedDown = true;
e.Row.Cells[ugc].ValueList = “Some value”;
e.Row.Cells[ugc].SetValue(e.Row.Cells[ugc.Key].Value, false);
e.Row.Cells[ugc].Style = Infragistics.Win.UltraWinGrid.ColumnStyle.DropDownList;
}
}
}
But its Giving error at e.Row.Cells[ugc].DroppedDown = true;
I learned that only Main thread can update the UI.
But is it possible that while updating the DroppedDown only it switch to main thread. Bcoz more than 1000’s rows are initialized in this way making the load of Grid very slow. So I want to do some kind of parallelism in this process.
In any function in your Form or UserControl, you can use the following type of code:
public void SetText(string text)
{
if (InvokeRequired)
{
BeginInvoke(new Action<string>(SetText), text);
}
else
{
label1.Text = text;
}
}
label1 would be the control to update in this case.
This will make sure that you invoke the function on the UI-thread.
You should still be careful with syncrhonization, though, but simply updating your UI from another thread can be easily done like that.
The answer to this question is that you shouldn't be using threading in the InitialzieRow event to set or even access properties on the grid or its related objects.
What you should do instead is look for ways to optimize what you are doing in this method first. For example why are you setting the value of a cell to the value it already has, this line of code should be able to be removed without impacting behavior.
Also all of the logic provided is only based on the column key so if the column has a consistent set of values, you could set the ValueList on the column in InitializeLayout instead of using InitializeRow.

How should one implement a change state dialog with undo/redo support in Catel?

I cannot get Undo and Redo to behave correctly when using a dialog.
I have a simple model with a property indicating the state of the object(running, paused, stopped) which can be altered via a dialog. What happens is that I get actions that seems to do nothing in my undo queue or undo restores the object to an intermediate state.
The model object is registered with memento in the constructor. The dialog has three radio buttons each representing one of the three different states. Each radio button is bind to a command each. Each command performs a change of the property. I have tried two different approaches, either each command sets the property directly in the object or each command sets an instance variable for the view model when called and then I use the Saving event to modify the object.
If using the first approach each property change is put on the Undo queue if the user clicks on more than just one radiobutton before clicking Ok in the dialog. Tried to solve that by wrapping the whole dialog into a batch but that results in undoing the state change the object is restored to the state it had before the final one, i.e. if the property was set to stopped before the dialog opened and the user pressed the pause radiobutton, then start one and finally Ok, undo will set the property to paused instead of the expected stopped.
If using the second approach the user opens the dialog, change the state to paused, click Ok in the dialog the undo/redo behaves as expected but if the dialog is opened again and Cancel is chosen one more action is added to the Undo queue, i.e. the user has to click Undo twice to get back to the initial stopped-state.
So my question is how should this be correctly implemented to get the expected behaviour; that each dialog interaction can be undone and not every interaction in the dialog?
Here is the code for the ViewModel:
namespace UndoRedoTest.ViewModels
{
using Catel.Data;
using Catel.MVVM;
public class StartStopViewModel : ViewModelBase
{
Machine.MachineState _state;
public StartStopViewModel(Machine controlledMachine)
{
ControlledMachine = controlledMachine;
_state = controlledMachine.State;
StartMachine = new Command(OnStartMachineExecute);
PauseMachine = new Command(OnPauseMachineExecute);
StopMachine = new Command(OnStopMachineExecute);
Saving += StartStopViewModel_Saving;
}
void StartStopViewModel_Saving(object sender, SavingEventArgs e)
{
ControlledMachine.State = _state;
}
[Model]
public Machine ControlledMachine
{
get { return GetValue<Machine>(ControlledMachineProperty); }
private set { SetValue(ControlledMachineProperty, value); }
}
public static readonly PropertyData ControlledMachineProperty = RegisterProperty("ControlledMachine", typeof(Machine));
public override string Title { get { return "Set Machine state"; } }
public Command StartMachine { get; private set; }
public Command PauseMachine { get; private set; }
public Command StopMachine { get; private set; }
private void OnStartMachineExecute()
{
_state = Machine.MachineState.RUNNING;
//ControlledMachine.SecondState = Machine.MachineState.RUNNING;
}
private void OnPauseMachineExecute()
{
_state = Machine.MachineState.PAUSED;
//ControlledMachine.SecondState = Machine.MachineState.PAUSED;
}
private void OnStopMachineExecute()
{
_state = Machine.MachineState.STOPPED;
//ControlledMachine.SecondState = Machine.MachineState.STOPPED;
}
}
}
First of all, don't subscribe to the Saving event but simply override the Save() method. Note that Catel handles the model state for you when you decorate a model with the ModelAttribute. Therefore you need to get the prestate and poststate of the dialog and then push the result set into a batch.
For example, I would create extension methods for the object class (or model class) like this:
public static Dictionary<string, object> GetProperties(this IModel model)
{
// todo: return properties
}
Then you do this in the Initialize and in the Save method and you would have 2 sets of properties (pre state and post state). Now you have that, it's easy to calculate the differences:
public static Dictionary<string, object> GetChangedProperties(Dictionary<string, object> preState, Dictionary<string, object> postState)
{
// todo: calculate difference
}
Now you have the difference, you can create a memento batch and it would restore the exact state as you expected.
ps. it would be great if you could put this into a blog post once done or create a PR with this feature

Resources