How to get a WPF window's ClientSize? - wpf

In WinForms, Form had a ClientSize property (inherited from Control), which returns the size of its client area, i.e., the area inside the title bar and window borders.
I'm not seeing anything similar in WPF: there's no ClientSize, ClientWidth, ClientHeight, GetClientSize(), or anything else that I can think to guess the name of.
How do I go about getting the client size of a WPF Window?

One way you could do it is to take the top most child element, cast this.Content to its type, and call .RenderSize on it, which will give you its size.
<Window x:Class="XML_Reader.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="400" Width="600" WindowStyle="SingleBorderWindow">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Grid>
</Window>
((Grid)this.Content).RenderSize.Height
((Grid)this.Content).RenderSize.Width
edit:
as Trent said, ActualWidth and ActualHeight are also viable solutions. Basically easier methods of getting what I put above.

var h = ((Panel)Application.Current.MainWindow.Content).ActualHeight;
var w = ((Panel)Application.Current.MainWindow.Content).ActualWidth;

One way to do it is with the code below. XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<Canvas>
</Canvas>
</Window>
C#:
using System.Windows;
using System.IO;
using System.Xml;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
double dWidth = -1;
double dHeight = -1;
FrameworkElement pnlClient = this.Content as FrameworkElement;
if (pnlClient != null)
{
dWidth = pnlClient.ActualWidth;
dHeight = pnlClient.ActualHeight;
}
}
}
}

I used a Grid with VerticalAlignment=Top. As a result the Grid unfortunately didn't fill the parent Window anymore (which is its default behaviour, but the VerticalAligment property spoils it).
I solved it by putting an empty Border around the Grid. This border fills the complete content of the window, it has the same dimensions as the default border that a wpf window has anyways.
To get the Grid to fill the main window, I used the binding:
<Border BorderThickness="0" x:Name=Main>
<Grid VerticalAlignment="Top" Height="{Binding ElementName=Main, Path=ActualHeight}"> ...
</Grid>
</Border>

All the suggested solutions are based on the idea to use the size of Windows.Content to know what is the actual size available within the window, like this:
var h = ((Panel)Application.Current.MainWindow.Content).ActualHeight;
This of course only works if Window.Content is not null. Which is a problem if you want to set Window.Content from your code and you already then need to know exactly how much space is available.
The other problem is that the above code only provides the available space once a first layout cycle has completed (i.e. in the Window_Loaded event). But what do you do if you need to know the available space during the first layout cycle, for example because you draw to the window during Windows.OnRender() ?
The first control in the visual tree of any Window is always a Border, even if Window.Content is null. Interestingly, Border.RenderSize has already a value, even when RenderSize.ActualSize might still be zero. I guess the reason is that the size of the Border does not depend on Window.Content, but only on the size of the window (unless, of course, if Window.SizeToContent is used).
I recommend to place your code into the Window.SizeChanged event. Because each time the Window size changes, your content needs to change too. You cannot use the size provided in the event parameters, which gives you the size of the complete window, but you can get the the available size within the window like this:
var h = ((Border)GetVisualChild(0)).RenderSize.Height;
You can use that line of code also if you override Windows.OnRender().

Related

Control Focus in dialog box with Prism 7.x and 8.x?

I need to set a TextBox being focused when the dialog box was opened. It was working with Prism 6.x; but is no longer working since 7.x and 8.x. The framework difference is that Prism 6.x uses InteractionRequest for dialog box; while Prism 7/8 uses the dialog service. Does Prism 7 and 8 also introduce some new approaches related to dialog box? Following is the code snippet of XAML setting FocusManager:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
It does not function any more. Does it related to the dialog service?
The dialog service starting from Prism 7.2.x is completely different from the legacy interaction requests in prior versions, so it is hard to tell what exactly was the breaking change. Setting the focus manager property on the root element of the UserControl instead of on the UserControl could solve this the issue, but it might not work under other circumstances, read below why.
<UserControl x:Class="FeatureModule.Views.MyDialogView" ...>
<Grid FocusManager.FocusedElement="{Binding ElementName=edit}">
<!-- ...your markup -->
</Grid>
</UserControl>
I think the issue is more general. WPF has two different focus concepts, logical and keyboard focus. What you want to set on the TextBox is keyboard focus, but the FocusManager only sets logical focus.
Logical focus pertains to the FocusManager.FocusedElement within a specific focus scope.
The important part is the relationship between logical and keyboard focus.
An element with logical focus does not necessarily have keyboard focus, but an element with keyboard focus will have logical focus.
This means, even if you set the logical focus on a different element it might not work.
Keyboard focus pertains to the element that is currently receiving keyboard input. There can be only one element with keyboard focus.
Unfortunately, the Keyboard.FocusedElement property has only a getter and is not a dependency property, so you cannot bind to it, but you can call the Keyboard.Focus method instead. In order to avoid code-behind, you can either create a small behavior or simply an attached property like this:
public static class KeyboardFocus
{
public static readonly DependencyProperty ElementProperty = DependencyProperty.RegisterAttached(
"Element", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(null, OnElementChanged));
public static FrameworkElement GetElement(DependencyObject dependencyObject)
{
return (FrameworkElement)dependencyObject.GetValue(ElementProperty);
}
public static void SetElement(DependencyObject dependencyObject, FrameworkElement value)
{
dependencyObject.SetValue(ElementProperty, value);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is FrameworkElement frameworkElement)
frameworkElement.Loaded += OnLoaded;
}
private static void OnLoaded(object sender, RoutedEventArgs e)
{
var frameworkElement = (FrameworkElement)sender;
frameworkElement.Loaded -= OnLoaded;
Keyboard.Focus(frameworkElement);
}
}
This attached property can be used on any FrameworkElement, not only TextBox. You can customize it to fit your needs. In XAML apply it to the root element of your UserControl, not the UserControl itself, otherwise the binding element might not be found e.g.:
<UserControl x:Class="FeatureModule.Views.MyDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:local="clr-namespace:FeatureModule.Views"
prism:ViewModelLocator.AutoWireViewModel="True"
FocusManager.FocusedElement="{Binding ElementName=edit}"
mc:Ignorable="d" d:DesignHeight="140" d:DesignWidth="400">
<Grid local:KeyboardFocus.Element="{Binding ElementName=edit}">
<!-- ...your markup. -->
</Grid>
</UserControl>
Alternatively, you can set the property on the TextBox itself.
<TextBox local:KeyboardFocus.Element="{Binding RelativeSource={RelativeSource Self}}"/>

Why is RoutedEventArgs.Handled ignored between TouchDown and Click

I am trying to support both TouchDown and Click with the same event.
You will notice that the following code, instead of toggling between green and red, just flashes red and goes back to green. As far as I can tell, this is because the click event is ignoring the Handled property of the RoutedEvent. I need this to work with both Touch and Mouse.
XAML:
<Window x:Class="CodeSpace.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Background="LightGreen">
<Grid>
<Button Content="Touch Me" HorizontalAlignment="Center" VerticalAlignment="Center" Height="75" Width="75" Click="OnClick" TouchDown="OnClick"/>
</Grid>
</Window>
Code behind:
using System.Windows;
using System.Windows.Media;
namespace CodeSpace.WPF
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnClick(object sender, RoutedEventArgs e)
{
Background = (Background.Equals(Brushes.LightGreen)) ? Brushes.LightCoral : Brushes.LightGreen;
e.Handled = true;
}
}
}
Note: the reason I am not just using Click alone (which is supposed to work with Touch) is because with my specific case when I do that, the first time the button is touched nothing happens. All subsequent touches work just fine. This is a separate issue that I can't ask about because I cannot reproduce it in a simple code example. Also, this behavior only happens when windows is set to 120 DPI. Everything works just fine in 96 DPI. I have no idea!
The click event comes as a result of MouseUp/TouchUp rather than *Down (unless you change the ClickBehavior of the button). This answer has a good approach to globally blocking the promotion of mouse events to touch events for an element: https://stackoverflow.com/a/19540120/518955

How to set WPF application windowstartuplocation to top right corner using xaml?

I just want to set windowstartuplocation to top right corner of desktop. I saw this thread with same question:
Changing the start up location of a WPF window
I want my application to start in top right corner,where right refers to MY RIGHT SIDE(not as if my desktop is a person looking at me and ITS RIGHT SIDE).So,
1.) Setting left and top to 0 only is not a solution(brings app to left side not right)
2.) I tried using SystemParameters.PrimaryScreenWidth, but I can't perform operation to subtract the width of my app from this value at binding time.
Is there a way I can do it without going into much complexity?
Is there a way I can do it without going into much complexity?
The simplest way would be to setup your start location manually, and then set the Left property in code behind:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="500" Width="500"
WindowStartupLocation="Manual"
Top="0">
</Window>
In your code behind:
public Window1()
{
InitializeComponent();
this.Left = SystemParameters.PrimaryScreenWidth - this.Width;
}
This is one place where I feel the simplicity of doing it in code outweights any disadvantages of introducing code behind.
By using WorkArea.Width make sure the width of the taskbar is taken into account (when placed on a side, left or right). By using this.Width or this.MaxWidth make sure your window never slips outside the work area, but both require their value be set (in xaml or in code behind) to not produce the value NaN (Not a Number) for this.Left.
public MainWindow()
{
InitializeComponent();
this.Left = SystemParameters.WorkArea.Width - this.MaxWidth;
}
<Window
... more code here ...
WindowStartupLocation="Manual" Top="0" MaxWidth="500" >

How do you measure the size of a Path object in WPF/Silverlight?

I am working on a mapping system in WPF and have come across some weird behavior when dealing with Path elements. We have a canvas element with path objects within it that show locations on a map. When I try to measure their size or location through code, their size is always includes their distance from the origin (0,0) and their location is always at the origin. I cannot figure out how to measure the actual visible area of the path itself. Here is an example:
Now the path as you can see is only roughly a hundred pixels in size, but when reading the AcutalWidth/ActualHeight properties it includes its distance from the top/left corner too. I also tried the .Measure() method on the path and got the same result. Are there any special tools to use that can actually measure the visible area of paths?
Here is the code of this example for reference:
<Window x:Class="WPF_Testing_Area.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Canvas x:Name="Wrapper" Width="2185.922" Height="3091.486">
<Path Fill="#FF9EC99F" MouseLeftButtonDown="Path_MouseLeftButtonDown" Stroke="Black" StrokeThickness="1.5" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeDashCap="Round" StrokeLineJoin="Round"
Data="F1M1501.916,677.412C1480.471,683.851 1457.263,676.647 1443.234,659.196 1441.36,656.865 1439.083,654.889 1436.51,653.362L1436.805,800.23 1533.736,819.855C1537.515,820.62,1541.335,821.166,1545.177,821.49L1501.916,677.412z"/>
</Canvas>
</Grid>
</Window>
And the code behind:
using System.Windows;
using System.Windows.Input;
using System.Windows.Shapes;
namespace WPF_Testing_Area
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Path_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var path = sender as Path;
var size = new Size(path.ActualWidth, ActualHeight);
MessageBox.Show(size.ToString());
// try to measure the shape
path.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
MessageBox.Show(path.DesiredSize.ToString());
}
}
}
I'd try subtracting the value of the path's Canvas.Left from the value of path.ActualWidth (same for Canvas.Top and path.ActualHeight). I'd think that would give you the correct size.
This will prove to be quite tricky because if the path contains an arc and the arc extends beyond the coordinates/point that were used to define the path, it will be hard to calculate a bounding box.
See: WPF: How to get the true size (Bounding Box) of shapes and Getting the true visual bounding box of a WPF element?
This will be more trick I think.Can you refer these links.
http://msdn.microsoft.com/en-us/library/865tf5y6%28v=vs.80%29.aspx
http://forums.silverlight.net/t/22300.aspx/1

WPF Control actual size

I have this piece of XAML code:
<Window x:Class="SizingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Label x:Name="theLabel" Width="Auto">A very large label with a lot of text</Label>
</Grid>
</Window>
In the code behind, I'm trying to get the label's actual width, I thought
theLabel.ActualWidth
would do the trick, but after trying this code:
public Window1()
{
InitializeComponent();
double width = theLabel.ActualWidth;
}
The value of width is 0, I also checked with theLabel.Width, which returns NaN, theLabel.DesiredSize.Width, which also return 0. What can I use to find the real width of the label?
Thank you.
ActualWidth isn't set until the component's parents (and possible children) are laid out.
To get a component's ActualWidth, you'll need to wait for a layout pass to complete. Listen to the Loaded event, as its not called until after the first layout pass.

Resources