Suppress unsaved changes message in Cuba Platform - cuba-platform

I have a lookup screen with authors and I load the data by using a custom query in init from AuthorsBrowse::AbstractLookup. I manipulate the data I want to display, but the changes should not be saved.
If I close the tab i get this message dialog:
You have unsaved changes
Do you want to discard unsaved changes?
How can I suppress this message?

This message appears if anything datasource was changed.
If you want suppress message, you need to add to datasource (that was changed) attribute allowCommit="false", like that:
<collectionDatasource id="authorsDs"
class="com.haulmont.workshop.core.entity.Author"
view="_local" allowCommit="false">
<query>
<![CDATA[
select e from ws$Author e where e.status = 10
]]>
</query>
</collectionDatasource>

You can also implement a more fine-grained control over the unsaved changes behavior of your screen if the screen implements Window.Committable interface. The interface contains isModified() method which returns a boolean value.
AbstractEditor already implements this interface and its isModified() looks as follows:
#Override
public boolean isModified() {
return getDsContext() != null && getDsContext().isModified();
}
You can implement Window.Committable in your browser screen and just return false or write some logic in isModified().

Related

Update Checked State of Handler in E4

In E3 we had a couple of handlers that were to be checked, and so the handler had to figure out when to be checked. Back then that was an easy task:
public class MyHandler extends AbstractHandler implements IElementUpdater {
#Override
public void updateElement(UIElement element, Map parameters) {
element.setChecked(calculateCheckState());
}
}
I found this similar question, but it's much broader (updating all commands, while I only want to set the checked state whenever the framework seems it necessary). Since tool and menu items can be check or radio items, this has to be possible somehow.
So how do I set the check state of a handler in E4?
You can set the check (selected) state in the #CanExecute method of the handler using something like:
#CanExecute
public boolean canExecute(MItem item)
{
item.setSelected(... checked state ....);
return true;
}
For a menu item the #CanExecute method is called every time the menu is displayed.
For a tool bar item you may need to use the IEventBroker UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC event to force the method to run.
eventBroker.send(UIEvents.REQUEST_ENABLEMENT_UPDATE_TOPIC, argument);
argument can be
UIEvents.ALL_ELEMENT_ID to update all items.
The id of a single item to be updated.
A Selector (org.eclipse.e4.ui.workbench.Selector) to select the items to be updated.

is it possible to call an event receiver from an event receiver with different properties?

I have an event receiver that runs when metadata on a folder (docset) is updated. In the same event receiver, I want to run kick off an event receiver for each document in the folder. What I want to know is: is it possible to call an event receiver using a different SPitemEventProperties than the one given.
e.g.
public override void ItemUpdated (SPItemEventProperties properties) {
// when properties is/contains a folder:
// on each document in the folder
// run ItemUpdated where properties is a/contains a document
}
Is it possible to do this? If so, any ideas?
Short answers: no. There is possibly a really hacky way to do it but no obvious solution.
For my particular situation, it was easier to setup the environment so the document inherits the changing piece of metadata so it's event receiver fires when this column changes.
Hope it helps someone else in the future, or for my own records if no one else reads this.
You can call SPListItem.Update() on each document folder ItemUpdatedER. This run document EventReceiver.
public override void ItemUpdated (SPItemEventProperties properties) {
query all sub items/documents
on each document change your data:
item["customField"] = "update value";
item.Update() //call recursively ItemUpdating/ItemUpdate
}

Metro c++ async programming and UI updating. My technique?

The problem: I'm crashing when I want to render my incoming data which was retrieved asynchronously.
The app starts and displays some dialog boxes using XAML. Once the user fills in their data and clicks the login button, the XAML class has in instance of a worker class that does the HTTP stuff for me (asynchronously using IXMLHTTPRequest2). When the app has successfully logged in to the web server, my .then() block fires and I make a callback to my main xaml class to do some rendering of the assets.
I am always getting crashes in the delegate though (the main XAML class), which leads me to believe that I cannot use this approach (pure virtual class and callbacks) to update my UI. I think I am inadvertently trying to do something illegal from an incorrect thread which is a byproduct of the async calls.
Is there a better or different way that I should be notifying the main XAML class that it is time for it to update it's UI? I am coming from an iOS world where I could use NotificationCenter.
Now, I saw that Microsoft has it's own Delegate type of thing here: http://msdn.microsoft.com/en-us/library/windows/apps/hh755798.aspx
Do you think that if I used this approach instead of my own callbacks that it would no longer crash?
Let me know if you need more clarification or what not.
Here is the jist of the code:
public interface class ISmileServiceEvents
{
public: // required methods
virtual void UpdateUI(bool isValid) abstract;
};
// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
_myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
// This is where I would render my assets if I could.
// Cannot legally do much here. Always crashes.
// Follow the rest of the code to get here.
}
// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
Uri^ uri = ref new URI(MY_SERVER + "login.json");
String^ inJSON = "some json input data here"; // serialized email and password with other data
// make the HTTP request to login, then notify XAML that it has data to render.
_myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
String^ assets = MyParser::Parse(outputJSON);
// The Login has returned and we have our json output data
if(_delegate)
{
_delegate->UpdateUI(assets);
}
});
}
// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
return _httpRequest.PostAsync(uri,
json->Data(),
_cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
{
try
{
if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
String^ j = ref new String(response.get().c_str());
return j;
}
catch (Exception^ ex) .......;
return ref new String(L"");
}, task_continuation_context::use_current());
}
Edit: BTW, the error I get when I go to update the UI is:
"An invalid parameter was passed to a function that considers invalid parameters fatal."
In this case I am just trying to execute in my callback is
txtBox->Text = data;
It appears you are updating the UI thread from the wrong context. You can use task_continuation_context::use_arbitrary() to allow you to update the UI. See the "Controlling the Execution Thread" example in this document (the discussion of marshaling is at the bottom).
So, it turns out that when you have a continuation, if you don't specify a context after the lambda function, that it defaults to use_arbitrary(). This is in contradiction to what I learned in an MS video.
However by adding use_currrent() to all of the .then blocks that have anything to do with the GUI, my error goes away and everything is able to render properly.
My GUI calls a service which generates some tasks and then calls to an HTTP class that does asynchronous stuff too. Way back in the HTTP classes I use use_arbitrary() so that it can run on secondary threads. This works fine. Just be sure to use use_current() on anything that has to do with the GUI.
Now that you have my answer, if you look at the original code you will see that it already contains use_current(). This is true, but I left out a wrapping function for simplicity of the example. That is where I needed to add use_current().

How to make a propertysheetpage be a selection provider?

I've got a contributed command and a handler for it. The handler's execute event has to get the value for the property actually selected in the properties view and act on it, or to be disabled if no property selected.
I've tried:
1) Set the selection provider to something which provides selection from the property view. Something in this case is just PropertySheetViewer for my PropertySheetPage, but i can't set it as the selection provider because the PropertySheetPage's viewer is private and has no getter.
2) Overriding PropertySheetPage's createControl method: This method creates a Tree control for the PropertySheetViewer. A selection listener can be installed for that tree control, so maybe i can make my command handler implement SelectionListener... The solution would be somethin like:
In my editor:
public Object getAdapter(#SuppressWarnings("rawtypes") Class type) {
if (type == IPropertySheetPage.class) {
PropertySheetPage page = new PropertySheetPage() {
#Override
public void createControl(Composite parent) {
super.createControl(parent);
IHandler handler = someWayToGetMyCmdHandler();
((org.eclipse.swt.widgets.Tree) getControl())
.addSelectionListener(handler);
}
};
IPropertySheetEntry entry = new UndoablePropertySheetEntry(
getCommandStack());
page.setRootEntry(entry);
return page;
}
return super.getAdapter(type);
}
And my command handler implementing SelectionListener as i said... The problem with this approach is that i can't find a way to get a reference to my contributed command handler (someWayToGetMyCmdHandler() above).
Has anybody got any clue on this, or any other possible approach to the problem??
There's handleEntrySelection(ISelection selection) method in PropertySheetPage that you could override to be notified about selection changes in the viewer (although PropertySheetPage is #noextend).
The second part (updating the handler) is a bit more tricky than it would normally be. Commands/handlers get updated automatically when workbench selection changes (you just need to implement setEnabled(Object evaluationContext) AbstractHandler). But since PropertySheetPage is designed to change its input on global selection change, then you have to find some custom way to notify/update your handler.
As I understand, it is currently not possible to extend the platform command event handling mechanism with custom variables, so you just need to directly look up your handler using IHandlerService of the workbench.

Silverlight 4.0 Form Initialization Problem

I am getting a very strange error on one of my Silverlight 4.0 pages. I have a form that has a "save" button which is disabled by default. This form gets populated by a bunch of user-specified defaults, which come from an asynchronous server call (MyFacade.getFormDefaults below). When the user changes one of the fields (after it's populated), I want the "save" button to become enabled.
I think I have the logic correct, but I'm getting a very strange error that I can't find much useful information on. The error is: System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.
Below is a very simplified version of what I have...
profile.fs:
type profile() as this =
inherit UriUserControl("/whatever;component/profile.xaml", "profile")
[<DefaultValue>]
val mutable isFormLoaded : bool
[<DefaultValue>]
val mutable btnSave : Button
[<DefaultValue>]
val mutable txtEmail : TextBox
// constructor
do
this.isFormLoaded <- false
// make the "this" values point at the XAML fields
this.btnSave <- this?btnSave
this.txtEmail <- this?txtEmail
// get the form defaults and send them to
MyFacade.getFormDefaults(new Action<_>(this.populateFormDefaults))
()
member this.populateFormDefaults (formDefaults : MyFormDefaultsUIVO array option) =
// populate this.txtEmail with the default value here
this.isFormLoaded <- true // set the form to be loaded once that's done
()
// enable the "Save" button when the user modifies a form field
member this.userModifiedForm (sender : obj) (args : EventArgs) =
// **** EXCEPTION OCCURS ON THE LINE BELOW ****
if this.isFormLoaded then
this.btnSave.IsEnabled <- true
()
profile.xaml:
<nav:Page Name="profile" Loaded="formLoaded">
<TextBox Name="txtEmail" TextChanged="userModifiedForm "/>
<Button Name="btnSave" IsEnabled="False"/>
</nav:Page>
Even if I get rid of all the isFormLoaded logic, and simply set this.btnSave.IsEnabled <- true inside of this.userModifiedForm, I get the same error. Any ideas would be greatly appreciated. Thanks.
The exception - "The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized" - is generated by the F# runtime when an object is accessed before being fully initialized.
Accessing this - whether to check isFormLoaded or btnSave.IsEnabled - before the constructor has run will cause the error. Have you verified that userModifiedForm is only called after the constructor?