JavaFX: How to trigger TreeItem event - events

I have class myClass extends TreeItem<file> to be used as datamodel in a TreeTableView mostly following the example here: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeItem.html .
public class myTreeItem extends TreeItem<File>
private boolean isLeaf;
private boolean isFirstTimeChildren = true;
private boolean isFirstTimeLeaf = true;
#Override public ObservableList<TreeItem<File>> getChildren() {
// ... full code see link to Oracle documentation
return super.getChildren();
}
private ObservableList<TreeItem<File>> buildChildren(TreeItem<File> TreeItem) {
// ... full code see link to Oracle documentation
};
}
I have added a function to add children to this item. I have problems with the correct update of the TreeTableView. More details see in the code and comments below:
public void addChild(String name) {
itemManger.addChild(this.getValue(), name); // Generate Child
isFirstTimeChildren = true; // Ensure that buildChildren() is called, when getchildren() is called.
// getChildren(); // If I would activate this line,
// all listeners would be notified
// and the TreeTableView is updated.
// This is most likely due to the call super.getChildren();
// However I want to throw the event on my own in order
// to avoid the extra call of this.getChildren(). Here is my
// (not sufficent) try:
EventType<TreeItem.TreeModificationEvent<MLDCostumizableItem>> eventType = TreeItem.treeNotificationEvent();
TreeModificationEvent<MLDCostumizableItem> event = new TreeModificationEvent<>(eventType,this);
Event.fireEvent(this, event);
// Here I don't know how to get a value for target.
// Is there some standard target, which includes all FX components?
}
How the correctly throw this event?

Seems that I had a missunderstanding in how the triggering works in JavaFX. Now the most simple solution is:
#Override // Taken from Link
public void update(Observable observ, Object arg1) {
if (observ!=this.item)
{
LOGGER.error(new MLDConnectionException("Unexpected call of update() with observ = " + observ.toString()));
return;
}
// Build new Chidren list
try {
super.getChildren().removeIf((x) -> true); // empty list
super.getChildren().setAll(buildChildren(this));
} catch (MLDConnectionException e) {
LOGGER.error("Error when genereting children List: ", e);
}
}
public File addChild(String name) throws MLDException {
File newChild = itemManger.addChild(item, name);
update(this.item, null);
return newChild;
}

Related

Mocking a class with internal SDK calls using NSubstitute

First time trying to use NSubstitute.
I have the following method in my Web API.
For those who don't know Couchbase, lets say that a collection/bucket is like a DB table and a key is like a DB row.
Couchbase_internal.Collection_GET returns Task<ICouchbaseCollection>
I would like to write 2 unit tests.
One that tests the returned class when the key exist and one when it doesn't (couchbaseServiceResultClass).
I don't really understand where is the part where I control whether or not the key exist in the mocked data.
public class CouchbaseAPI : ControllerBase, ICouchbaseAPI
{
// GET /document_GET?bucketName=<bucketName>&key=<key>
[HttpGet]
[Consumes("application/x-www-form-urlencoded")]
[Produces(MediaTypeNames.Application.Json)]
public async Task<couchbaseServiceResultClass> document_GET([FromQuery, BindRequired] string bucketName, [FromQuery, BindRequired] string key)
{
var collection = await Couchbase_internal.Collection_GET(bucketName);
if (collection != null)
{
IGetResult result;
try
{
// get document
result = await collection.GetAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
couchbaseServiceResultClass decryptResult = new();
try
{
// decrypt document
decryptResult = Encryption.decryptContent(result);
}
catch (Exception ex)
{
return new ErrorHandling().handleException(ex, null);
}
// remove document if decryption failed
if (!decryptResult.DecryptSuccess)
{
try
{
await collection.RemoveAsync(key);
}
catch (CouchbaseException ex)
{
return new ErrorHandling().handleCouchbaseException(ex);
}
}
decryptResult.Message = "key retrieved successfully";
// return result
return decryptResult;
}
else
{
return new ErrorHandling().handleError("Collection / bucket was not found.");
}
}
This is what I have so far for the first test:
public class CouchbaseAPITests
{
private readonly CouchbaseAPI.Controllers.ICouchbaseAPI myClass = Substitute.For<CouchbaseAPI.Controllers.ICouchbaseAPI>();
[Fact]
public async Task document_GET_aKeyIsRetrievedSuccessfully()
{
// Arrange
string bucketName = "myBucket";
string keyName = "myKey";
couchbaseServiceResultClass resultClass = new();
resultClass.Success = true;
resultClass.Message = "key retrieved successfully";
myClass.document_GET(bucketName, keyName).Returns(resultClass);
// Act
var document = await myClass.document_GET(bucketName, keyName);
// Assert
Assert.True(document.Success);
Assert.Equal("key retrieved successfully", document.Message);
}
}
If we want to test that we are retrieving documents from the Couchbase API properly, then generally we want to use a real instance (local test setup) of that API where possible. If we are mocking this then our tests are not really telling us about whether our code is working correctly (just that our mock is working the way we want it to).
When certain APIs are difficult to use real instances for (e.g. non-deterministic code, difficult to reproduce conditions such as network errors, slow dependencies, etc), that's when it can be useful to introduce an interface for that dependency and to mock that for our test.
Here's a very rough example that doesn't quite match the code snippets posted, but hopefully will give you some ideas on how to proceed.
public interface IDataAdapter {
IEnumerable<IGetResult> Get(string key);
}
public class CouchbaseAdapter : IDataAdapter {
/* Implement interface for Couchbase */
}
public class AppApi {
private IDataAdapter data;
public AppApi(IDataAdapter data) {
this.data = data;
}
public SomeResult Lookup(string key) {
try {
var result = data.Get(key);
return Transform(Decrypt(result));
} catch (Exception ex) { /* error handling */ }
}
}
[Fact]
public void TestWhenKeyExists() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
testAdapter.Get("abc").Returns(/* some valid data */);
var result = api.Lookup("abc");
/* assert that result is decrypted/transformed as expected */
Assert.Equal(expectedResult, result);
}
[Fact]
public void TestWhenKeyDoesNotExist() {
var testAdapter = Substitute.For<IDataAdapter>();
var api = new AppApi(testAdapter);
var emptyData = new List<IGetResult>();
testAdapter.Get("abc").Returns(emptyData);
var result = api.Lookup("abc");
/* assert that result has handled error as expected */
Assert.Equal(expectedError, result);
}
Here we've introduced a IDataAdapter type that our class uses to abstract the details of which implementation we are using to get data. Our real code can use the CouchbaseAdapter implementation, but our tests can use a mocked version instead. For our tests, we can simulate what happens when the data adapter throws errors or returns specific information.
Note that we're only testing AppApi here -- we are not testing the CouchbaseAdapter implementation, only that AppApi will respond in a certain way if its IDataAdapter has certain behaviour. To test our CouchbaseAdapter we will want to use a real instance, but we don't have to worry about those details for testing our AppApi transformation and decryption code.

How can I await modal form dismissal using Xamarin.Forms?

Using Xamarin.Forms how can I use make an async method that waits for the form to dismiss? If I use
await Navigation.PushModalAsync(page);
it will return once the animation is finished not when the page is dismissed.
I want a to create modal Task SignInAsync method that return true if sign-in is successful.
You can do this by triggering an event in your login page and listen for that event before going on, but you want the full TAP support and I second you there. Here's a simple yet working 2 page app that does just this. You'll obviously want to use ContentPage custom subclass and have proper methods instead of my quick Commands, but you get the idea, and it saves me typing.
public static Page GetFormsApp ()
{
NavigationPage navpage = null;
return navpage = new NavigationPage (new ContentPage {
Content = new Button {
Text = "Show Login dialog",
Command = new Command (async o => {
Debug.WriteLine ("Showing sign in dialog");
var result = await SignInAsync (navpage);
Debug.WriteLine (result);
})
}
});
}
static Task<bool> SignInAsync (NavigationPage navpage)
{
Random rnd = new Random ();
var tcs = new TaskCompletionSource<bool> ();
navpage.Navigation.PushModalAsync (new ContentPage {
Content = new Button {
Text = "Try login",
Command = new Command ( o => {
var result = rnd.Next (2) == 1;
navpage.Navigation.PopModalAsync ();
tcs.SetResult (result);
})
}
});
return tcs.Task;
}
The minor drawback is that the Task<bool> returns before the end of the pop modal animation, but that's:
easy to fix
only an issue if you're awaiting that result to push a new modal Page. Otherwise, meh, just go on.
Override OnAppearing
Firstly, it's worth noting that simply overriding OnAppearing in the calling Page may suffice in many circumstances.
protected override void OnAppearing()
{
base.OnAppearing();
...
// Handle any change here from returning from a Pushed Page
}
(note that the pushed page's OnDisappearing override is called after the caller's OnAppearing - seems a bit backwards to me!)
AwaitableContentPage
Secondly...this is my take on #Chad Bonthuys answer:
public class AwaitableContentPage : ContentPage
{
// Use this to wait on the page to be finished with/closed/dismissed
public Task PageClosedTask { get { return tcs.Task; } }
private TaskCompletionSource<bool> tcs { get; set; }
public AwaitableContentPage()
{
tcs = new System.Threading.Tasks.TaskCompletionSource<bool>();
}
// Either override OnDisappearing
protected override void OnDisappearing()
{
base.OnDisappearing();
tcs.SetResult(true);
}
// Or provide your own PopAsync function so that when you decide to leave the page explicitly the TaskCompletion is triggered
public async Task PopAwaitableAsync()
{
await Navigation.PopAsync();
tcs.SetResult(true);
}
}
And then call it thus:
SettingsPage sp = new SettingsPage();
await Navigation.PushAsync(sp);
await sp.PageClosedTask; // Wait here until the SettingsPage is dismissed
Just thought I would contribute to this one, although it's been a while since it was asked and answered. I built upon the answer by #noelicus. I wanted a generic way to do this with multiple situations so the Task needs to be able to return not just bool but anything. Then, using generics:
public class AwaitableContentPage<T> : ContentPage
{
// Use this to wait on the page to be finished with/closed/dismissed
public Task<T> PageClosedTask => tcs.Task;
// Children classes should simply set this to the value being returned and pop async()
protected T PageResult { get; set; }
private TaskCompletionSource<T> tcs { get; set; }
public AwaitableContentPage()
{
tcs = new TaskCompletionSource<T>();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
tcs.SetResult(PageResult);
}
}
Now, in the page you want to run as modal, you can do:
public partial class NewPerson : AwaitableContentPage<Person>
and when done, simply do:
base.PageResult = newPerson; // object you created previously
await base.Navigation.PopAsync();
Then, to make it super simple to use, use an extension method:
public static class ExtensionMethods
{
async public static Task<T> GetResultFromModalPage<T>(this INavigation nav, AwaitableContentPage<T> page)
{
await nav.PushAsync(page);
return await page.PageClosedTask;
}
That's all. Now, in your code, in any page where you want to use this, the syntax ends up simply like this:
Person newPerson = await Navigation.GetResultFromModalPage<string>(new NewPersonCreatePage());
if (newPerson != null)
UseNewPersonCreatedByOtherPage();
Hope this helps!
In my implementation I used:
await navigation.PopModalAsync();
Full Example:
private INavigation navigation;
public LoginPageModel(INavigation navigation, LoginPage loginPage)
{
this.navigation = navigation;
this.loginPage = loginPage;
}
public bool IsValid { get; set; }
protected async void ExecuteLoginCommand()
{
var loginResult = await AuthenticationHelper.Authenticate(Email, Password);
var isValid = false;
if (loginResult != null)
{
isValid = true;
}
//return isValid;
AuthenticationResult(isValid);
}
private async void AuthenticationResult(bool isValid)
{
if (isValid)
{
Debug.WriteLine("Logged in");
await navigation.PopModalAsync();
}
else
{
Debug.WriteLine("Failed" + email + password);
await loginPage.DisplayAlert("Authentication Failed", "Incorrect email and password combination","Ok", null);
}
}
The answer selected and given by #Stephane Delcroix above is awesome. But for anybody willing to push this further, by waiting for a page's completion and returning more structured data in a good MVVM fashion, you could do the following:
By calling an event from the page's OnDisapearing method, this event can then be subscribed by the navigation service which you create, and you can then use the "TaskCompletionSource" to wati until your page finishes its work and then complete the task.
For more details about accomplishing this, you can check this blog post.
Here is the base page's implementation, every page in this demo app inherit this page:
public class BasePage<T> : ContentPage
{
public event Action<T> PageDisapearing;
protected T _navigationResut;
public BasePage()
{
}
protected override void OnDisappearing()
{
PageDisapearing?.Invoke(_navigationResut);
if (PageDisapearing != null)
{
foreach (var #delegate in PageDisapearing.GetInvocationList())
{
PageDisapearing -= #delegate as Action<T>;
}
}
base.OnDisappearing();
}
}
Here is an overview of the navigation service you should use:
public async Task<T> NavigateToModal<T>(string modalName)
{
var source = new TaskCompletionSource<T>();
if (modalName == nameof(NewItemPage))
{
var page = new NewItemPage();
page.PageDisapearing += (result) =>
{
var res = (T)Convert.ChangeType(result, typeof(T));
source.SetResult(res);
};
await App.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page));
}
return await source.Task;
}
To call this page with the navigation service, you can use the following code:
var item = await new SimpleNavigationService().NavigateToModal<Item>(nameof(NewItemPage));
Items.Add(item);

Fragment with update of detail layout

I have programmed an Android App with fragments. A ListView fragment and a detail fragment.
What I wanna do is, if someone clicks inside the detail activity, a layout which is "View.Gone" should be "View.Visible". The code works without errors but nothing changed on the screen.
You can see it in Detail fragment code where a clik event on the ImageButton btn is.
What do i wrong?
What is the best way to update the detail screen? If someone has a small example or could write me where in my code I have to change what, it makes me happy :-)
Thanks a lot
Tom
The FragmentActivity:
public class CacheFragment extends SherlockFragmentActivity {
CacheListFragment f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_cachelist);
f = new CacheListFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
f.setArguments(args);
}
Here's the Detail Fragment, where you can see whatt should happen if someone clicks on the Imagebutton:
public class CacheDetailsFragment extends SherlockFragment implements OnClickListener {
private CacheDetailsLoading cdLoad= new CacheDetailsLoading();
private static GeocacheDetails _cacheDetails = new GeocacheDetails();
private static GCRatingTyp _cacheVote = new GCRatingTyp();
private CacheDetailsUsing cdUsing = new CacheDetailsUsing();
private Activity _context;
private static CacheDetailsFragment f;
private View view;
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static CacheDetailsFragment newInstance(int index ) {
f = new CacheDetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
public void setCacheDetail(GeocacheDetails cacheDetails)
{
_cacheDetails = cacheDetails;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
return null;
}
Bundle bundle=getArguments();
_cacheVote= bundle.getParcelable("cacheVote");
int index = bundle.getInt("index");
_cacheDetails=StaticCacheListByGroup.getCacheList().get(index);
_context = getActivity();
_context.setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);
view = inflater.inflate(R.layout.list_cachedetails, container,false);
((RelativeLayout) view.findViewById(R.id.relativeLoggingInfo)).setVisibility(View.GONE);
((RelativeLayout) view.findViewById(R.id.relativeKategorienInfo)).setVisibility(View.GONE);
ImageButton btn = (ImageButton) view.findViewById(R.id.description_expand);
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
if(((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).getVisibility() == View.GONE)
{
((ImageButton) getActivity().findViewById(R.id.description_expand)).setBackgroundResource(R.drawable.navigation_collapse_dark);
((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).setVisibility(View.VISIBLE);
}
else
{
((ImageButton) getActivity().findViewById(R.id.description_expand)).setBackgroundResource(R.drawable.navigation_expand_dark);
((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).setVisibility(View.GONE);
}
});
return view;
}
}
Now the Listfragment:
public class CacheListFragment extends SherlockListFragment {
boolean isDualPane;
int mCurCheckPosition = 0;
private CacheListArrayAdapter _adapter;
private SharedPrefs _sp= new SharedPrefs();
private double latitude=0;
private double longitude=0;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
latitude =Double.parseDouble(_sp.getSharedPrefs(getActivity(), LibraryDefaults.PROGRAMMNAME, "Latitude", "0"));
longitude =Double.parseDouble(_sp.getSharedPrefs(getActivity(), LibraryDefaults.PROGRAMMNAME, "Longitude", "0"));
// Check to see if we have a frame in which to embed the details
// fragment directly in the containing UI.
View detailsFrame = getActivity().findViewById(R.id.details);
isDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if(!isDualPane)
{
Bundle bundle = getActivity().getIntent().getExtras();
if(bundle != null && bundle.containsKey("Titel"))
((TextView) getActivity().findViewById(R.id.listtitle)).setText(bundle.getString("Titel"));
else
((TextView) getActivity().findViewById(R.id.listtitle)).setText(this.getResources().getString(R.string.caches_listtitle));
}
if (StaticCacheListByGroup.getCacheList() != null)
{
GeocachingCompass gc = new GeocachingCompass(getActivity());
_adapter = new CacheListArrayAdapter(getActivity(), StaticCacheListByGroup.getCacheList(), longitude,latitude);
_adapter.setActualCoordinates(new LatLng(latitude,longitude));
_adapter.setActualHeading(gc.getBearing(latitude,longitude));
if (_adapter != null)
setListAdapter(_adapter);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
getListView().setSoundEffectsEnabled(true);
getListView().setSmoothScrollbarEnabled(true);
getListView().setDrawSelectorOnTop(false);
getListView().setCacheColorHint(R.color.transparentBlack);
getListView().setDivider(getResources().getDrawable( R.color.divider));
getListView().setDividerHeight(5);
if (isDualPane) {
// In dual-pane mode, the list view highlights the selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
showDetails(mCurCheckPosition);
}
}
}
#Override
public void onResume() {
super.onResume();
GeocachingCompass gc = new GeocachingCompass(getActivity());
_adapter = new CacheListArrayAdapter(getActivity(), StaticCacheListByGroup.getCacheList(), longitude,latitude);
_adapter.setActualCoordinates(new LatLng(latitude,longitude));
_adapter.setActualHeading(gc.getBearing(latitude,longitude));
if (_adapter != null)
setListAdapter(_adapter);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
showDetails(position);
}
/**
* Helper function to show the details of a selected item, either by
* displaying a fragment in-place in the current UI, or starting a
* whole new activity in which it is displayed.
*/
void showDetails(int index) {
mCurCheckPosition = index;
ReadGCVote getVote = new ReadGCVote();
GeocacheDetails cacheDetails = new GeocacheDetails();
cacheDetails=StaticCacheListByGroup.getCacheList().get(index);
if (isDualPane) {
// We can display everything in-place with fragments, so update
// the list to highlight the selected item and show the data.
getListView().setItemChecked(index, true);
// Check what fragment is currently shown, replace if needed.
CacheDetailsFragment details = (CacheDetailsFragment)
getActivity().getSupportFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
// Make new fragment to show this selection.
details = CacheDetailsFragment.newInstance(index);
// Execute a transaction, replacing any existing fragment
// with this one inside the frame.
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.details, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else {
// Otherwise we need to launch a new activity to display
Intent intent = new Intent();
intent.setClass(getActivity(), CacheDetailsActivity.class);
intent.putExtra("index", index);
intent.putExtra("cacheDetails",cacheDetails);
intent.putExtra("cacheVote",getVote.getGCVoteByCacheGuid( StaticGCVoteList.getCacheList(), cacheDetails.GetGUID()));
startActivity(intent);
}
}
}
I found the bug :-)
In the code snippet of the Detail Fragment ...
public void onClick(View v)
{
if(((RelativeLayout) getActivity().findViewById(R.id.relativeDescriptionInfo)).getVisibility() == View.GONE)
}
...you shouldn't use "getActivity()" use "view" from "view = inflater.inflate(R.layout.list_cachedetails, container,false);"
Then it will work

Updating a datatable in Wicket

We are developing a new application in Wicket and have run into a small problem.
What we do:
1) create a new SortableDataProvider
2) create a new DefaultDataTablePagingInBottom
3) create a new WebMarkupContainer
4) add the DefaultDataTablePagingInBottom to the WebMarkupContainer
5) create a new AjaxCheckBox
6) in the onUpdate of the AjaxCheckBox, add the WebMarkupContainer to the AjaxRequestTarget
7) set the SortableDataProvider to a new SortableDataProvider (with the updated query)
8) DefaultDataTablePagingInBottom.replaceWith(new DefaultDataTablePagingInBottom - with the new provider).
What happends:
1) Click the checkbox -> nothing happends.
2) Click it again -> crash: "Last cause: This method can only be called on a component that has already been added to its parent.
WicketMessage: Method onRequest of interface org.apache.wicket.behavior.IBehaviorListener targeted at org.apache.wicket.ajax.markup.html.form.AjaxCheckBox$1#1a2fefd on component [ [Component id = checkBox]] threw an exception"
3) Click back in the browser -> the list i filtered with the new provider.
Any ideas?
---EDIT---
Here's some code.
1) In the constructor of the WebPage:
model = new Model(projectPlannerService);
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isChecked);
table = new DefaultDataTablePagingInBottom("table", columns, provider, 50);
listContainer = new WebMarkupContainer("wmc");
listContainer.add(table);
add(listContainer.setOutputMarkupId(true));
/*
* checkbox för filtrering
*/
AjaxCheckBox checkBox = new AjaxCheckBox("checkBox", new Model()) {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(listContainer, "wmc");
isChecked = !isChecked;
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isChecked);
updateTable();
}
};
add(checkBox);
2) In updateTable():
table.replaceWith(new DefaultDataTablePagingInBottom("table", columns, provider, 50));
3) The SortableProjectDataProvider:
// Constructor
public SortableProjectDataProvider(IModel<?> model, WebSession webSession, boolean isChecked) {
this.model = model;
this.projectPlannerService = (ProjectPlannerService) model.getObject();
this.webSession = webSession;
setSort("customer", SortOrder.ASCENDING);
System.out.println("ischecked:" + isChecked);
if(!isChecked)
list = ((ProjectPlannerService) model.getObject()).findAllProjects();
else
list = ((ProjectPlannerService) model.getObject()).findAllActiveProjects();
System.out.println("size: " + list.size());
comparator = new ProjectComparator();
}
public Iterator<Project> iterator(int first, int count) {
Collections.sort(list, comparator);
if (first > list.size()) {
first = 0;
}
if (first + count > list.size()) {
return list.subList(first, list.size()).iterator();
} else {
return list.subList(first, first + count).iterator();
}
}
public IModel<Project> model(Project object) {
return new DetachableProjectModel((Project) object);
}
public int size() {
return list.size();
}
private class DetachableProjectModel extends LoadableDetachableModel {
private Long id;
#SpringBean
ProjectPlannerService projectPlannerService;
public DetachableProjectModel(Long id) {
Injector.get().inject(this);
if (id == null) {
throw new IllegalArgumentException();
}
this.id = id;
}
public DetachableProjectModel(Project project) {
this(project.getPk());
Injector.get().inject(this);
}
public int hashCode() {
return id.hashCode();
}
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj == null) {
return false;
} else if (obj instanceof DetachableProjectModel) {
DetachableProjectModel other = (DetachableProjectModel) obj;
return other.id == this.id;
}
return false;
}
protected Object load() {
return ((ProjectPlannerService) model.getObject()).findProjectById(id);
}
}
}
wicket:extend
-input wicket:id="checkBox" type="checkbox"- Show active -/input-
-div wicket:id="wmc"-
-table wicket:id="table"--/table-
-/div-
-/wicket:extend-
Thanks in advance!
/Andreas
By replacing the instance of your SortableProjectDataProvider with a new one you are making your life difficult. Instead of using the boolean isChecked in the constructor you could use an IModel<Boolean>. Assign the same instance of that model to your data provider and the check-box and you are done. No need to replace anything in onUpdate, add your listContainer to the AjaxRequestTarget and everything should just work...
e.g.
...
private IModel<Boolean> isCheckedModel = new Model<Boolean>(Boolean.FALSE);
...
provider = new SortableProjectDataProvider(model, (WebSession) getSession(), isCheckedModel);
...
AjaxCheckBox checkBox = new AjaxCheckBox("checkBox", isCheckedModel) {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(listContainer);
}
};
...
It is almost never a good idea to replace such things with new ones in Wicket. Encapsulate what changes in a model and change / replace the model's object. Every object that has a reference to that model can see the updated value or change it as needed.
Hope this helps.
Try this:
Wrong: target.add(listContainer, "wmc");
Right: target.add(listContainer);
Wrong; table.replaceWith(new DefaultDataTablePagingInBottom("table", columns, provider, 50));
Right: DefaultDataTablePagingInBottom tmp = new DefaultDataTablePagingInBottom("table", columns, provider, 50);
table.replaceWith(tmp);
table = tmp;
(You replace the DefaultDataTablePagingInBottom but not your reference.)
//olle

adding Double click event in CellTable cell - GWT

I am unable to figure out how could I add a double click event to the cell of the CellTable.
Is it possible with GWT CellTable or not?
Is there any workaround
thank you..
al
BTW, i saw this post but there is no reply...
http://www.devcomments.com/Adding-DoubleClicks-and-OnContextMenu-to-CellTable-at1066168.htm
I crafted something different that just fit my needs:
cellTable.addCellPreviewHandler(new Handler<TitoloProxy>() {
long lastClick=-1000;
#Override
public void onCellPreview(CellPreviewEvent<TitoloProxy> event) {
long clictAt = System.currentTimeMillis();
GWT.log("clickAt: "+(clictAt));
GWT.log("lastClick: "+(lastClick));
if(event.getNativeEvent().getType().contains("click")){
GWT.log(""+(clictAt-lastClick));
if(clictAt-lastClick < 300) { // dblclick on 2 clicks detected within 300 ms
Window.alert("I am a double click crafted event!");
}
lastClick = System.currentTimeMillis();
}
}
});
cellTable.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
Window.alert("That's it!");
}
}, DoubleClickEvent.getType());
Integer row=0;// to hold row index
Integer column=0;// to hold column index
_Grid.addCellPreviewHandler(new CellPreviewEvent.Handler<Model>() {
// this is to handle row id
#Override
public void onCellPreview(final CellPreviewEvent<Model> event) {
if (BrowserEvents.CLICK.equalsIgnoreCase(event.getNativeEvent().getType())) {
row = event.getIndex();
column=event.getColumn();
}
}
});
// because Doubleclick handler doesn't give row index or column index we will use addCellPreviewHandler to return row index or column index.
_Grid.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(final DoubleClickEvent event) {
System.out.println(" You clicked row = " + row);
System.out.println(" You clicked column = " + column);
}
}, DoubleClickEvent.getType());
For cell lists, this code works ok:
cellList.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
// do the stuff
}
}, DoubleClickEvent.getType());
I'm not sure about table cells
Because the CellPreview interface does not natively capture double click events you will need add event logic into the Overriden onCellPreview method. First you would think the best way would be to check the click time differences. However it is much more efficient and elegant to use a state machine and count clicks. This is more robust and allows you to deal with multiple event cases - Such as mouse hover, single, and double clicks. The code is pretty straightforward. So enjoy!
public class CellHoverHandler implements Handler<T> {
Timer singleClickTimer;
int clickCount = 0;
int clickDelay = 300;
#Override
public void onCellPreview(final CellPreviewEvent<T> event) {
if (Event.getTypeInt(event.getNativeEvent().getType()) == Event.ONMOUSEOVER) {
handleOnMouseOver(event);
} else if (Event.getTypeInt(event.getNativeEvent().getType()) == Event.ONCLICK) {
clickCount++;
if (clickCount == 1) {
singleClickTimer = new Timer() {
#Override
public void run() {
clickCount = 0;
handleOnClick(event);
}
};
singleClickTimer.schedule(clickDelay);
} else if (clickCount == 2) {
singleClickTimer.cancel();
clickCount = 0;
handleOnDblClick(event);
}
}
}
private void handleOnMouseOver(CellPreviewEvent<T> event) {
Element cell = event.getNativeEvent().getEventTarget().cast();
GWT.log("mouse over event");
}
private void handleOnClick(CellPreviewEvent<T> event) {
Element cell = event.getNativeEvent().getEventTarget().cast();
GWT.log("click event");
}
private void handleOnDblClick(CellPreviewEvent<T> event) {
Element cell = event.getNativeEvent().getEventTarget().cast();
GWT.log("double click event");
}
OPTIMIZATION: feel free to stick the count, timer, and delay as static class members or global members to reuse. Also check to see if the timer is null before making a new instance. I had omitted this for simplicity. Unlike a lot of other techniques this way still provides you with easy and direct access to the cell event. The technique with overloading the AbstractCell works well too, however sometimes you really don't have custom cells or want to make a custom cell to just handle events on the cell.
Leaving this here for future reference
private Set<GenericEventHandler<T>> dblClickHandlers = new HashSet<>(4);
dblClickHandlers simply maps interface implementations of my choice
table.addCellPreviewHandler(event -> {
if (BrowserEvents.DBLCLICK.equalsIgnoreCase(event.getNativeEvent().getType())) {
LOGGER.info("dblclick (native) " + event.getIndex() + " " + event.getColumn() + "; " + event.getValue());
dblClickHandlers.forEach(handler -> {
handler.onEvent(event.getValue());
});
}
});
table.sinkBitlessEvent(BrowserEvents.DBLCLICK);
The trick is to sink the 'dblclick' event.
If you wanted a text cell that allows you to support your own chosen list of events, you can use this:
public class EventfulTextCell extends AbstractSafeHtmlCell`<String`> {
private static final String[] NO_CONSUMED_EVENTS = null;
public EventfulTextCell() {
this(NO_CONSUMED_EVENTS);
}
public EventfulTextCell(String... consumedEvents) {
super(SimpleSafeHtmlRenderer.getInstance(), consumedEvents);
}
#Override
public void render(Context context, SafeHtml value, SafeHtmlBuilder sb) {
if (value != null) {
sb.append(value);
}
}
}
Then you instantiate it:
new EventfulTextCell("click", "dblclick")
Then override the onBrowserEvent() method to process your events.

Resources