When scrolling down, Groove moves the header up, outside of the viewable area just like a regular ListView header. When scrolling back up it moves the header back down into the viewable area right away, regardless of the current vertical scroll offset. The header seems to be part of the ListView content because the scrollbar includes the header.
How can this be implemented in a Windows 10 UWP app?
You can do this by utilizing the ListView's internal ScrollViewer's ViewChanged event.
First you got to obtain the internal ScrollViewer. This is the simplest version, but you might want to use one of the many VisualTreeHelper Extensions around to do it safer and easier:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var border = VisualTreeHelper.GetChild(MyListView, 0);
var scrollviewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
scrollviewer.ViewChanged += Scrollviewer_ViewChanged;
}
In the EventHandler, you can then change the visibility of your header depending on the scroll direction.
private void Scrollviewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
var sv = sender as ScrollViewer;
if (sv.VerticalOffset > _lastVerticalOffset)
{
MyHeader.Visibility = Visibility.Collapsed;
}
else
{
MyHeader.Visibility = Visibility.Visible;
}
}
This is the basic idea. You might wan't to add some smooth animations instead of just changing the visibility.
After looking around a bit and experimentation I can now answer my own question.
One can use an expression based composition animation to adjust the Y offset of the the header in relation to scrolling. The idea is based on this answer. I prepared a complete working example on GitHub.
The animation is prepared in the SizeChanged event of the ListView:
ScrollViewer scrollViewer = null;
private double previousVerticalScrollOffset = 0.0;
private CompositionPropertySet scrollProperties;
private CompositionPropertySet animationProperties;
SizeChanged += (sender, args) =>
{
if (scrollProperties == null)
scrollProperties = ElementCompositionPreview.GetScrollViewerManipulationPropertySet(scrollViewer);
var compositor = scrollProperties.Compositor;
if (animationProperties == null)
{
animationProperties = compositor.CreatePropertySet();
animationProperties.InsertScalar("OffsetY", 0.0f);
}
var expressionAnimation = compositor.CreateExpressionAnimation("animationProperties.OffsetY - ScrollingProperties.Translation.Y");
expressionAnimation.SetReferenceParameter("ScrollingProperties", scrollProperties);
expressionAnimation.SetReferenceParameter("animationProperties", animationProperties);
var headerVisual = ElementCompositionPreview.GetElementVisual((UIElement)Header);
headerVisual.StartAnimation("Offset.Y", expressionAnimation);
};
The OffsetY variable in the animationProperties will drive the animation of the OffsetY property of the header. The OffsetY variable is updated in the ViewChanged event of the ScrollViewer:
scrollViewer.ViewChanged += (sender, args) =>
{
float oldOffsetY = 0.0f;
animationProperties.TryGetScalar("OffsetY", out oldOffsetY);
var delta = scrollViewer.VerticalOffset - previousVerticalScrollOffset;
previousVerticalScrollOffset = scrollViewer.VerticalOffset;
var newOffsetY = oldOffsetY - (float)delta;
// Keep values within negativ header size and 0
FrameworkElement header = (FrameworkElement)Header;
newOffsetY = Math.Max((float)-header.ActualHeight, newOffsetY);
newOffsetY = Math.Min(0, newOffsetY);
if (oldOffsetY != newOffsetY)
animationProperties.InsertScalar("OffsetY", newOffsetY);
};
While this does animate correctly, the header is not stacked on top of the ListView items. Therefore the final piece to the puzzle is to decrease the ZIndex of the ItemsPanelTemplate of the ListView:
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Canvas.ZIndex="-1" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Which gives this as a result:
I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.
In form load i'm updating Drop Down List Value
ddlFont:
foreach (FontFamily font in System.Drawing.FontFamily.Families)
{
ddlFont.Items.Add(font.Name);
}
ddlFontSize:
for (int i = 8; i < 24; i++)
{
ddlFontSize.Items.Add(i.ToString().Trim());
}
ddlFontStyle:
ddlFontStyle.Items.Add(System.Drawing.FontStyle.Bold.ToString());
ddlFontStyle.Items.Add(System.Drawing.FontStyle.Italic.ToString());
ddlFontStyle.Items.Add(System.Drawing.FontStyle.Regular.ToString());
ddlFontColor:
ddlColor.Items.Add(System.Drawing.Color.Black.Name.ToString());
ddlColor.Items.Add(System.Drawing.Color.Blue.Name.ToString());
ddlColor.Items.Add(System.Drawing.Color.Green.Name.ToString());
ddlColor.Items.Add(System.Drawing.Color.Red.Name.ToString());
ddlColor.Items.Add(System.Drawing.Color.White.Name.ToString());
ddlColor.Items.Add(System.Drawing.Color.Yellow.Name.ToString());
If user change the Font, size, style, color then i must change RichTextBox control text font, size, style, color.
I call the "FontFormation" method from SelectedIndexChanged event of Drop Down List controls which is having Font Name ddl, Font Style ddl, Font Size ddl.
private void ddlFont_SelectedIndexChanged(object sender, EventArgs e)
{
FontFormation();
}
In my code first two conditions are executing without error but last one alone showing "Input string was not in a correct format" error at ddlFont.Text.
public void FontFormation()
{
if (FontStyle.Bold.ToString() == ddlFontStyle.Text)
{
rchtxtMainBody.Font = new System.Drawing.Font(ddlFont.Text, Convert.ToUInt32(ddlFontSize.Text), FontStyle.Bold);
}
else if(FontStyle.Italic.ToString() == ddlFontStyle.Text)
{
rchtxtMainBody.Font = new System.Drawing.Font(ddlFont.Text, Convert.ToUInt32(ddlFontSize.Text), FontStyle.Italic);
}
else if (FontStyle.Regular.ToString() == ddlFontStyle.Text)
{
rchtxtMainBody.Font = new System.Drawing.Font(ddlFont.Text, Convert.ToUInt32(ddlFontSize.Text), FontStyle.Regular);
}
}
If i remove my last else if condition i.e FontStyle.Regular.ToString() == ddlFontStyle.Text then code is getting executed without any error.
The problem lies in your ddlFontSize.Text. The exception occurs when it cannot convert in to UInt32. Please debug your code and make sure that ddlFontSize.Text doesn't have "px" or "pt" with and and its only a number in string format.
I wrote a method which changes backcolor of the rows before painting gridview in devexpress. It works fine but I realized that my code begins slowing down. Then I've found that the event firing continuously. It never stops. How can I handle this? Is there any way to stop firing event manually after gridview painted or should I try to solve this problem with an another event or another method???
Here is my event:
private void gvStep_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
try
{
DataRowView drw = (DataRowView)gvStep.GetRow(e.RowHandle);
byte actionTypeID = (byte)drw.Row["ActionType"];
//string colorCode = (new DivaDs()).GetBackColor(actionTypeID);
string colorCode = divaDs.GetBackColor(actionTypeID);
Color backColor = ColorTranslator.FromHtml(colorCode);
e.Appearance.BackColor = backColor;
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
public string GetBackColor(byte actionTypeID)
{
string color = string.Empty;
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[DivaSqlSiteConnString].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand(#"Select BackColor from ActionTypes where ID = #actionTypeID"))
{
SqlParameter param = new SqlParameter("#actionTypeID", actionTypeID);
cmd.Parameters.Add(param);
cmd.Connection = conn;
conn.Open();
color = cmd.ExecuteScalar().ToString();
conn.Close();
}
}
return color;
}
My best guess is that some part of your code is just really slow.
The event only fires for each visible cell in the grid. If you attempt to debug the event, focus will shift to the debugger, and when you return to the application the cells need to be redrawn, causing the event to fire again, thus giving the impression that the event fires continuously. It does not, however.
Here are some pointers to improve performance:
You are constructing a new DivaDs every time the event fires
Instead, consider reusing the same instance of the class as a member variable
What happens in the constructor?
Take a closer look at the GetBackColor method or ColorTranslator.FromHtml and see if any modifications can be made to improve performance.
Update
It appears you are querying the database for each cell in the grid. This is a really bad idea.
A simple solution would be to preload all ActionTypes and their background colors (or at least the subset of ActionTypes that is displayed in the grid) before setting the grid's data source.
// member variable
private Dictionary<byte, Color> actionTypeColorDict;
void BuildActionTypeColorDictionary()
{
string connectionString = ConfigurationManager
.ConnectionStrings[DivaSqlSiteConnString].ConnectionString;
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = conn.CreateCommand())
using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
{
// load all action type IDs and corresponding background color:
cmd.CommandText = #"SELECT ActionTypeID, BackColor FROM ActionTypes";
DataTable actionTypeTable = new DataTable();
adapter.Fill(actionTypeTable);
// build a dictionary consisting of action type IDs
// and their corresponding colors
actionTypeColorDict = actionTypeTable.AsEnumerable().ToDictionary(
r => r.Field<byte>("ActionTypeID"),
r => ColorTranslator.FromHtml(r.Field<string>("ColorCode")));
}
}
Call the BuildActionTypeColorDictionary method before setting the data source of the grid. In the RowStyle or CustomDrawCell events, use the new dictionary member to determine the background color. See the following modified version of your RowStyle code:
private void gvStep_RowStyle(object sender,DevExpress.XtraGrid.Views.Grid.RowStyleEventArgs e)
{
try
{
DataRow row = gvStep.GetDataRow(e.RowHandle);
if (row == null)
return;
byte actionTypeID = row.Field<byte>("ActionImage");
// look up color in the dictionary:
e.Appearance.BackColor = actionTypeColorDict[actionTypeID];
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
How do you know it's firing continuously? Are you debbuging?
This code runs whenever the grid is redrawn, meaning whenever the form gets focus.
This event runs for each cell - so it will run quite a few times.
If you put a break-point in this event you'll never get out of it. It will break, you will debug, when it's done it will return focus to the form - causing the form to be redrawn using this event and the break-point is reached again.
And just a side note - Whenever I use that event I have to put e.Handled = true; in the code so that the cell isn't "drawn" by anyone but me :)
Finally, I found it. RowStyle event only fires same time with gridview's row count
private void gvStep_RowStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowStyleEventArgs e)
{
try
{
DataRowView drw = (DataRowView)gridView1.GetRow(e.RowHandle);
if (drw == null)
return;
byte actionTypeID = (byte)drw.Row["ActionImage"];
string colorCode = divaDs.GetBackColor(actionTypeID);
Color backColor = ColorTranslator.FromHtml(colorCode);
e.Appearance.BackColor = backColor;
}
catch (Exception ex)
{
XtraMessageBox.Show(ex.Message);
}
}
afaik The TableView in javafx have 2 column resize policies: CONSTRAINED_RESIZE_POLICY and UNCONSTRAINED_RESIZE_POLICY, but I want columns is resized to fit the content of theirs cells
I think it's a simple problem in other platform (like datagridview in C#) but can not resolve
After 3 years I come back to this problem again, some suggestions are calculating the size of text of data in each cell (it's complicated depending on font size, font family, padding...)
But I realize that when I click on the divider on table header, it's resized fit to content as I want. So I dig into JavaFX source code I finally found resizeColumnToFitContent method in TableViewSkin, but it is protected method, we can resolve by reflection:
import com.sun.javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.Skin;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GUIUtils {
private static Method columnToFitMethod;
static {
try {
columnToFitMethod = TableViewSkin.class.getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
columnToFitMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public static void autoFitTable(TableView tableView) {
tableView.getItems().addListener(new ListChangeListener<Object>() {
#Override
public void onChanged(Change<?> c) {
for (Object column : tableView.getColumns()) {
try {
columnToFitMethod.invoke(tableView.getSkin(), column, -1);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
});
}
}
Note that we call "tableView.getItems()" so we have to call this function after setItems()
After testing the previous solutions I finally found one that worked for me.
So here is mine (call the method after inserting the data into table):
public static void autoResizeColumns( TableView<?> table )
{
//Set the right policy
table.setColumnResizePolicy( TableView.UNCONSTRAINED_RESIZE_POLICY);
table.getColumns().stream().forEach( (column) ->
{
//Minimal width = columnheader
Text t = new Text( column.getText() );
double max = t.getLayoutBounds().getWidth();
for ( int i = 0; i < table.getItems().size(); i++ )
{
//cell must not be empty
if ( column.getCellData( i ) != null )
{
t = new Text( column.getCellData( i ).toString() );
double calcwidth = t.getLayoutBounds().getWidth();
//remember new max-width
if ( calcwidth > max )
{
max = calcwidth;
}
}
}
//set the new max-widht with some extra space
column.setPrefWidth( max + 10.0d );
} );
}
I think just by overriding a call back function that returns true will solve your problem it will disable the re-sizing of columns and all columns will be re-sized to fit the content of their cells.
Example:
TableView<String[]> table = new TableView<>();
table.setColumnResizePolicy(new Callback<TableView.ResizeFeatures, Boolean>() {
#Override
public Boolean call(ResizeFeatures p) {
return true;
}
});
If you want that only one column fills the remaining width of a table, I have found a pretty straight forward solution, which is short and does not require the hacky reflection solution described above:
DoubleBinding usedWidth = columnA.widthProperty().add(columnB.widthProperty()).add(columnC.widthProperty());
fillingColumn.prefWidthProperty().bind(tableView.widthProperty().subtract(usedWidth));
Or to make it short:
// automatically adjust width of columns depending on their content
configAttributeTreeTable.setColumnResizePolicy((param) -> true );
I have used the other solutions on this question, and it works pretty good. However, the downside of this is when the width of the TableView is greater than the required width of the TableColumns together. I have created a hack to solve this problem, and it works OK:
orderOverview.setColumnResizePolicy((param) -> true );
Platform.runLater(() -> FXUtils.customResize(orderOverview));
where FXUtils.customResize() is created as follows:
public static void customResize(TableView<?> view) {
AtomicDouble width = new AtomicDouble();
view.getColumns().forEach(col -> {
width.addAndGet(col.getWidth());
});
double tableWidth = view.getWidth();
if (tableWidth > width.get()) {
TableColumn<?, ?> col = view.getColumns().get(view.getColumns().size()-1);
col.setPrefWidth(col.getWidth()+(tableWidth-width.get()));
}
}
I hope this could be helpful for other people as well!
This is the way I found :
tableview.setColumnResizePolicy( TableView.CONSTRAINED_RESIZE_POLICY );
idCol.setMaxWidth( 1f * Integer.MAX_VALUE * 50 ); // 50% width
nameCol.setMaxWidth( 1f * Integer.MAX_VALUE * 30 ); // 30% width
ageCol.setMaxWidth( 1f * Integer.MAX_VALUE * 20 ); // 20% width
This code autoresizes all column widths in relational proportions to the table width,
while it can fix the first column width to a given value when table width is lower than x
// To generalize the columns width proportions in relation to the table width,
// you do not need to put pixel related values, you can use small float numbers if you wish,
// because it's the relative proportion of each columns width what matters here:
final float[] widths = { 1.2f, 2f, 0.8f };// define the relational width of each column
// whether the first column should be fixed
final boolean fixFirstColumm = true;
// fix the first column width when table width is lower than:
final float fixOnTableWidth = 360; //pixels
// calulates sum of widths
float sum = 0;
for (double i : widths) {
sum += i;
}
// calculates the fraction of the first column proportion in relation to the sum of all column proportions
float firstColumnProportion = widths[0] / sum;
// calculate the fitting fix width for the first column, you can change it by your needs, but it jumps to this width
final float firstColumnFixSize = fixOnTableWidth * firstColumnProportion;
// set the width to the columns
for (int i = 0; i < widths.length; i++) {
table.getColumns().get(i).prefWidthProperty().bind(table.widthProperty().multiply((widths[i] / sum)));
// ---------The exact width-------------^-------------^
if (fixFirstColumm)
if (i == 0) {
table.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> arg0, Number oldTableWidth, Number newTableWidth) {
if (newTableWidth.intValue() <= fixOnTableWidth) {
// before you can set new value to column width property, need to unbind the autoresize binding
table.getColumns().get(0).prefWidthProperty().unbind();
table.getColumns().get(0).prefWidthProperty().setValue(firstColumnFixSize);
} else if (!table.getColumns().get(0).prefWidthProperty().isBound()) {
// than readd the autoresize binding if condition table.width > x
table.getColumns().get(0).prefWidthProperty()
.bind(table.widthProperty().multiply(firstColumnProportion));
}
}
});
}
}
advice to put the code in an separated TableAutoresizeModel class, there you can handle further calculations, for example on hiding columns add listener...
#HarleyDavidson 's answer in kotlin
val String.fxWidth: Double
get() = Text(this).layoutBounds.width
// call the method after inserting the data into table
fun <T> TableView<T>.autoResizeColumns() {
columnResizePolicy = TableView.UNCONSTRAINED_RESIZE_POLICY
columns.forEach { column ->
column.setPrefWidth(
(((0 until items.size).mapNotNull {
column.getCellData(it)
}.map {
it.toString().fxWidth
}.toMutableList() + listOf(
column.text.fxWidth
)).maxOrNull() ?: 0.0) + 10.0
)
}
}
This will set the minimum width of columns based on the font and the text, so that the column names wont be cropped.
public static void setDataTableMinColumnWidth(TableView<?> dataTable)
{
for (Node columnHeader : dataTable.lookupAll(".column-header"))
{
var columnString = columnHeader.getId();
if (columnString != null)
{
for (Node columnHeaderLabel : columnHeader.lookupAll(".label"))
{
var tableColumn = dataTable.getColumns()
.stream()
.filter(x -> x.getId()
.equals(columnString))
.findFirst();
if (columnHeaderLabel instanceof Label && tableColumn.isPresent())
{
var label = (Label) columnHeaderLabel;
/* calc text width based on font */
var theText = new Text(label.getText());
theText.setFont(label.getFont());
var width = theText.getBoundsInLocal()
.getWidth();
/*
* add 10px because of paddings/margins for the button
*/
tableColumn.get()
.setMinWidth(width + 10);
}
}
}
}
}
How to use:
dataTable.needsLayoutProperty()
.addListener((obs, o, n) -> setDataTableMinColumnWidth(dataTable));
For the Columns, the id property needs to be set first:
TableColumn<BundImportTask, String> columnTask = new TableColumn<>("My Column");
columnTask.setId("MyColumnId");
columnTask.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()
.fileName()));
I implemented a solution that it's fairly more complicated than the ones that I found here, but that allows a specific column to be resized by double clicking on the header, while still letting the user resize columns manually.
This is achieved by listening to click events on the header of the table (TableHeaderRow). When a double click occurs, the specific column header is found by matching the mouse event X and Y.
Note: to make this work it's necessary that each column has an ID set.
// when skin is loaded (hence css), setup click listener on header to make column fit to content max width on double click
tableView.skinProperty().addListener((a, b, newSkin) -> {
TableHeaderRow headerRow = (TableHeaderRow) tableView.lookup("TableHeaderRow");
NestedTableColumnHeader headers = (NestedTableColumnHeader) (headerRow.getChildren().get(1));
headerRow.setOnMouseClicked(evt -> {
if (evt.getClickCount() != 2 || evt.getButton() != MouseButton.PRIMARY) return;
// find the header column that contains the click
for (TableColumnHeader header : headers.getColumnHeaders()) {
if (header.contains(header.parentToLocal(evt.getX(), evt.getY()))) {
fitColumnWidthToContent(header.getId());
}
}
evt.consume();
});
});
The method that takes care of the resizing is the following:
private void fitColumnWidthToContent (String colId) {
// find column matching id
TableColumn column = null;
for (TableColumn tempCol : tableView.getColumns()) {
if (tempCol.getId().equals(colId)) {
column = tempCol;
break;
}
}
if (column == null) {
throw new IllegalStateException("Column ID doesn't match any actual column");
}
// set default width to column header width
Text text = new Text(column.getText());
double max = text.getLayoutBounds().getWidth();
for (int i = 0; i < tableView.getItems().size(); i++ ) {
if (column.getCellData(i) == null) continue;
text = new Text(column.getCellData(i).toString());
double textWidth = text.getLayoutBounds().getWidth();
if (textWidth > max) {
max = textWidth;
}
}
column.setPrefWidth(max + 12);
}
I hope this can be useful to anyone.
In order to allow also manual resizing, it's necessary to add a bit more code on table initalization:
// listen to width changes in columns and set to pref width (otherwise if for example width changes because of
// user resizing the column, applying the old pref width won't work because it stayed the same)
for (TableColumn col : tableView.getColumns()) {
col.widthProperty().addListener((obs, oldVal, newVal) -> {
col.setPrefWidth(newVal.doubleValue());
});
}
I have implemented a solution for TreeTableView. It is still in evolution but it manifests now promising results. Hereafter a description of the solution.
In the control skin class, I added to the control children the TreeTableView and an invisible VBox. A cell factory provide derived cells to the target TreeTableColumn. The derived cells wrap a Label node which is added or removed to the invisible VBox according to the empty property, and which its prefWidth is set according to the cell width. The cells make use of:
getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE)
I override the cell's computePrefWidth() method as follow:
#Override
protected double computePrefWidth(double height) {
return Double.max(_box.prefWidth(-1.0), super.computePrefWidth(height) + 24.0);
}
The Vbox width property is bind to the TreeTableColumn's prefWidth. This is required to resize as well the header of the column.
Is worth to note, that at the time being, to simplify the development of a solution, this approach works well with built in sort, order, and resize feature disabled. Ie.
_nameColumn = new TreeTableColumn<>("Name");
_nameColumn.setResizable(false);
_nameColumn.setReorderable(false);
_nameColumn.setSortable(false);
Happy coding
After long research. Best Solution is..
tblPlan.setColumnResizePolicy((param) -> true );
Platform.runLater(() -> customResize(tblPlan));
"Custom Resize"
public void customResize(TableView<?> view) {
AtomicLong width = new AtomicLong();
view.getColumns().forEach(col -> {
width.addAndGet((long) col.getWidth());
});
double tableWidth = view.getWidth();
if (tableWidth > width.get()) {
view.getColumns().forEach(col -> {
col.setPrefWidth(col.getWidth()+((tableWidth-width.get())/view.getColumns().size()));
});
}
}
<TableView fx:id="datalist" layoutX="30.0" layoutY="65.0" prefHeight="400.0" AnchorPane.bottomAnchor="100.0" AnchorPane.leftAnchor="30.0" AnchorPane.rightAnchor="30.0" AnchorPane.topAnchor="100.0">
<columns>
<TableColumn fx:id="number" minWidth="-1.0" prefWidth="-1.0" style="width: auto;" text="number" />
<TableColumn fx:id="id" minWidth="-1.0" prefWidth="-1.0" text="id" />
<TableColumn fx:id="name" minWidth="-1.0" prefWidth="-1.0" text="name" />
<TableColumn fx:id="action" minWidth="-1.0" prefWidth="-1.0" text="todo" />
</columns>
**<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>**
</TableView>