JFace button in TableViewerColumn - button

Is it possible to have a button in a TableViewerColumn? There are several posts that confirm this, but I've found no code that actually works. I've read about a DialogCellEditor, too, is that what to look into?
Regards,
Marcus

As this seems to be a common problem, I've tried a workaround. I use an image as label and add editing support like so:
col = createTableViewerColumn(titles[10], bounds[10], 10);
col.setEditingSupport(new DeleteSupport(viewer));
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public Image getImage(Object element) {
return new Image(ApplicationRunner.getApp().getShell()
.getDisplay(), "ressources/images/delete.png");
}
#Override
public String getText(Object element) {
return "";
}
});
In the DeleteSupport class (extending EditingSupport), you have to let canEdit() return false, so the image is not selectable. But then, you can't work with getValue(). So, I do whatever I have to in canEdit() BEFORE returning false. That's the same behavior as a simple push button would have.
The DeleteSupport looks like this:
public class DeleteSupport extends EditingSupport {
private final TableViewer viewer;
public DeleteSupport(TableViewer viewer) {
super(viewer);
this.viewer = viewer;
}
#Override
protected CellEditor getCellEditor(Object element) {
return new TextCellEditor(viewer.getTable());
}
#Override
protected boolean canEdit(Object element) {
// if confirmed, try to delete the customer
if (MessageDialog.openConfirm( ApplicationRunner.getApp().getShell(),
"Confirm delete",
"Soll " + ((Customer) element).getFirstname()
+ " " + ((Customer) element).getLastname()
+ " be deleted? Cannot be undone!")) {
try {
CustomerDAO.getInstance().delete(((Customer) element).getId());
} catch (SQLException e) {
// TODO something
}
}
// reload anyways
try {
viewer.setInput(CustomerDAO.getInstance().getAll());
} catch (SQLException e) {
// TODO something else
}
viewer.refresh();
return false;
}
#Override
protected Object getValue(Object element) {
return "";
}
#Override
protected void setValue(Object element, Object value) {
}
}

Related

Send Data to HTML Template through Spring AOP?

I'd like to wrap all the controller methods using Spring AOP for Error Handling.
But, How to send e.getMessage() in catch block to ${errorMessage} in error.html properly?
Thanks for the response!
#Pointcut("within(com.test.mvc.controller.*) && #within(org.springframework.stereotype.Controller)")
public void controllerLayer() {
}
#Pointcut("execution(public String *(..))")
public void publicMethod() {
}
#Pointcut("controllerLayer() && publicMethod()")
public void controllerPublicMethod() {
}
#Around("controllerPublicMethod()")
public String processRequest(ProceedingJoinPoint joinPoint) {
try {
return (String) joinPoint.proceed();
} catch (Throwable e) {
LOGGER.info("{}", e.getMessage());
return "error.html";
}
}
<body>
<h1>Something went wrong!</h1>
<h3 th:text="'Error Message: ' + ${errorMessage}"></h3>
</body>
Following aspect can set the request attribute to display errorMessage.
#Around("controllerPublicMethod()")
public Object processRequest(ProceedingJoinPoint joinPoint) {
Object object=null;
try {
object = joinPoint.proceed();
} catch (Throwable e) {
RequestContextHolder.getRequestAttributes().setAttribute("errorMessage",e.getMessage(),0); // scope 0 - request , 1 - session
return "error.html";
}
return object;
}
I recommend you please explore the possibilities of #ControllerAdvice

Calling Async task in button click in xamarin.forms

I have xamarin.forms app contains a listview which will load values from Rest API.Which is working fine.I have button just above the listview.When I click on the button, the listview API call will be placed again and the listview should update. But stuck at this update part.I am not using MVVM pattern.The listview listing portion is an async Task.I am calling the async task again when the button click, but App gets crash. Is it due to calling the async task again from button click? Any help is appreciated.
Here is My code.
namespace app
{
public partial class List : ContentPage
{
PendingWeekRange pendingWeekRange = new PendingWeekRange();
public TimeSheetList()
{
InitializeComponent();
Task.Run(async () =>
{
await LoadScreenItems();
});
}
async Task LoadScreenItems()
{
await Task.Run(async () => {
try
{
// Doing some stuff
await loadTimeSheetList();
}
catch (Exception)
{
}
});
}
async Task loadTimeSheetList()
{
await Task.Run(() => { + string postdataForPendingList = "{\"date\":\"" + "1" + "\"}";
APICall callForAPICallResult = new APICall("/API/ListMobile/ListForApproval", postdataForList, loadingIndicator);
try
{
List<ListData> resultObjForPendingTimeSheetList = callForAPICallResult<List<ListData>>();
if (resultObjForPendingTimeSheetList != null)
{
TimesheetList.ItemsSource = resultObjForPendingTimeSheetList;
screenStackLayout.VerticalOptions = LayoutOptions.FillAndExpand;
TimesheetList.IsVisible = true;
}
else
{
}
}
catch (Exception)
{
}
});
}
async void Button_Tapped(object sender, EventArgs e)
{
try
{
// Calling my listview again. After calling app gets crash
Task.Run(async () => await loadTimeSheetList());
}
catch (Exception ex) { }
}
}
}
A few things before getting to the problem. You've got async/await all wrong, go though Async Programming
Task.Run runs the passed action on a different thread, if you make changes to UI elements on this thread, your app will definitely(take my word) crash.
If you want to make async call at page launch, make use of OnAppearing method (if you only want to call once, maintain a flag)
Do not change the ItemsSource of a list view frequently, just clear and add items to it.
namespace app
{
public partial class List : ContentPage
{
PendingWeekRange pendingWeekRange = new PendingWeekRange();
private ObservableCollection<ListData> TimesheetObservableCollection = new ObservableCollection<ListData>();
public TimeSheetList()
{
InitializeComponent();
TimesheetList.ItemsSource = TimesheetObservableCollection;
}
protected override async OnAppearing()
{
// flag for first launch?
await LoadScreenItems();
}
async Task LoadScreenItems()
{
try
{
// Doing some stuff
TimesheetObservableCollection.Clear();
TimesheetObservableCollection.AddRange(await GetTimeSheetList());
}
catch (Exception)
{
//handle exception
}
}
async Task<List<ListData>> GetTimeSheetList()
{
string postdataForPendingList = "{\"date\":\"" + "1" + "\"}";
APICall callForAPICallResult = new APICall("/API/ListMobile/ListForApproval", postdataForList, loadingIndicator);
try
{
return callForAPICallResult<List<ListData>>();
}
catch (Exception)
{
// handle exception
}
}
async void Button_Tapped(object sender, EventArgs e)
{
try
{
// Calling my listview again. After calling app gets crash
TimesheetObservableCollection.Clear();
TimesheetObservableCollection.AddRange(await GetTimeSheetList());
}
catch (Exception ex) { }
}
}
}
#Androdevil,
Update your loadTimeSheetList with this,
async Task loadTimeSheetList()
{
try
{
// I am calling my API for Listview here.
List<TimeSheetListData> resultObjForPendingTimeSheetList = await callForPendingTimeSheetList.APICallResult<List<TimeSheetListData>>();
if (resultObjForPendingTimeSheetList != null)
{
TimesheetList.ItemsSource = resultObjForPendingTimeSheetList;
screenStackLayout.VerticalOptions = LayoutOptions.FillAndExpand;
TimesheetList.IsVisible = true;
}
else
{
}
}
catch (Exception)
{
}
}

Mobile map package returns null for map

I downloaded Arcgis sample mmpk file and even I made a mmpk myself.
In both files I have 1 map(checked by debug) but when I try load the map (with codes in Esri guide page) it returns null for map.
Good to say that I can show online map in my map view and android studio shows no warning or error.
import static n.k.masoud.sbmap.R.id.mapView;
public class ActivityMain extends AppCompatActivity {
private MapView mMapView;
private ArcGISMap map;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapView = (MapView) findViewById(mapView);
code and file from main site
try {File mmpkFile = new File(Environment.getExternalStorageDirectory(),"devlabs-package.mmpk");
String mmpkPath = mmpkFile.getAbsolutePath();
final MobileMapPackage mobileMapPackage=new MobileMapPackage(mmpkPath);
mobileMapPackage.addDoneLoadingListener(new Runnable() {
#Override
public void run() {
this if gets false
if (mobileMapPackage.getLoadStatus() == LoadStatus.LOADED) {
showMessage(String.format("Number of maps = %d", mobileMapPackage.getMaps().size()));
map = mobileMapPackage.getMaps().get(0);
} else {
dealWithLoadFailure();
}
}
});
mobileMapPackage.loadAsync();
}
catch (Exception err){
Log.e("TAG", "onCreate: "+err);
}
map.addDoneLoadingListener(new Runnable() {
#Override
public void run() {
if (map.getLoadStatus() == LoadStatus.LOADED) {
Log.e("TAG", "run: map loaded ok" );
// Once map is loaded, can check its properties and content
if (map.getBookmarks().size() > 0) {
}
} else {
dealWithLoadFailure();
}
}
});
map.loadAsync();
As I told part below works correctly
// for online maps
// ArcGISMap map = new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 29.453826, 60.852134,12);
mMapView.setMap(map);
mMapView.addLayerViewStateChangedListener(new LayerViewStateChangedListener() {
#Override
public void layerViewStateChanged(LayerViewStateChangedEvent layerViewStateChangedEvent) {
// Each layer may have more than one layer view state.
StringBuilder layerStatuses = new StringBuilder();
for (LayerViewStatus status : layerViewStateChangedEvent.getLayerViewStatus()) {
if (layerStatuses.length() > 0) {
layerStatuses.append(",");
} layerStatuses.append(status.name());
}
showMessage(String.format("Layer '%s' status=%s", layerViewStateChangedEvent.getLayer().getName(), layerStatuses.toString()));
} });
}
#Override
protected void onPause(){
mMapView.pause();
super.onPause();
}
#Override
protected void onResume(){
super.onResume();
mMapView.resume();
}
}
if the line
if (mobileMapPackage.getLoadStatus() == LoadStatus.LOADED)
is returning false, then the mobile map package is not loaded and won't contain any maps.
In your dealWithLoadFailure() function you can retrieve the load error:
mobileMapPackage.getLoadError()
and see what it is. It should tell you what the error causing the load failure is.
One of my friends tried this way but didn't got any result just like me.
So he changed the official guide code to this and got well response.
I think he got code from Internet , so I don't know about it's copyright permission.
private void setupMobileMap() {
if (mMapView != null) {
File mmpkFile = new File(Environment.getExternalStorageDirectory(), "devlabs-package.mmpk");
final MobileMapPackage mapPackage = new MobileMapPackage(mmpkFile.getAbsolutePath());
mapPackage.addDoneLoadingListener(new Runnable() {
#Override
public void run() {
// Verify the file loaded and there is at least one map
if (mapPackage.getLoadStatus() == LoadStatus.LOADED && mapPackage.getMaps().size() > 0) {
mMapView.setMap(mapPackage.getMaps().get(0));
} else {
// Error if the mobile map package fails to load or there are no maps included in the package
//setupMap();
//Log for Error
}
}
});
mapPackage.loadAsync();
}
}

Spinner control value

I'm using Spinner from 8u40b17.
SpinnerValueFactory svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100);
Spinner sp = new Spinner();
sp.setValueFactory(svf);
sp.setEditable(true);
sp.setPrefWidth(80);
I noticed that when I enter some value from keyboard and I increase the upper value the expected number is not the next. Instead of this it's the next default value. How I can fix this?
For example: if I have 5 as default value and I enter 34, then press the upper arrow I expect to get 35 by actually get 6.
I had the same problem with the spinner control. Your bug has been documented here: JDK-8094205
Here is the last comment:
Jonathan Giles added a comment - Dec, 15 2014 12:59 AM
Fixed locally in my repo, will push to the 8u60 repo this week once it
opens. Now the text editor input is committed when increment /
decrement are called (although the value is still not committed when
focus is lost).
Unit tests:
javafx.scene.control.SpinnerTest.test_rt_39655_decrement()
javafx.scene.control.SpinnerTest.test_rt_39655_increment()
The changeset: http://hg.openjdk.java.net/openjfx/8u-dev/rt/rev/89ca7d3f699e
Here is my take on an Autocommit spinner. This one will auto commit anything that the factory will accept.
public class SpinnerAutoCommit<T> extends Spinner<T> {
public SpinnerAutoCommit() {
super();
addListenerKeyChange();
}
public SpinnerAutoCommit(int min, int max, int initialValue) {
super(min, max, initialValue);
addListenerKeyChange();
}
public SpinnerAutoCommit(int min, int max, int initialValue, int amountToStepBy) {
super(min, max, initialValue, amountToStepBy);
addListenerKeyChange();
}
public SpinnerAutoCommit(double min, double max, double initialValue) {
super(min, max, initialValue);
addListenerKeyChange();
}
public SpinnerAutoCommit(double min, double max, double initialValue, double amountToStepBy) {
super(min, max, initialValue, amountToStepBy);
addListenerKeyChange();
}
public SpinnerAutoCommit(ObservableList<T> items) {
super(items);
addListenerKeyChange();
}
public SpinnerAutoCommit(SpinnerValueFactory<T> valueFactory) {
super(valueFactory);
addListenerKeyChange();
}
private void addListenerKeyChange() {
getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
commitEditorText();
});
}
private void commitEditorText() {
if (!isEditable()) return;
String text = getEditor().getText();
SpinnerValueFactory<T> valueFactory = getValueFactory();
if (valueFactory != null) {
StringConverter<T> converter = valueFactory.getConverter();
if (converter != null) {
T value = converter.fromString(text);
valueFactory.setValue(value);
}
}
}
}
By design, the changes in the textfield of the Spinner control are commited only when the user hits ENTER key, via action handler:
getEditor().setOnAction(action -> {
String text = getEditor().getText();
SpinnerValueFactory<T> valueFactory = getValueFactory();
if (valueFactory != null) {
StringConverter<T> converter = valueFactory.getConverter();
if (converter != null) {
T value = converter.fromString(text);
valueFactory.setValue(value);
}
}
});
Note that if the typed value can't be converted, this will throw a NumberFormatException, keeping the wrong value in the textfield.
We can provide our own implementation, listening to other keys, like TAB key, via event filter, and at the same time, and in case of exception, restore the last valid value.
Something like this:
private final Spinner sp = new Spinner();
#Override
public void start(Stage primaryStage) {
SpinnerValueFactory svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(0, 100);
sp.setValueFactory(svf);
sp.setEditable(true);
sp.setPrefWidth(80);
// Commit on TAB
sp.addEventFilter(KeyEvent.ANY, e->{
if (sp.isEditable() && e.getCode().equals(KeyCode.TAB)) {
doCommit();
e.consume();
}
});
// Override Commit on ENTER
sp.getEditor().setOnAction(e->{
if(sp.isEditable()) {
doCommit();
e.consume();
}
});
Scene scene = new Scene(new StackPane(sp), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
/*
Commit new value, checking conversion to integer,
restoring old valid value in case of exception
*/
private void doCommit(){
String text = sp.getEditor().getText();
SpinnerValueFactory<Integer> valueFactory = sp.getValueFactory();
if (valueFactory != null) {
StringConverter<Integer> converter = valueFactory.getConverter();
if (converter != null) {
try{
Integer value = converter.fromString(text);
valueFactory.setValue(value);
} catch(NumberFormatException nfe){
sp.getEditor().setText(converter.toString(valueFactory.getValue()));
}
}
}
}
This solved the problem for me but it relys on Apache Commons Validator to validate entered value in the spinner (org.apache.commons.validator.GenericValidator)
valueSpinner.getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
try {
if (GenericValidator.isInt(newValue)) {
valueSpinner.getValueFactory().setValue(Integer.parseInt(newValue));
}
} catch (NumberFormatException e) {
if (GenericValidator.isInt(oldValue)) {
valueSpinner.getValueFactory().setValue(Integer.parseInt(oldValue));
}
}
});
Edit :-
You can validate the value without using Apache Commons Validator like this example :-
private boolean isInteger(String value) {
if (value == null) {
return false;
}
try {
new Integer(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
valueSpinner.getEditor().textProperty().addListener((observable, oldValue, newValue) -> {
try {
if (isInteger(newValue)) {
valueSpinner.getValueFactory().setValue(Integer.parseInt(newValue));
}
} catch (NumberFormatException e) {
if (isInteger(oldValue)) {
valueSpinner.getValueFactory().setValue(Integer.parseInt(oldValue));
}
}
});

Save selected items when using caliburn.micro /Telerik RadGridView /Silverlight

I am using Caliburn micro(1.3)/MVVM and Silverlight. When I update the itemsource RadGridView, I lose the selected items. I found a blog about implementing a behavior to save the selected items when you are implementing MVVM. I can get the selected items, but I cannot set them back once the itemsource is refreshed. Can someoneshow me how to implement this using caliburn.micro and the RadGridVIew? I think the best way to go is to create a caliburn micro convention, but I can only find a reference for creating a convention for selectedItem, not selectedItems.
Can someone show me how to accomplish this? I tried the following, but it does not work.
private static void SetRadGridSelecteditemsConventions()
{
ConventionManager
.AddElementConvention<DataControl>(DataControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged")
.ApplyBinding = (viewModelType, path, property, element, convention) =>
{
ConventionManager.SetBinding(viewModelType, path, property, element, convention, DataControl.ItemsSourceProperty);
if (ConventionManager.HasBinding(element, DataControl.SelectedItemProperty))
return true;
var index = path.LastIndexOf('.');
index = index == -1 ? 0 : index + 1;
var baseName = path.Substring(index);
foreach (var selectionPath in
from potentialName in ConventionManager.DerivePotentialSelectionNames(baseName)
where viewModelType.GetProperty(potentialName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null
select path.Replace(baseName, potentialName))
{
var binding = new Binding(selectionPath) { Mode = BindingMode.TwoWay };
BindingOperations.SetBinding(element, DataControl.SelectedItemProperty, binding);
}
return true;
};
}
Thanks,
Stephane
You should use a behavior for this since the SelectedItems property is readonly.
Telerik has an example for this, only the example is not specific for caliburn.micro.
If you add the following class to your project:
public class MultiSelectBehavior : Behavior<RadGridView>
{
public INotifyCollectionChanged SelectedItems
{
get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(INotifyCollectionChanged), typeof(MultiSelectBehavior), new PropertyMetadata(OnSelectedItemsPropertyChanged));
private static void OnSelectedItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
var collection = args.NewValue as INotifyCollectionChanged;
if (collection != null)
{
collection.CollectionChanged += ((MultiSelectBehavior)target).ContextSelectedItems_CollectionChanged;
}
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItems_CollectionChanged;
}
void ContextSelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UnsubscribeFromEvents();
Transfer(SelectedItems as IList, AssociatedObject.SelectedItems);
SubscribeToEvents();
}
void GridSelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
UnsubscribeFromEvents();
Transfer(AssociatedObject.SelectedItems, SelectedItems as IList);
SubscribeToEvents();
}
private void SubscribeToEvents()
{
AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItems_CollectionChanged;
if (SelectedItems != null)
{
SelectedItems.CollectionChanged += ContextSelectedItems_CollectionChanged;
}
}
private void UnsubscribeFromEvents()
{
AssociatedObject.SelectedItems.CollectionChanged -= GridSelectedItems_CollectionChanged;
if (SelectedItems != null)
{
SelectedItems.CollectionChanged -= ContextSelectedItems_CollectionChanged;
}
}
public static void Transfer(IList source, IList target)
{
if (source == null || target == null)
return;
target.Clear();
foreach (var o in source)
{
target.Add(o);
}
}
}
This behavior takes care of the synchronization between collection RadGridView.SelectedItems and MultiSelectBehavior.SelectedItems.
Now we need to have an ObservableCollection in the ViewModel
//Collection holding the selected items
private ObservableCollection<object> selectedGridItems;
public ObservableCollection<object> SelectedGridItems
{
get
{
if (selectedGridItems == null)
selectedGridItems = new ObservableCollection<object>();
return selectedGridItems;
}
set
{
if (selectedGridItems == value) return;
selectedGridItems = value;
NotifyOfPropertyChange(() => SelectedGridItems);
}
}
//Deselect all selected items in the gridview
public void ClearSelectedGridItems()
{
SelectedGridItems.Clear();
}
Last thing is bind the behavior in the view
<telerik:RadGridView x:Name="CustomLogs" AutoGenerateColumns="true" SelectionMode="Extended">
<i:Interaction.Behaviors>
<local:MultiSelectBehavior SelectedItems="{Binding SelectedGridItems}"/>
</i:Interaction.Behaviors>
</telerik:RadGridView>
Thats it, hope it helps you!

Resources