Dynamic/instant resize in JavaFX - binding

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
}

Related

.NET MAUI: Unwanted whitespace at bottom of screen using grid layout

I have a simple grid layout:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="5*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Text="Test1"
VerticalOptions="Fill"
BackgroundColor="Green"/>
<Button Grid.Row="1"
Text="Test2"
VerticalOptions="Fill"
BackgroundColor="Red"/>
</Grid>
I expect the red area to extend all the way to the bottom of the screen. However, There's some white space.
If I change it to a collection view with a footer, it properly fills the bottom section of the screen:
For reference:
<CollectionView>
<CollectionView.Header>
<Button
Text="Test1"
VerticalOptions="Fill"
BackgroundColor="Green"/>
</CollectionView.Header>
<CollectionView.EmptyView>
<Label Text="No items"/>
</CollectionView.EmptyView>
<CollectionView.Footer>
<Button
Text="Test2"
VerticalOptions="Fill"
BackgroundColor="Red"/>
</CollectionView.Footer>
</CollectionView>
Is there a correct way to fill that bottom "footer" area on a grid view? or without using a collection view?
I don't think it matters but I'm running in iOS simulator on Mac iOS 16.2.
The white space at the bottom of the screen is iOS Page UseSafeArea in iOS. However, it is not yet implemented in Maui and you can track the MAUI issue here: https://github.com/dotnet/maui/issues/5856.
As an alternative workaround for now, you can set the Page Padding value to remove the bottom white space. In the OnAppearing Method, set the safeInsets of the page:
protected override void OnAppearing()
{
base.OnAppearing();
DeviceSafeInsetsService d = new DeviceSafeInsetsService();
double topArea = d.GetSafeAreaTop();
double bottomArea = d.GetSafeAreaBottom();
var safeInsets = On<iOS>().SafeAreaInsets();
safeInsets.Top = -topArea;
safeInsets.Bottom = -bottomArea;
Padding = safeInsets;
}
And then write platform specific code to get the topArea and bottomArea value.
1.Create DeviceSafeInsetsService.cs in Project folder.
public partial class DeviceSafeInsetsService
{
public partial double GetSafeAreaTop();
public partial double GetSafeAreaBottom();
}
2.And then under the Project/Platform/iOS folder, create a iOS specific class DeviceSafeInsetsService.cs:
public partial class DeviceSafeInsetsService
{
public partial double GetSafeAreaBottom()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
var bottomPadding = window.SafeAreaInsets.Bottom;
return bottomPadding;
}
return 0;
}
public partial double GetSafeAreaTop()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
var TopPadding = window.SafeAreaInsets.Top;
return TopPadding;
}
return 0;
}
}
For more details, you can refer to this sample .NET MAUI Platform Code Sample.
Set your Corner Radius property to 0 in your button control's property definition
<Button
CornerRadius="0"
Text="Test2"
VerticalOptions="Fill"
BackgroundColor="Red"/>

Correct shadows for the Xamarin frame ios

I made a custom render shadow for the frame element - everything is fine the shadow that turns out to suit me, but because of using this custom render I get a bad effect on the RadioButton element, how do I edit this render so that the radio button element is normal?
sample image
/// <summary>
/// Renderer to update all frames with better shadows matching material design standards
/// </summary>
public class MaterialFrameRenderer : FrameRenderer
{
public override void Draw(CGRect rect)
{
base.Draw(rect);
// Update shadow to match better material design standards of elevation
Layer.ShadowRadius = Element.CornerRadius;
Layer.ShadowColor = UIColor.Gray.CGColor;
Layer.ShadowOffset = new CGSize(2, 2);
Layer.ShadowOpacity = 0.40f;
Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
Layer.MasksToBounds = false;
}
}
It seems that something goes wrong with RadioButton Rendering , we even can't create custom renderer for RadioButton on iOS ,check https://forums.xamarin.com/discussion/comment/430069#Comment_430069 .
I strongly recommend you to use Xamarin.Forms.InputKit instead of Xamarin RadioButton .
It is pretty nice.
xmlns:input="clr-namespace:Plugin.InputKit.Shared.Controls;assembly=Plugin.InputKit"
<Frame Margin="100" CornerRadius="20" >
<input:RadioButtonGroupView>
<input:RadioButton Text="Option 1" />
<input:RadioButton Text="Option 2" />
<input:RadioButton Text="Option 3" />
<input:RadioButton Text="Option 4" />
</input:RadioButtonGroupView>
</Frame>
</ContentPage.Content>

making a multiline, expanding TextInput with React-Native

I'm working on a react-native app and need a TextInput that has similar functionality to the textview in the "messages" app on iOS—it should start out as one line and then gracefully expand to more lines until some limit (like 5 lines of text) and then start scrolling along to latest line as needed.
Took a look at the SlackTextViewController but a) seems like it has a lot of stuff that I don't want and b) I'd like to try to keep as much code in React (and out of objective-C/swift) as possible.
Edit: Just want to emphasize that I would prefer REACT (JAVASCRIPT) code, as stated above, rather than Objective-C or Swift.
I tried two different ways to do this today. Neither are the best but I thought I'd record my efforts in case they are helpful. They both definitely had the effect you are looking for, though sometime delayed with all the async communication.
1) Offscreen Text height
So just under the TextInput, I added a regular Text field with the same font and padding and such. I registered the onChange listener on the input and called setState({text: event.nativeEvent.text}). The Text filed got its value from the state. Both had onLayout Listeners. Basically, the goal was to get the height for the TextInput from the (unrestricted) Text. Then I hid the Text way offscreen
https://gist.github.com/bleonard/f7d748e89ad2a485ec34
2) Native Module
Really, I just needed the height of the content in the real UITextView. So I added a category to RCTUIManager as there are several methods there already that are helpful. I got rid of the hidden Text view. So onChange, I ask for the height and use that in the same way via the state.
https://gist.github.com/bleonard/6770fbfe0394a34c864b
3) Github PR
What I really hope is that this PR gets accepted. It looks to do something like this automatically.
https://github.com/facebook/react-native/pull/1229
Adding multiline={true} to a TextInput will allow scrolling if the amount of text exceeds the available space. You can then change the height of the TextInput by accessing the nativeEvent.contentSize.height of the event from the onChange prop.
class Comment extends Component {
state = {
text: '',
height: 25
}
onTextChange(event) {
const { contentSize, text } = event.nativeEvent;
this.setState({
text: text,
height: contentSize.height > 100 ? 100 : contentSize.height
});
}
render() {
return (
<TextInput
multiline
style={{ height: this.state.height }}
onChange={this.onTextChange.bind(this)}
value={this.state.text}
/>
);
}
}
As of Oct'17 there is a nice component from Wix to do this:
https://github.com/wix/react-native-autogrow-textinput
The usage can be very simple:
<AutoGrowingTextInput
style={styles.textInput}
placeholder="Enter text"
value={this.state.text}
onChangeText={this._handleChangeText}
/>
And there are some extra props like minHeight and maxHeight for example.
I'm using it on RN 0.47.2
Implement the UITextView's delegate method textViewDidChange and play with the rect
- (void)textViewDidChange:(UITextView *)textView {
CGSize constraintSize = CGSizeMake(textView.frame.size.width, MAXFLOAT);
CGRect textRect = [textView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName:textView.font}
context:nil];
NSLog(#"Frame:%#", NSStringFromCGRect(textRect));
CGRect newRect = textView.frame;
newRect.size.height = textRect.size.height;
textView.frame = newRect;
}
#Groovietunes got almost all of it correct but as of 26-06-19 things have change a bit from his initial answer.
For one, the onChange prop on textinputs no longer returns the contentSize object, hence event.nativeEvent.contentSize will return undefined.
The fix for this is to use the onContentSizeChange prop. But there's a catch; onContentSizeChange runs only once when the component mounts thus making it impossible to dynamically get the height as it changes.
To fix this we need to make sure the value prop is set after onContentSizeChange in the hierarchy of props in order to keep obtaining the content size as the textinput grows.
At the end of it all i had a component that looked like this
<TextInput
placeholder={this.props.placeholder}
autoFocus={this.props.autoFocus}
style={[
styles.inputFlat,
{ height: this.state.height > 40 ? this.state.height : 40 }
]}
ref="inputFlat"
onFocus={() => {
this.toggleInput();
this.toggleButton();
}}
multiline={this.props.multiline}
onBlur={() => {
this.toggleInput();
this.toggleButton();
}}
onChangeText={this.props.onChangeText}
onContentSizeChange={event => {
const { contentSize } = event.nativeEvent;
this.setState({
height: contentSize.height
});
}}
value={this.state.value}
/>
if for some reason it seems broken as at the time of viewing this answer, refer to this documentation for any updates
Another solution is to check '\n' symbols and set the numberOfLines property.
Works for me.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {TextInput} from 'react-native';
export default class TextInputAutogrow extends Component {
constructor(props) {
super(props);
this._ref = null;
this.bindRef = this.bindRef.bind(this);
this.onChangeText = this.onChangeText.bind(this);
this.state = {
numberOfLines: this.getNumberOfLines()
};
}
bindRef(c) {
this._ref = c;
this.props.innerRef && this.props.innerRef(c);
}
getText() {
return typeof this.props.value === 'string' ?
this.props.value :
(
typeof this.props.defaultValue === 'string' ?
this.props.defaultValue :
''
);
}
getNumberOfLines(value) {
if (value === undefined) {
value = this.getText();
}
return Math.max(this.props.numberOfLines, value.split('\n').length - 1) + 1;
}
onChangeText(value) {
this.setState({numberOfLines: this.getNumberOfLines(value)})
}
render() {
return (
<TextInput
{...this.props}
ref={this.bindRef}
numberOfLines={this.state.numberOfLines}
onChangeText={this.onChangeText}
/>
)
}
}
TextInputAutogrow.propTypes = {
...TextInput.propTypes,
innerRef: PropTypes.func,
};
TextInputAutogrow.defaultProps = {
numberOfLines: 4,
};
For anyone wants an easy solution, using new versions of RN, currently using 0.63.4,
the team added autoGrow to the TextInput if it's multiline, which is the case.
Don't use height (StyeSheet property) for the component or the parent View/Views
Use minHeight, maxHeight instead, and if multiline is enabled, the TextInput will grow till reaching the maxHeight, then will be scrollable.
no calculation nor inner state needs to ba maintained.
Same thing apply for minWidth, maxWidth if you need to expand widly.
Since react-native-autogrow-textinput seems to be unmaintained, I would recommend using native-base <Input/> as it has properties that enables multiline and auto-height as follows:
import { Input } from 'native-base'
<Input
height={'auto'}
multiline={true}
// ...
/>

Matching the vertical scroll position of a Grid to a RichEditBox or TextBox

I've got a Windows Store app with a RichEditBox (editor) and a Grid (MarginNotes).
I need the vertical scroll position of the two elements to be matched at all times. The purpose of this is to allow the user to add notes in the margin of the document.
I've already figured out Note positioning based on the cursor position - when a note is added, a text selection is made of everything up to the cursor. that selection is then added to a second, invisible RichEditBox, inside a StackPanel. I then get the ActualHeight of this control which gives me the position of the note in the grid.
My issue is that when I scroll the RichEditBox up and down, the Grid does not scroll accordingly.
First Technique
I tried putting them both inside a ScrollViewer, and disabling scrolling on the RichEditBox
<ScrollViewer x:Name="EditorScroller"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="{Binding *" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Grid x:Name="MarginNotes" Grid.Column="0" HorizontalAlignment="Right"
Height="{Binding ActualHeight, ElementName=editor}">
</Grid>
<StackPanel Grid.Column="1">
<RichEditBox x:Name="margin_helper" Opacity="0" Height="Auto"></RichEditBox>
</StackPanel>
<RichEditBox x:Name="editor" Grid.Column="1" Height="Auto"
ScrollViewer.VerticalScrollBarVisibility="Hidden" />
</Grid>
</ScrollViewer>
When I scroll to the bottom of the RichEditBox control, and hit enter a few times, the cursor drops out of sight. The ScrollViewer doesn't scroll automatically with the cursor.
I tried adding C# code which would check the position of the cursor, compare it to the VerticalOffset and height of the editor, and then adjust the scroll accordingly. This worked, but was incredibly slow. Initially I had it on the KeyUp event which brought the app to a standstill when I typed a sentence. Afterwards I put it on a 5 second timer, but this still slowed down the app performance and also meant that there could be a 5 second delay between the cursor dropping out of sight and the RichEditBox scrolling.
Second Technique
I also tried putting just MarginNotes in its own ScrollViewer, and programmatically setting the VerticalOffset based off my RichEditBoxs ViewChanged event.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="{Binding *" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<ScrollViewer x:Name="MarginScroller" Grid.Column="0"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid x:Name="MarginNotes" HorizontalAlignment="Right"
Height="{Binding ActualHeight, ElementName=editor}">
</Grid>
</ScrollViewer>
<StackPanel Grid.Column="1">
<RichEditBox x:Name="margin_helper" Opacity="0" Height="Auto"></RichEditBox>
</StackPanel>
<RichEditBox x:Name="editor" Grid.Column="1" Height="Auto"
Loaded="editor_loaded" SizeChanged="editor_SizeChanged" />
</Grid>
relevant event handlers
void editor_Loaded(object sender, RoutedEventArgs e)
{
// setting this in the OnNavigatedTo causes a crash, has to be set here.
// this uses WinRTXAMLToolkit as suggested by Nate Diamond to find the
// ScrollViewer and add the event handler
editor.GetFirstDescendantOfType<ScrollViewer>().ViewChanged += editor_ViewChanged;
}
private void editor_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
// when the RichEditBox scrolls, scroll the MarginScroller the same amount
double editor_vertical_offset = ((ScrollViewer)sender).VerticalOffset;
MarginScroller.ChangeView(0, editor_vertical_offset, 1);
}
private void editor_SizeChanged(object sender, SizeChangedEventArgs e)
{
// when the RichEditBox size changes, change the size of MarginNotes to match
string text = "";
editor.Document.GetText(TextGetOptions.None, out text);
margin_helper.Document.SetText(TextSetOptions.None, text);
MarginNotes.Height = margin_helper.ActualHeight;
}
This worked, but was quite laggy as scrolling is not applied until the ViewChanged event fires, after scrolling has stopped. I tried using the ViewChanging event, but it does not fire at all for some reason. Additionally, the Grid was sometimes mis-positioned after a fast scroll.
So, what makes this difficult is that the size of the text or the placement of the text in different types of TextBoxes means that syncing the scrollbar doesn't guarantee you are syncing the text. Having said that, here's how you do it.
void MainPage_Loaded(object sender, RoutedEventArgs args)
{
MyRichEditBox.Document.SetText(Windows.UI.Text.TextSetOptions.None, MyTextBox.Text);
var textboxScroll = Children(MyTextBox).First(x => x is ScrollViewer) as ScrollViewer;
textboxScroll.ViewChanged += (s, e) => Sync(MyTextBox, MyRichEditBox);
}
public void Sync(TextBox textbox, RichEditBox richbox)
{
var textboxScroll = Children(textbox).First(x => x is ScrollViewer) as ScrollViewer;
var richboxScroll = Children(richbox).First(x => x is ScrollViewer) as ScrollViewer;
richboxScroll.ChangeView(null, textboxScroll.VerticalOffset, null);
}
public static IEnumerable<FrameworkElement> Children(FrameworkElement element)
{
Func<DependencyObject, List<FrameworkElement>> recurseChildren = null;
recurseChildren = (parent) =>
{
var list = new List<FrameworkElement>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is FrameworkElement)
list.Add(child as FrameworkElement);
list.AddRange(recurseChildren(child));
}
return list;
};
var children = recurseChildren(element);
return children;
}
Deciding when to invoke the sync is tricky. Maybe on PointerReleased, PointerExit, LostFocus, KeyUp - there are a lot of ways to scroll is the real issue there. You might need to handle all of those. But, it is what it is. At least you can.
Best of luck.

wpf Window Width not binding

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}"

Resources