I would like to control the height and width of one of my windows through my ViewModel.
This seems simple enough.
<Window ... Width="{Binding Path=Width}" Height="{Binding Path=Height}" />
but nope. it doesn't work.
It checks the ViewModel's Width but not the Height.
Strangely enough, if I switch the order of Width and Height in the XAML it checks the Height and not the Width. i.e. It only checks the first of the two properties and totally ignores the second one.
Binding the MaxHeight and MinHeight do work, even after the Width. But then the user can not re-size the window.
Don't know what your ViewModel code looks like, but try creating a set for the Width and Height properties and set the binding Mode to TwoWay
I'm having this exact problem also, it seems like a bug.
For now I am just handling the DataContextChanged event of the Window and setting the Width and Height from the ViewModel manually.
you should have something like this in your window's load event:
public void AfterLoad()
{
this.Height = this.ViewModel.Settings.WindowHeight;
this.Width = this.ViewModel.Settings.WindowWidth;
if (this.ViewModel.Settings.WindowTop > 0 && this.ViewModel.Settings.WindowLeft > 0)
{
this.Top = this.ViewModel.Settings.WindowTop;
this.Left = this.ViewModel.Settings.WindowLeft;
}
}
then, handle the window's size changed event to remember the widht and height and also the position changed to remember top,left (if you wish).
binding to WindowState works fine.
This is correct by design (sad but true). The layout system does not actually enforce these values, see the remarks on the msdn documentation page for Window.Height...
In some cases, it is acceptable to set MinWidth and MaxWidth (resp. *Height) to achive a similar behaviour, but not always.
Sorry to have no better news...
You want to bind to ActualHeight and ActualWidth, not Height and Width.
So instead of:
<Window ... Width="{Binding Path=Width}" Height="{Binding Path=Height}" />
You want:
<Window ... Width="{Binding Path=ActualWidth}" Height="{Binding Path=ActualHeight}" />
I assume that you're using MVVM pattern with the common methods for raising any changed propertys from the ViewModel to the View, by an implementation of the INotifiyPropertyChanged-Interface in your ViewModel.
Not working:
WdwWidth = 600;
WdwHeight = 600;
RaisePropertyChanged("WdwWidth");
RaisePropertyChanged("WdwHeight");
Working:
WdwWidth = 600;
RaisePropertyChanged("WdwWidth");
WdwHeight = 600;
RaisePropertyChanged("WdwHeight");
It seems to me that the PropertysChanged-notification has to be raised right after the property has been actually changed.
Strange, but does the trick for me.
Edit: Make sure to set the Binding to TwoWay, e.g.Height="{Binding Path=WdwHeight, Mode=TwoWay}"
Related
I have a UITableView in a ViewController in a Storyboard (not a UITableViewController). What I want to do is add a custom UIView above the TableView in code. When the View is not there, the TableView's top anchor is anchored to PrimaryNavCollectionViewOutlet. I store this constraint as an outlet, and then if I have to add the View, I can use this outlet to remove the storyboard constraint.
I then constrain the inserted View to be below where the TableView was, and constraint the TableView to be below that.
Here's my code:
if (_viewAboveTableView != null)
{
TableView.RemoveConstraint(TableViewTopConstraint);
View.AddSubview(_viewAboveTableView);
_viewAboveTableView.TranslatesAutoresizingMaskIntoConstraints = false;
TableView.TranslatesAutoresizingMaskIntoConstraints = false;
_viewAboveTableView.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor).Active = true;
_viewAboveTableView.TopAnchor.ConstraintEqualTo(PrimaryNavCollectionViewOutlet.BottomAnchor).Active = true;
_viewAboveTableView.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor).Active = true;
_viewAboveTableView.BottomAnchor.ConstraintEqualTo(TableView.TopAnchor).Active = true;
}
Unfortunately, when I run it, I can't see _viewAboveTableView. I feel like I'm missing something easy, but I can't figure out what. I've tried LayoutIfNeeded() and a few other methods on the View, but they don't make it appear. What have I missed?
Add height constraint to _viewAboveTableView.
Or
Make sure _viewAboveTableView has content that will specify intrinsic content size -- it should have properly laid-out subviews with content size (intrinsic or explicit via layouts. e.g. image, label etc.)
My guess is _viewAboveTableView is not getting inflated because it doesn't specify intrinsic content size (height > 0). If it's wrong constraints, you should be able to see it in the console.
It looks like the problem with what I was doing was that TableView.RemoveConstraint(TableViewTopConstraint); didn't have the intended effect.
From this answer I learned that RemoveConstraint is, or is about to be, deprecated in favour of TableViewTopConstraint.Active = false;. This made my code work the way that was intended.
I want to change the width of my label since my swift code.
If possible, do it in a annimation prograssif for a change. Is it possible ?
When I do:
self.lblChoice1.frame.size.width += 50
My exchange label width not ... Why?
Thank you in advance.
EDIT :
The first response is not working.
print(self.lblChoice1.frame.size.width)
self.lblChoice1.frame.size.width += 150
self.lblChoice1.contentMode = UIViewContentMode.Redraw
self.lblChoice1.setNeedsDisplay()
print(self.lblChoice1.frame.size.width)
This code displays me well in my console:
150.0
300.0
But my label size does not change the display ...
You are changing the frame, but you aren't telling the view that it needs to redraw itself. After changing the frame you should then call
self.lblchoice1.setNeedsDisplay()
Although you may need to change the label's content mode to UIViewContentModeRedraw to make sure it redraws.
Although it would be better to use UIView block animation methods to do this.
I dont think you can edit the UILabel's frame directly. You should change entire frame instead.
var lblChoice1Frame = self.lblChoice1.frame
lblChoice1Frame.size.width += 150
UIView.animateWithDuration(0.3) { () -> Void in
self.lblChoice1.frame = lblChoice1Frame
}
I have a UIPageController that I placed in my xib file, so I could get the placement proper, but the value of "currentPage" set in the xib is the value that currentPage assumes when i try to set it to something else, like below, the line of code that sets my UIScrollView to the proper initial image when starting in the middle (i.e. not page 0):
aScrollView.contentOffset = CGPointMake((currentIndex + 0.0) * screenWidth, 0.0);
which calls
-(void) scrollViewDidScroll: (UIScrollView*) aScrollView
{
// Update the page control to match the current scroll:
CGPoint offset = aScrollView.contentOffset;
float width = self.view.frame.size.width;
int pageNumber = (int)(offset.x / width);
uiPageControl.currentPage = pageNumber;
NSLog(#"%g // %g = %d", offset.x, width, (int)(offset.x/width));
}
The pageNumber integer is correct, so I am properly calculating the desired page number from the offset. However, when I try to manually (programatically) set currentPage, it doesn't set to pageNumber, it sets to the value in the xib.
Easy solution is to not use the xib and do it all programatically, but I want to know how I can continue using the UIPageController in the xib, because it was easy to place without knowledge of coordinates, etc. Normally, Attributes set in the xib are overwritable programatically, so this bug really surprised me. Any suggestions on how I can properly set currentPage?
Thanks for your help!
I found a hack that temporarily solves my problem: in the xib, I set the "Number of Pages" value to something I know will be higher than the number of pages I will ever have.
If this is too small, it turns out that setting currentPage beyond that value didn't do anything because it was clamping or setting it to some number below the number of pages I had in the xib.
If I set this xib attribute higher, then setting currentPage works.
Sorry for the quick trigger.
I'm trying to have a TabSet in a Window. I would like the window to fit the contents so I turned autoSize on like I have done with many other windows. The window have a minimum width and height that I want it to be. My problem is that the TabSet in the Window doesn't fill up the height completely.
Here is my setup:
public MyWindow()
{
setHeight(MIN_HEIGHT);
setWidth(MIN_WIDTH);
setAutoSize(true);
theTabSet = new TabSet();
theTabSet.setTabBarPosition(Side.TOP);
theTabSet.setWidth100();
theTabSet.setHeight100();
// I need to set this for it to at least display the contents
theTabSet.setPaneContainerOverflow(Overflow.VISIBL E);
theTabSet.setOverflow(Overflow.VISIBLE);
//This seems to solve my issue buy I think I shouldn't be doing this.
theTabSet.setMinHeight(WINDOW_HEIGHT);
Tab tab1 = new Tab("first");
tab1.setPane(getFirstTab());
Tab tab2 = new Tab("second");
tab2.setTab(getSecondTab());
addItem(theTabSet);
}
private Layout getFirstTab(){
if(theFirstTab == null){
theFirstTab = new VLayout();
theFirstTab.setWidth100();
theFirstTab().setHeight100();
}
return theFirstTab;
}
Please let me know if I am missing something. According to the Layout API:
Like other Canvas subclasses, Layout and Stack components may have %
width and height values. To create a dynamically-resizing layout that
occupies the entire page (or entire parent component), set width and
height to "100%
theTabSet.setMinHeight(WINDOW_HEIGHT);
Seems to be the way to go. The window is set to fit it contents and the layout is set to fill the canvas. The window knows a minimum height and width but initially the layout doesn't know that. So we need to set it manually.
How do you create a JavaFX application that (instantly) dynamically resizes? Right now I have coded a simple application that dynamically resizes but the layout changes don't display until after the release of the mouse button on a drag. I want instantly see the results/layout changes before this button release.
I'm assuming this is done by just binding the correct values/controls with inverse... Any help would be great!
EDIT:
Here's how I got things working (thanks to Praeus). Exactly as he said, I had to bind my top level container/layout width and height to scene.width and scene.height. --
var scene: Scene;
Stage {
title: "Application title"
scene: scene = Scene {
content: [
XMigLayout {
width: bind scene.width;
height: bind scene.height;
...}]}}
Bind is different in JavaFX's 2.0 release. Angela Caicedo's video demonstrates how to bind properties nicely. A variable must be setup as an ObservableNumberValue and its get and set methods used. After it's setup you can then bind it to a component's property.
DoubleProperty x = new SimpleDoubleProperty(0);
x.set(1);
x.getValue();
imageView.xProperty().bind(x);
anchorPane.heightProperty().add(x);
Eric Bruno's post is another way of doing it.
scene.widthProperty().addListener(
new ChangeListener() {
public void changed(ObservableValue observable,
Object oldValue, Object newValue) {
Double width = (Double)newValue;
tbl.setPrefWidth(width);
}
});
scene.heightProperty().addListener(
new ChangeListener() {
public void changed(ObservableValue observable,
Object oldValue, Object newValue) {
Double height = (Double)newValue;
tbl.setPrefHeight(height);
}
});
Edits: Added another, more fitting answer along with where I found it.
Actually if you've already organized everything into layouts, then you might be able to just bind the top level layout container(s) width and height to the scene.width and scene.height. I don't think top level layout controls are managed by anything, so you may be safe just setting width and height of the layout control directly. If that doesn't work then you might have success binding the layoutInfo width and height.
Ideally you want to use layout management before resorting to binds. Excessive binds can be a performance problem, and can lead to slow redraws as you are resizing. In this case you can probably just use binds for the top level container, and then all of the children of the top level container should resize accordingly. At least I think that should work.
Yes I think you're on the right lines - binding would be the obvious way to do what you trying to do. You could also look at triggers.
Read the 1.3 guide to layouts as a starting point if you haven't already
The layout containers automatically manage the dynamic layout behavior of their content nodes. However, it is often desirable to also use the binding mechanism of the JavaFX Script to establish certain dynamic aspects of layout. For example, you can bind the width and height of an HBox container to the Scene height and width so that whenever Scene resizes, the HBox container resizes as well.
It's also probably worth reading articles and blog posts by JavaFX layout guru Amy Fowler
Here's what I ended up doing to get my resizing how I liked. There has to be an easier way but I surely don't know it.
My main method loads my controller and scene then calls my controllers init() method
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = (Parent) loader.load();
primaryStage.setTitle("Robot Interface");
primaryStage.setScene(new Scene(root));
primaryStage.show();
Controller controller = (Controller)loader.getController();
controller.init();
I am using a gridpane that dynamically resizes as the whole stage is resized by the user so it always fits. This is becaus its percentage sizing. This is done in the fxml way easier than with binding IMO.
<GridPane fx:id="gridPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="700.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints percentWidth="50" />
<ColumnConstraints percentWidth="50" />
</columnConstraints>
<rowConstraints>
<RowConstraints percentHeight="35" />
<RowConstraints percentHeight="35" />
<RowConstraints percentHeight="35" />
</rowConstraints>
<children>
<TextArea fx:id="textArea_Console" prefHeight="200.0" prefWidth="200.0" promptText="console/reponse" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<!-- <MediaView fx:id="mediaView_Images" fitHeight="225.0" fitWidth="225.0" GridPane.halignment="LEFT" GridPane.valignment="BOTTOM" /> -->
<TextArea fx:id="textArea_RoboAvail" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="121.0" prefWidth="123.0" promptText="robots available" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.valignment="TOP" />
<TextArea fx:id="textArea_SensorData" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="249.0" prefWidth="106.0" promptText="sensor data" GridPane.halignment="RIGHT" GridPane.valignment="TOP" />
<TextArea fx:id="textArea_Scripting" prefHeight="200.0" prefWidth="200.0" promptText="Scripting" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Currently Viewing" GridPane.halignment="LEFT" GridPane.valignment="TOP" />
<TextArea maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="250.0" prefWidth="100.0" promptText="to do list" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.valignment="TOP" />
<ImageView fx:id="imageView_Images" fitHeight="233.0" fitWidth="225.0" pickOnBounds="true" preserveRatio="true" GridPane.halignment="LEFT" GridPane.valignment="TOP" />
<Label fx:id="label_CheatSheet" text="Label" GridPane.halignment="LEFT" GridPane.rowIndex="2" GridPane.valignment="BOTTOM" />
</children>
Remember how my controller class had its init() method called. Well init() calls setupUI where I get my height and width and create callbacks for height and width changes later. Those change my class wide variables mHeight and mWidth;
private void setupUI()
{
imageView_Images.setImage( new Image("file:///C:\\Users\\administration\\IdeaProjects\\Robot User Interface\\Media\\filler.jpeg"));
mHeight = gridPane.getHeight();
mWidth = gridPane.getWidth();
//for initial startup sizing
dynamicRsize();
//callbacks to detect changes for sizing
gridPane.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
mWidth = newValue.doubleValue();
dynamicRsize();
}
});
gridPane.heightProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
mHeight = newValue.doubleValue();
dynamicRsize();
}
});
setup_TextArea_Scriptng();
setup_Label_CheatSheet();
}
Finally I can call dynamicResize() which adjust every object in the gridpane as a percentage of that rows width or height
private void dynamicRsize()
{
//two columns 50/50
//three rows 35/35/30
double columnType1 = mWidth/2;
double rowType1 = mHeight * .35;
double rowType2 = mHeight * .3;
//node 1
imageView_Images.setFitWidth(columnType1 * .75);
textArea_SensorData.setPrefWidth(columnType1 * .25);
//node 2
//node 3
//node 4
//node 5
//node 6
}