10

I'm building a WPF application in which I have a need to get the width, height and locations of the window from my view model. I'm using the following XAML:

<Window x:Class="ScreenCapture.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Height="{Binding Height, Mode=OneWayToSource, FallbackValue=300}"
        Width="{Binding Width, Mode=OneWayToSource, FallbackValue=300}"
        Title="MVVM Light Application"
        Top="{Binding Top, Mode=OneWayToSource}"
        Left="{Binding Left, Mode=OneWayToSource}"
        DataContext="{Binding Main, Source={StaticResource Locator}}">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="13*" />
            <ColumnDefinition Width="265*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="25" />
        </Grid.RowDefinitions>
        <TextBlock FontSize="36"
                   FontWeight="Bold"
                   Foreground="Purple"
                   Text="{Binding Welcome}"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   TextWrapping="Wrap" Grid.Column="1" Margin="19,70,32,70" />
        <Button Grid.Row="1" Content="Capture" Grid.ColumnSpan="2" Command="{Binding Capture}" />
    </Grid>
</Window>

In my view model, the values for the Top, Left work fine, but, the Width and Height properties are both NaN. I have properties like this for each of the attributes I'm binding on:

private double height;

public double Height
{
    set
    {
        height = value;
    }
}

Any idea why the values are coming back NaN? More importantly, what can I do to get the values I'm looking for?

EDIT: I realize this is a bit off, but I need to capture a screenshot of the application from the view model. The view model contains additional properties that I didn't paste, but are necessary in order to implement the desired functionality. For the curious, below is my method for capturing the screen:

private void CaptureScreen()
{
    int x = Convert.ToInt32(left);
    int y = Convert.ToInt32(top);
    int w = Convert.ToInt32(width);
    int h = Convert.ToInt32(height);
    Bitmap image = new Bitmap(w, h);
    Graphics graphics = Graphics.FromImage(image);

    graphics.CopyFromScreen(x, y, x, y, new Size(w, h), CopyPixelOperation.SourceCopy);

    // Do something with the image
}
senfo
  • 28,488
  • 15
  • 76
  • 106

2 Answers2

10

As mzabsky said, you need to use ActualWidth and ActualHeight. The problem here is that they are readonly and you can't do a OneWayToSource Binding on a readonly Dependency Property.

However, there are workarounds.
See the following question: Pushing read-only GUI properties back into ViewModel

The attached behavior provided by Kent Boogaart lets you do this

<Window ...
        SizeObserver.Observe="True"
        SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
        SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}">

or you can try the solution I made for this called PushBinding which can be used to push readonly DP's to the viewmodel. In your case the usage would look like this

<Window ...>
    <pb:PushBindingManager.PushBindings> 
        <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/> 
        <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
    </pb:PushBindingManager.PushBindings> 
</Window> 

You can download a demo project using PushBinding here if you're interested: https://www.dropbox.com/s/eqkmsp0q7hb568z/PushBindingInStyleDemo.zip?dl=0

Fredrik Hedblad
  • 83,499
  • 23
  • 264
  • 266
2

You are supposed to use ActualWidth and ActualHeight as binding targets for one way to source binding, not Height and Width - those are desired size, not actual size. Width and Height is NaN unless you specifically set them to some value.

Of course, another question is that this doesn't seem like proper use of view model.

Matěj Zábský
  • 16,909
  • 15
  • 69
  • 114
  • I would agree this is typically not a valid use case for a view model, but I need the coordinates of the window so that I can capture a screenshot of the window from within my view model. Keep in mind the view I posted is simplified and there is data I need (in addition to the coordinates and size) in order to build the functionality. – senfo Oct 04 '11 at 20:33
  • 1
    `GetWindow()` wouldn't work from the view model. And how do you use `ActualWidth` as binding target? The code `ActualWidth="{Binding Width, Mode=OneWayToSource}"` doesn't work. – svick Oct 04 '11 at 20:34
  • 1
    By the way, the GetWindow() method expects a dependency property, so I'm not sure that will work from my view model. The other problem is ActualWidth is a read-only property, so I can't bind to it in this way, unfortunately. – senfo Oct 04 '11 at 20:36
  • I dont understand, why not to use Window.RenderSize ? – vrrathod Oct 04 '11 at 21:17