Javafx validate CheckBoxTableCell when check a checkbox - javafx

I follow this example to use CheckBoxTableCell in TableView:
JavaFX: CheckBoxTableCell get ActionEvent when user check a checkBox
I have table in my project like this:
I tried this code to validate (in the same row) if primary checkbox or secondary checkbox is selected, the other one unselect. But code doesnt work:
primaryTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(i -> {
Diet diet = diets.get(i);
if (diet.isPrimary()) {
diet.setSecondary(false);
}
return diet.primaryProperty();
}));
secondaryTableColumn.setCellFactory(CheckBoxTableCell.forTableColumn(i -> {
Diet diet = diets.get(i);
if (diet.isSecondary()) {
diet.setPrimary(false);
}
return diet.secondaryProperty();
}));
How can i code in order to uncheck one checkbox if other one in same row is checked (preferably using bindings)? thanks and sorry for my english

Related

allowsSelectionDuringEditing property for NSTableView

Application I'm trying to develop is heavily built around being able to have editable TableViews. I'm beginning to conclude that most if not all of my problems stem from the fact that I do not see allowsSelectionDuringEditing available for NSTableView as it is for UITableView.
First, would like to get insight into why.
Second, how do I go about implementing one in my NSTableView class?
Finally, I have a NSSegmentedControl in one of my columns. So I need to implement allowSelectiongWhenFocused property somewhere, because while I have focus on the button and allowing user to use <- and -> with spacebar to select switch(es), I don't want mouse / keyboard from changing the selected row.
As an aside, while I now know how to write custom UI classes and hook them into Interface Builder, I'm struggling on when/whether to customize NSTableView, NSTableRowView, NSTableCellView or NSSegmentedControl. I've tried to understand how refuseFirstResponder works as well. Trial and error is not getting me anywhere - I fix something and break something somwehre else. If someone can suggest any other reading besides Apple documentation (sometimes I suspect if it is in English) would really appreciate it.
Here's what I would do:
Subclass NSTableRowView and override hitTest(_:). If the row is not selected then the row view returns itself instead of a control. The first click in a row selects the row and doesn't change the value of a control.
override func hitTest(_ point: NSPoint) -> NSView? {
let view = super.hitTest(point)
if view == nil || self.isSelected {
return view
}
else {
return self
}
}
Implement NSTableViewDelegate's tableView(_:rowViewForRow:) to use the custom row view:
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
return MyTableRowView()
}
Implement NSTableViewDelegate's selectionShouldChange(in:) so a row can't be deselected if the values aren't valid or other conditions aren't met.
func selectionShouldChange(in tableView: NSTableView) -> Bool {
let row = tableView.selectedRow
if (row >= 0) {
return self.validateRow(row)
}
else {
return true
}
}

tornadofx EventBus expand table row using tableview object

Background:
Suppose I have multiple fragments of a single table in a view, each with a rowExpander.
Expected Behaviour:
If in one table fragment I expand a row, other fragments same indexed row should get expanded. Same for collapse
My Progress:
Sample Fragment:
tableview(dataset) {
column("First Name", Person::firstNameProperty)
column("Last Name", Person::lastNameProperty)
rowExpander(true) {
selectedData.item?.apply {
fire(ExpandDataEvent(dataset.indexOf(this)))
}
column("Mobile Nos.", Person::mobileNumProperty)
column("Email Ids", Person::emailIdProperty)
}
bindSelected(selectedData)
subscribe<ExpandDataEvent> { event ->
selectionModel.select(event.index)
}
}
Event Class:
class ExpandDataEvent(val index: Int) : FXEvent()
What I understand from "subscribe" is that it gets called when an event is fired (currently I am firing the event whenever the user expands the row by either double-click/clicking the plus sign); and since the subscribe is placed inside tableview, it gets called for all the table fragments present (which is what I want). But in the subscribe method I am doing a selectionModel.select(event.index) which only selects the corresponding index row. I want to expand the row (preferably by using the selectionModel)
Question 2:
Is it possible to remove the plus sign column? For rowExpand, if I have set expandOnDoubleClick to true, I dont want the plus sign column in my tableview.
The rowExpander builder returns the actual ExpanderColumn which is basically a normal TableColumn. Save a reference to the expander so that you can operate on it later:
val expander = rowExpander(true) { ... }
Directly below, you can now hide the expander column:
expander.isVisible = false
Now it's easy to toggle the expanded state of a specific row from the event subscriber as well:
subscribe<ExpandDataEvent> { event ->
expander.toggleExpanded(event.index)
selectionModel.select(event.index)
}
You might want to double check that you don't toggle the expander for the tableview that fired the event, so consider including the event source in your event and discriminate on that in the subscriber.
I will investigate if we can add a visible boolean parameter to the rowExpander builder function so you don't need to call isVisible manually :)

Javafx tableview row contextmenu bindings

Per this link, I have added context menu to my tableview using the following pattern -
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
I have a requirement where the multiple rows can be selected (SelectionMode.MULTIPLE) and I need to display the context menu when a row property matches a specific value. I have tried this but no luck -
// Show menu only if row is not null and value property == "foo"
row.contextMenuProperty().bind(
Bindings.when(Bindings.and(Bindings.isNotNull(row.itemProperty()), Bindings.equal("foo", row.getItem().value))
.then(rowMenu)
.otherwise((ContextMenu)null));
UPDATE
I got this working via a listener -
row.emptyProperty().addListener((obs, wasEmpty, isEmpty) -> {
if (isEmpty) {
row.setContextMenu(null);
} else {
if (row.getItem().name.get().equals("foo")) {
row.setContextMenu(contextMenu);
}
}
});
Question - Is there a way to achieve this using the Bindings API?
Your binding does this:
Bindings.equal("foo", row.getItem().value)
But your listener does this:
row.getItem().name.get().equals("foo")
So perhaps, using this in the binding would work:
Bindings.equal("foo", row.getItem().name.get())

show different item on selectionchange on a grid

i have a grid and a form, i need to show different items on the form each time we select a row on that grid
i ve been looking on how to do this, and found
Ext.getCmp('myform').hide() // or .show()
and
listeners: { selectionchange: function () {...}
now i dont know which row is selected so i can specify which item to show
thanks
You get the selected rows as second parameter in the selectionchange event handler:
listeners: {
selectionchange: function (view, selections, options) {
console.log(view, selections, options);
}
}
So the first selected row is the first element in the selections array:
record = selections[0]
This is described in the Ext JS 4 API documentation for the selectionchange event.
Try to following code in your grid.
listeners:{
itemclick:function(view, record, item, index, e ) {
var v = record.get('firstName');
....
....
}
}
firstName will be your dataindex of colums in your grid.
You can get value of any field like this.

DevExpress XtraGrid FocusedRowChanged event problem when changing datasource

This problem has bugged me for several years and maybe someone here knows a simple solution, since I just ran into it again.
QUESTION: Is there any way to get the XtraGrid to "forget" the current focused row index before a new (different) datasource is assigned to the grid?
BACKGROUND
We use the XtraGrid as a kind of controller for what is displayed in another panel of a multipane Winform.
Now imagine a hypothetical scenario where the datasource of the XtraGrid keeps changing according to menu selections. Menu item 1 populates the grid with a list of today's entrees in the cafeteria: Id, Name. Menu item 2 populates the grid with a list of Customers the user must phone that day: ID, Name. Important thing is that these are separate distinct datasources, and the grid's datasource is being assigned and reassigned.
CRITICAL FACT FOR THIS QUESTION:
We want the grid's FocusedRowChanged event to be the single place where we trap the user's selection in the controller grid. We are a "no spaghetti code" shop. FocusedRowChanged is better than a click event because it handles keyboard navigation too. The row with the focus contains the ID of the detail record we need to fetch from the database for display in Panel #2. This works--most of the time.
Here's how it doesn't work: let's say that on a given day, the list of customers the user must contact contains only one row. So the first (and only) row in the grid is the focused row. Now let's say that the user goes up to the menu and selects the menu item to display the day's cafeteria entrees. When the user clicks on the first item in the Entrees list, the FocusedRowChanged event does NOT fire because the grid has retained a memory of the focused row index from the previous datasource. The focused row index has not changed. And thus the user's selection doesn't trigger anything.
I tried to get DevExpress to offer a second more row-object-oriented mode (as distinct from row-index-oriented approach) whereby each row in the grid would have a GUID, and the FocusedRowChanged event would fire whenever the GUID of the currently focused row differed from the GUID of the previously focused row, regardless of whether the focused row index happened to be the same. This would allow dynamic changes of datasource and enable the desired behavior. But they demurred.
So I'll ask my question again, Is there any way to get the XtraGrid to "forget" the current focused row index before a new datasource is assigned to the grid?
Tim, I had the exact same problem when the grid only had one row of data in it and then changed data sources. I solved it by setting the gridview.FocusedRowHandle = -1 after setting the new datasource.
In a similar situation, I am subscribing to the
FocusedRowObjectChanged
event (using DevExpress 16.1).
I think that the best solution to this problem is to create a new GridView object and override its DoChangeFocusedRowInternal method. Below you will find the default implementation of this method. All you need to do is to change the marked row just as your needs dictate. Also, take a look at the How to create a GridView descendant class and register it for design-time use article, it contains some useful information.
public class MyGridView : GridView {
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(FocusedRowHandle == newRowHandle) return; // <<<<<<
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
UPDATE
My code:
namespace MyXtraGrid {
public class MyGridControl : GridControl {
protected override BaseView CreateDefaultView() {
return CreateView("MyGridView");
}
protected override void RegisterAvailableViewsCore(InfoCollection collection) {
base.RegisterAvailableViewsCore(collection);
collection.Add(new MyGridViewInfoRegistrator());
}
}
public class MyGridViewInfoRegistrator : GridInfoRegistrator {
public override string ViewName { get { return "MyGridView"; } }
public override BaseView CreateView(GridControl grid) {
return new MyGridView(grid as GridControl);
}
}
public class MyGridView : GridView {
public MyGridView(GridControl ownerGrid) : base(ownerGrid) { }
public MyGridView() { }
protected virtual bool RowEqual(int focusedRowHandle, int newRowHandle) {
if(IsDesignMode)
return focusedRowHandle == newRowHandle;
DataRow row1 = GetDataRow(focusedRowHandle);
DataRow row2 = GetDataRow(newRowHandle);
return row1 == row2;
}
protected override void DoChangeFocusedRowInternal(int newRowHandle, bool updateCurrentRow) {
if(this.lockFocusedRowChange != 0) return;
if(!IsValidRowHandle(newRowHandle))
newRowHandle = DevExpress.Data.DataController.InvalidRow;
if(RowEqual(FocusedRowHandle, newRowHandle))
return;
int currentRowHandle = FocusedRowHandle;
BeginLockFocusedRowChange();
try {
DoChangeFocusedRow(FocusedRowHandle, newRowHandle, updateCurrentRow);
}
finally {
EndLockFocusedRowChange();
}
RaiseFocusedRowChanged(currentRowHandle, newRowHandle);
}
}
}
You can subscribe on the DataSourceChanged event which will fire when Data source changes (you guessed it!) so then you can get using GetFocusedObject() the object and display the relevant items for the other grid...

Resources