JavaFX 8 maxWidth Ignored? - layout

JavaFx 8 maxWidth Ignored
The maxWidth is ignored for HBox children with wider content.
For example, the center Pane below is far too large, extending under the right column (Pane):
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="1000.0" prefWidth="1236.0">
<children>
<Pane fx:id="leftPane" maxHeight="10000.0" maxWidth="120.0" minHeight="400.0" minWidth="100.0" prefHeight="10000.0" prefWidth="120.0"/>
<Pane fx:id="centerPane" maxHeight="10000.0" maxWidth="10000.0" minHeight="400.0" minWidth="120.0" prefHeight="10000.0" >
</Pane>
<Pane fx:id="rightPane" maxHeight="10000.0" maxWidth="120.0" minHeight="400.0" minWidth="100.0" prefHeight="10000.0" prefWidth="120.0" />
</children>
</HBox>
There is no conflicting CSS, such as defining a percentage, or otherwise setting a content width.
How to Make maxWidth a Hard-Limit?
Is there a way to make JavaFX recompute the child pane sizes each time the child content changes, while never exceeding the maxWidth of the parent?

From JavaFX Pane documentation:
This class may be used directly in cases where absolute positioning of children is required since it does not perform layout beyond resizing resizable children to their preferred sizes. It is the application's responsibility to position the children since the pane leaves the positions alone during layout.
So, you cannot reach your goal by using basic Panes as children. Better try to use StackPanes instead:
A stackpane's parent will resize the stackpane within the stackpane's resizable range during layout.
A stackpane's unbounded maximum width and height are an indication to the parent that it may be resized beyond its preferred size to fill whatever space is assigned to it.
Take a look at this example (I added background colors to the StackPanes, since I used SceneBuilder to test the behavior) and tell me please if it satisfies your needs:
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="1000.0" prefWidth="1236.0" style="-fx-background-color: lightblue;" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane fx:id="leftPane" maxHeight="10000.0" maxWidth="120.0" minHeight="400.0" minWidth="100.0" prefHeight="10000.0" prefWidth="120.0" style="-fx-background-color: red;" />
<StackPane fx:id="centerPane" maxHeight="10000.0" maxWidth="10000.0" minHeight="400.0" minWidth="120.0" prefHeight="10000.0" style="-fx-background-color: white;">
<children>
<Label prefWidth="999.0" text="Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. Label of example. " />
</children></StackPane>
<StackPane fx:id="rightPane" maxHeight="10000.0" maxWidth="120.0" minHeight="400.0" minWidth="100.0" prefHeight="10000.0" prefWidth="120.0" style="-fx-background-color: green;" />
</children></HBox>
You can also use other Pane or Box components instead.
Update:
The previous example makes the center Pane to dynamically adjust in function of the width of its children, fulfilling the parent Pane if needed, but never exceeding its maximum width. So, if you want the Pane to always fill the width of its parent, a big enough preferred width must be added to the center Pane.
<StackPane fx:id="centerPane" maxHeight="10000.0" minHeight="400.0" prefHeight="10000.0" prefWidth="1000.0"/>

To make container resize automatically according to its children size use javafx constants like:
centerPane.setMinWidth(Control.USE_PREF_SIZE);

Related

Unwanted margin after fo:external-graphic

A margin of 1 mm appears after an image. My XML:
<fo:block-container position="absolute" width="47.6mm" height="160mm" font-family="Verdana">
<fo:block-container position="absolute" height="25mm" space-after="3mm">
<fo:block>
<fo:external-graphic src="release heading.svg" />
</fo:block>
<fo:block font-size="7pt" text-align="start" color="#6f696d" display-align="center" start-indent="0mm" background-color="#efefef">
<fo:table border-collapse="collapse">
...
</fo:table>
</fo:block>
</fo:block-container>
It looks like this:
I do not want the white line.
I tried to set content-height="100%", to no avail:
<fo:external-graphic content-height="100%" src="release heading.svg" />
If I draw margins around the image and the block, it is clear that the margin is between the two:
<fo:block border-style="solid" border-width="thin" border-color="black">
<fo:external-graphic content-height="100%" src="release heading.svg" border-style="solid" border-width="thin" border-color="black"/>
</fo:block>
But how do I get rid of it?
I just found the answer on a FOP forum.
The line-height property is not specified, but calculated to 1.2 * font-size. So the block is higher than the image.
If I set font-size to 0 on the block:
<fo:block font-size="0pt">
<fo:external-graphic content-height="100%" src="release heading.svg"/>
</fo:block>
... the line disappears.

win10 app - Rounded corner images keeping aspect ratio

Simple app that just displays a list of images. The list of images must keep there aspect ratio, but be full window width + a bit of margin. The hard part is making the images additionally have rounded corners.
Ideas?
Only success I have had is with ImageBrush, but any control using that doesn't keep the aspect ratio. For example, here you must set the height and width.
<Rectangle RadiusX="10" RadiusY="10" Stretch="Fill" Width="100" Height="100" >
<Rectangle.Fill>
<ImageBrush ImageSource="{Binding}"></ImageBrush>
</Rectangle.Fill>
</Rectangle>
full source here: http://1drv.ms/1HlZHVe
I got nicely rounded corners on my images (in my UWP app) by using an ImageBrush, however be careful if you do this programmatically - the first time I did I managed to use my memory rather poorly and consumed too much (poor coding).
I was using the ImageBrush much as you seem to be, but I wasn't getting any distortion of the aspect ratio; make sure you're setting properties like Stretch appropriately - e.g. Stretch.UniformToFill.
<Rectangle x:Name="rctCardFace" Margin="0" RadiusX="20" RadiusY="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<ImageBrush Stretch="UniformToFill"/>
</Rectangle.Fill>
</Rectangle>
Or in C#
Uri path = new Uri(_ActiveStyle.BackgroundImagePath, UriKind.Absolute);
BitmapImage bitmapImage = new BitmapImage(path);
if (_AppReference.CardManager.TempImgBrush == null)
{
_AppReference.CardManager.TempImgBrush = new ImageBrush();
}
_AppReference.CardManager.TempImgBrush.ImageSource = bitmapImage;
_AppReference.CardManager.TempImgBrush.Stretch = _ActiveStyle.BackgroundImageStretch;
_AppReference.CardManager.TempImgBrush.AlignmentX = _ActiveStyle.BackgroundImageAlignX;
_AppReference.CardManager.TempImgBrush.AlignmentY = _ActiveStyle.BackgroundImageAlignY;
cfPreview.ImgB = _AppReference.CardManager.TempImgBrush;
Hi you can use the following code to create a rounded corner image in UWP:
<Ellipse Width="250" Height="250">
<Ellipse.Fill>
<ImageBrush ImageSource="ms-appx:///highfive.jpg" />
</Ellipse.Fill>
</Ellipse>

How do you make an Imageview to resize itself when the Window gets resized

I have a GridPane with an arbitrary number of rows and columns, and fill each cell with an ImageView.
Now, this GridPane is contained in an AnchorPane, so when the Window gets resized the GridPane grows or shrinks fine, but the ImageViews does not change its size.
What do I have to do to make the ImageView take the size of its cell in the GridPane?
This may have two reasons.
1) you might have not defined row and column constraints for your grid pane. Even when a GridPane grows, it's cells will not necessarily go beyond their preferred size. The best way to achieve this is to define constraints with a percentage width/height, like so:
public class ImageGrid extends Application {
#Override
public void start(final Stage stage) throws Exception {
final GridPane pane = new GridPane();
for (int i = 0; i < 10; i++) {
final ImageView imageView = new ImageView(new Image("..."));
pane.getChildren().add(imageView);
GridPane.setConstraints(imageView, i%5, i/5);
}
pane.getColumnConstraints().addAll(
columnWithPercentage(20),
columnWithPercentage(20),
columnWithPercentage(20),
columnWithPercentage(20),
columnWithPercentage(20)
);
pane.getRowConstraints().addAll(
rowWithPercentage(50),
rowWithPercentage(50)
);
final Scene scene = new Scene(pane);
stage.setScene(scene);
stage.setWidth(800);
stage.setHeight(600);
stage.show();
}
private ColumnConstraints columnWithPercentage(final double percentage) {
final ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(percentage);
return constraints;
}
private RowConstraints rowWithPercentage(final double percentage) {
final RowConstraints constraints = new RowConstraints();
constraints.setPercentHeight(percentage);
return constraints;
}
public static void main(String[] args) {
Application.launch(args);
}
}
This will create a 5x2 grid of ImageViews wich will fill the entire space of the grid pane equally.
2) The solution above will grow your image views, but the ImageView class itself will not stretch an image beyond its normal size until told to. You'll probably have to bind ImageView#fitWidthProperty and ImageView#fitHeightProperty as well if you want the images to scale up when the grid expands.
I'm a beginner to JavaFX and I had a similar problem. I had an AnchorPane which contained a BorderPane. The center of the BorderPane contained a GridPane (gridPaneMaze). This gridPaneMaze used an array ImageView[7][7] images to fill each cell of my gridpane with a square .png file.
I wanted my images to change with the height of the window. Because I used squares, the width of the column should change automatically.
At first, when I resized my window, my columns were broader than my images and the height of the images were to big for the rows.
After trial and error I found that this did the trick:
IMAGES
I had for-loop traverse between the rows and columns and in each cell I set the Imageview with the corresponding image (which is always a square) and used this height-binding (I divided the height of the gridpane by the number of rows):
images[i][j].fitHeightProperty().bind(gridPaneMaze.heightProperty().divide(7));
images[i][j].setPreserveRatio(true);
I presume you could also use the widthProperty and divide by the number of columns. But because most monitors have a smaller height than width, I used the height.
GRIDPANE:
I made my gridPane with FXML but that makes no difference to the values.
<BorderPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<center>
<GridPane fx:id="gridPaneMaze" centerShape="false" minHeight="0.0" minWidth="0.0" BorderPane.alignment="TOP_LEFT">
<columnConstraints>
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
</columnConstraints>
<rowConstraints>
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
<RowConstraints valignment="CENTER" />
</rowConstraints>
<children>
//...
</children>
<BorderPane.margin>
<Insets left="10.0" top="10.0" />
</BorderPane.margin>
</GridPane>
</center>
BORDERPANE: I added a borderpane to my anchorpane, so I set all anchors to '0'.
GRIDPANE: had minWidth 0 and minHeight 0. I set no prefWidth, prefHeight, maxWidth or maxHeight.
ROWS: I set nothing but the vertical alignment to center (no minHeight, prefHeight of maxHeight)
COLUMNS: I set nothing but the horizontal alignment to center (no minWidth, prefWidth of maxWidth)
Hopefully this helps...

How do specify a width percentage in JavaFX 2 using FXML?

I am looking for a way to say set a maxWidth size to 80% in FXML.
Much like in web development.
<VBox fx:id="testVB" prefWidth="600">
But this does not:
<VBox fx:id="testVB" prefWidth="80%">
I know that in Straight JavaFX2 non-fxml you can create insets? What is the best way to do this outside of code in FMXL?
Thanks!
Riley
I'm not sure you can. You need to use the GridPane layout component. In this component, you can specify rows and columns constraints, and in these constraints you can specify a width as a percentage. For example:
<GridPane>
<children>
<TitledPane text="testGridPane" GridPane.columnIndex="0" GridPane.rowIndex="0" />
</children>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="20.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
This code defines a GridPane with a first column with a width of 80%. The TitledPane is set in the first cell of the first column of this GridPane, and can (because you need to be sure that the width constraints of the TitledPane match your needs) occupy 80% of the width of the GridPane.
Please note that I removed all information not relevant to your question. By the way, Oracle's Scene Builder tool is very useful to define complex FXML layout.
It seems like many answers have already been provided and they should work. However, there is a way to set percentages:
<fx:define>
<Screen fx:factory="getPrimary" fx:id="screen" />
</fx:define>
This would help you detect the dimensions of the current screen, the application is being displayed on. Now that we have the display dimensions, we can play with it in FXML as follows:
<HBox fx:id="hroot" prefHeight="${screen.visualBounds.height}" prefWidth="${screen.visualBounds.width}"> Your FXML elements inside the root... </HBox>
Note that I use visualBounds, since this would get me the available space on the screen, since I don't want an overlap with the taskbar in Windows for example. For fullscreen applications, you would just use 'bounds'.
Now, to come to your point of using percentages, you can actually play with the value of the prefheight and prefWidth. You can put calculations inside the ${}.
Optionally:
If you want to have all your elements use relative sizes, just refer to them, using their ID and width or height property, and make your calculation.
<VBox fx:id="VBSidebar" prefWidth="${hroot.width*0.15}" prefHeight="${hroot.height}"> more elements.. </VBox>
Hope this helps!
You can simulate it - basic example that simulates 50% for two cols in an HBox. You can add dummy panes to get thirds, etc.
HBox {
VBox {
static hgrow : "ALWAYS",
Label {
text : "Privacy",
alignment : "CENTER",
styleClass : ["h2", "heading"]
}
},
VBox {
static hgrow : "ALWAYS",
Label {
text : "Messages",
alignment : "CENTER",
styleClass : ["h2", "heading"]
},
Label {text:""}
}
}

WPF - Border with a OpacityMask/VisualBrush: Memory Leaks

A brief explanation about my app:
the application in which I'm working on is such a greeting cards designer. Imagine something in which there is a background image, and an indefinite number of "layers" (in particular, pictures) that stay over the background and can be moved, resized, moved front and back, etc...
It is also possibile to apply particular shapes to these layers, like a star, an ellipse, .. and after the card is made, it's possibile to save is to jpeg file.
The problem
Everything works correctly, but I detected that when a shape is applied to a layer, a memory leak is generated.
Here is the code of the UserControl of each layer:
<UserControl>
.....
<Grid x:Name="_myGrid" >
<Border x:Name="im_the_problem" BorderThickness="0" OpacityMask="{Binding Path=MyMask.Data, Converter={StaticResource MaskConverter}}">
<!-- My Image... -->
</Border>
</Grid>
</UserControl>
where MaskConverter code is the following:
public class MaskConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
String maskData = value as String;
if (maskData == null)
return null;
if (maskData == "")
return null;
VisualBrush vb = new VisualBrush();
vb.Visual = XamlReader.Parse(maskData) as Visual;
return vb;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
The Parameter "MyMask.Data" is a XAML Path (that is the shape that I'm applying) that I dinamically load from a textfile that contains different shapes.
So, the principle is that if I have the border named *im_the_problem*, the memory is NOT released. If I comment *im_the_problem* (so I'll just have rectangular layers/pictures without shapes) everything work like a charm, without memory leaks.
The problem should be in the OpacityMask + VisualBrush.
Am I doing something wrong?
Or is there a known problem? Is there a way to do the same (apply a shape to a picture..) in a different manner?
Thanks.
You might be able to try binding the MyMask.Data to an actual Path.Data, and setting the Path.Fill to an ImageBrush created from the image?
You need to freeze your VisualBrush ;)
I had this problem in a DataGrid's column template where I was using a <Canvas><Path /></Canvas> (as a static-resource) into a VisualBrush (also a static-resource) and using that as the OpacityMask for a Rectangle. Whenever the DataGrid was reloaded the Rectangle wouldn't release VisualBrush references to the OpacityMask, I used a memory-profiler tool to reveal that all the VisualBrush objects were using the bulk of memory.
I don't understand why or how this happened - but I'm glad I'm not alone (even if I had the same problem some 6.5 years later...).
My XAML was something like this:
<DataGrid.Resources>
<Canvas x:Key="icon" ...>
<Path ... />
</Canvas>
<VisualBrush x:Key="iconBrush" Stretch="Uniform" Visual="{StaticResource icon}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
OpacityMask="{StaticResource iconBrush}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...
</DataGrid.Columns>
I read that setting IsFrozen = true (done using this technique: https://www.codeproject.com/Tips/72221/Freeze-brushes-directly-in-the-XAML-to-improve-you ) would help memory issues with Brushes, however this seemingly had no effect at all. Weird.
I thought I'd experiment and I reasoned that if the issue was leaking the VisualBrush then I wondered if having it as a StaticResource was messing with object-references, so I changed it to an "owned" object, like so:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
>
<VisualBrush Stretch="Uniform" Visual="{StaticResource iconBrush}" />
</Rectangle>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
This fixed the issue! And I still don't know why - I wonder if it's a bug in WPF?
On a related note, I came to realise that using a VisualBrush was overkill as I'm rendering just a simple Path - VisualBrush is expensive because it renders an entire WPF view - I also learned from other documentation that Path itself isn't necessary for rendering simple shapes because itself is a complete UIElement and FrameworkElement - which are "heavier" types.
I changed my code to store the path in a PathGeometry value inside a GeometryDrawing static-resource which is loaded into a DrawingBrush:
<GeometryDrawing x:Key="iconDrawing" Brush="Black" Geometry="..." />
<Rectangle
Fill="{Binding Foreground, ElementName=myDataGrid}"
Width="14"
Height="14"
Margin="4"
Visibility="{Binding IconVisibility}"
OpacityMask="{StaticResource iconBrush}"
>
<DrawingBrush Stretch="Uniform" Drawing="{StaticResource iconDrawing}" />
</Rectangle>
Doing this also made a dent in memory usage, and hopefully, performance.
In your project I see you're not using the path information as a resource, but the same technique applies: load your path into a PathGeometry (or rather, StreamGeometry object, which is even faster and is meant for immutable geometry) and set that as the Drawing for a DrawingBrush.

Resources