简体   繁体   中英

Problems with binding to Window Height and Width

I have some problems when I try to bind the height and width of a window to properties in my view model. Here is a small sample app to illustrate the problem. This is the code in app.xaml.xs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
       base.OnStartup(e);
        MainWindow mainWindow = new MainWindow();
        MainWindowViewModel mainWindowViewModel = new MainWindowViewModel();
        mainWindow.DataContext = mainWindowViewModel;
        mainWindow.Show();
    }
}

This is MainWindow.xaml:

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="{Binding WindowHeight}" 
        Width="{Binding WindowWidth}"
        BorderThickness="{Binding WindowBorderThickness}">
</Window>

And this is the view model:

public class MainWindowViewModel
{
    public int WindowWidth { get { return 100; } }
    public int WindowHeight { get { return 200; } }
    public int WindowBorderThickness { get { return 8; } }
}

When the program is started the getters of WindowHeight and WindowBorderThickness (but not WindowWidth) are called, so the height and the border of the window is set properly, but not the width.

I then add button that will trigger PropertyChanged for all properties, so that the view model now looks like this:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void TriggerPropertyChanges()
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("WindowWidth"));
            PropertyChanged(this, new PropertyChangedEventArgs("WindowHeight"));
            PropertyChanged(this, new PropertyChangedEventArgs("WindowBorderThickness"));
        }

    }

    public ICommand ButtonCommand { get { return new RelayCommand(delegate { TriggerPropertyChanges(); }); } }

    public int WindowWidth { get { return 100; } }
    public int WindowHeight { get { return 200; } }
    public int WindowBorderThickness { get { return 8; } }
}

Now, when I click the button, the getter of WindowBorderThickness is called, but not the ones for WindowWidth and WindowHeight. It all just seems very weird and inconsistent to me. What am I missing?

尝试使用双向绑定,它对我有用:

Width="{Binding Path=xExt, Mode=TwoWay}"

I had the same problem and I noticed that it depends whether height or width is written first in xaml. If height is first, then Binding work only for it and vice versa. The solution was to set Binding mode to 'TwoWay': The project I have made was with MS Studio 2010 and .NET 4.0

I will try to answer my own question. The bindings are working, but we can't really be sure that the layout system asks for eg the Width property of the window.

From MSDN :

If this element is a child element within some other element, then setting this property to a value is really only a suggested value. The layout system as well as the particular layout logic of the parent element will use the value as a nonbinding input during the layout process. In practical terms, a FrameworkElement is almost always the child element of something else; even when you set the Height on Window. (For Window, that value is used when the underlying application model establishes the basic rendering assumptions that create the Hwnd that hosts the application.)

A solution that seems to work is to bind the WindowWidth property to MinWidth and MaxWidth, as well as Width. One of these will be retrieved, at least in the test scenario I was using above.

OK,

I had the same problem and could not bind correctly the window dimensions (min, max, normal) to my viewmodel through XAML.

I don't know why but you can acheive all those bindings without any problem if you do them by code instead of by XAML.

Here is my C# code that worked :

this.SetBinding(Window.WidthProperty, new Binding("Width") { Source = MyViewModel, Mode=BindingMode.TwoWay });
this.SetBinding(Window.HeightProperty, new Binding("Height") { Source = MyViewModel, Mode=BindingMode.TwoWay });
this.SetBinding(Window.MaxWidthProperty, new Binding("MaxWidth") { Source = MyViewModel });
this.SetBinding(Window.MaxHeightProperty, new Binding("MaxHeight") { Source = MyViewModel });
this.SetBinding(Window.MinWidthProperty, new Binding("MinWidth") { Source = MyViewModel });
this.SetBinding(Window.MinHeightProperty, new Binding("MinHeight") { Source = MyViewModel });

It is strange that it only works in code and not in XAML. It is even more strange that it binds TwoWay by default for mMin and Max dimensions but not for Normal dimensions for which you have to specify « Mode=BindingMode.TwoWay ».

There should be a bug that Microsoft has to correct about this...

此外,您可以将SizeToContent="WidthAndHeight"MinHeightMinWidth ,因此MaxHeightMaxWidth不需要额外的调用。

Binding to MinWidth and MinHeight is correct. Also, if you need bind to MaxWidth and MaxHeight if your dynamic will both enlarge or reduce the size of Window.

I am not sure about your specific implementation, but I just write an example that maybe could helpful.

XAML

<Window x:Name="MainWindow"
    x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" 
    MinWidth="200"
    MinHeight="100"
    Width="{Binding ElementName=MainWindow, Path=WindowWidth}"
    Height="{Binding ElementName=MainWindow, Path=WindowHeight}">
    <Grid>
        <Slider 
            x:Name="slWidth" 
            Value="{Binding ElementName=MainWindow, Path=WindowWidth, Mode=TwoWay}"
            Minimum="200"
            Maximum="1600"
            Height="23" HorizontalAlignment="Left" Margin="56,22,0,0" VerticalAlignment="Top" Width="61" />
        <Label 
            Content="Width" 
            Height="28" 
            HorizontalAlignment="Left" 
            Margin="12,22,0,0" 
            Name="Label1" 
            VerticalAlignment="Top" />
        <Slider 
            x:Name="slHeight" 
            Value="{Binding ElementName=MainWindow, Path=WindowHeight, Mode=TwoWay}"
            Minimum="100"
            Maximum="800"
            Height="23" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="61" Margin="56,51,0,0" />
        <Label 
            Content="Height" 
            Height="28" 
            HorizontalAlignment="Left"              
            VerticalAlignment="Top" Margin="12,46,0,0" />
    </Grid>
</Window>

Code

Class MainWindow

    Public Shared ReadOnly WindowWidthProperty As DependencyProperty = _
                           DependencyProperty.Register("WindowWidth", _
                           GetType(Integer), GetType(MainWindow), _
                           New FrameworkPropertyMetadata(Nothing))

    Public Shared ReadOnly WindowHeightProperty As DependencyProperty = _
                         DependencyProperty.Register("WindowHeight", _
                         GetType(Integer), GetType(MainWindow), _
                         New FrameworkPropertyMetadata(Nothing))

    Public Property WindowWidth As Integer
        Get
            Return CInt(GetValue(WindowWidthProperty))
        End Get
        Set(ByVal value As Integer)
            SetValue(WindowWidthProperty, value)
        End Set
    End Property

    Public Property WindowHeight As Integer
        Get
            Return CInt(GetValue(WindowHeightProperty))
        End Get
        Set(ByVal value As Integer)
            SetValue(WindowHeightProperty, value)
        End Set
    End Property


End Class

C# Code

public readonly DependencyProperty WindowWidthProperty = DependencyProperty.Register("WindowWidth", typeof(Double), typeof(MainWindow), new FrameworkPropertyMetadata());
public readonly DependencyProperty WindowHeightProperty = DependencyProperty.Register("WindowHeight", typeof(Double), typeof(MainWindow), new FrameworkPropertyMetadata());

public double WindowWidth {
    get { return Convert.ToDouble(this.GetValue(WindowWidthProperty)); }
    set { this.SetValue(WindowWidthProperty, value); }
}

public double WindowHeight {
    get { return Convert.ToDouble(this.GetValue(WindowHeightProperty)); }
    set { this.SetValue(WindowHeightProperty, value); }
}
Height="{DynamicResource {x:Static SystemParameters.PrimaryScreenHeightKey}}" Width="{DynamicResource {x:Static SystemParameters.PrimaryScreenWidthKey}}"

对我来说效果很好。

I have fixed this issue using the below code. I hope it can help someone.

Important point: Dynamic binding in WPF always works only with property but not with a variable.

Declare a global public property in .xaml.cs file like below:

public Double DynamicHeight { get; set; }

// Set DynamicHeight property value in Window_Loaded or any other event
private void Window_Loaded(object sender, RoutedEventArgs e)
{
     DynamicHeight = 200;
}

Set a dynamic height in .xaml file like below:

<Grid Height="{Binding DynamicHeight, Mode=TwoWay}">
</Grid>

Please vote for this answer if you find it useful. Thanks!

只需添加 Mode = TwoWay Width="{Binding WindowWidth, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, FallbackValue=350}"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM