WPF Control actual size - wpf

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.

Related

No Canvas.ZIndex or SetZIndex() property on WPF Canvas

I'm trying to change the Z order of components on my WPF canvas, but it doesn't seem to exist as a XAML property or method in the code behind.
Here's my XAML:
<UserControl x:Class="FrontendUI.Controls.RadialTracker"
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:local="clr-namespace:FrontendUI.Controls"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="500">
<Grid >
<Canvas x:Name="TrackerCanvas">
</Canvas>
</Grid>
At first I was trying to do this programmatically:
Then I tried adding a Path using XAML and setting the Canvas.ZIndex, which also didn't exist. There was the Panel.ZIndex though, but I'm assuming this is referring to something higher in the UI (the grid, or User control itself?)
Not sure how to proceed. Does anyone know why this is happening?
Canvas inherits from Panel. Panel.SetZIndex() is a static method and Panel.ZIndex is an attached property that is why you are not seeing it in the xaml.
To set ZIndex for child components in xaml.
<Canvas >
<Button Content="Button" Panel.ZIndex="1" />
</Canvas>
To set Zindex for child components in code behind.
Canvas.SetZIndex(control, index);
You said "There was the Panel.ZIndex though, but I'm assuming this is referring
to something higher in the UI (the grid, or User control itself?)"
Your Assumption is wrong. To achieve your requirement you need to set the Panel.ZIndex
You asked : But they all use Canvas.ZIndex, Does anyone know why this is happening?
Because ZIndex is an attached property (and not a regular dependency property).

How to disable resizing of user control in WPF

I have Usercontrol.I want to disable its resizing.
The usercontrol is:
<UserControl x:Class="DocumentUpload"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerikGrid="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:telerikGrid1="clr-namespace:Telerik.Windows.Controls.GridView;assembly=Telerik.Windows.Controls.GridView"
xmlns:telerikInp="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Input"
xmlns:telerikNav="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation"
xmlns:telerikData="clr-namespace:Telerik.Windows.Data;assembly=Telerik.Windows.Data"
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="auto" Width="auto" MaxWidth="520">
I got to know that there is property called
ResizeMode="NoResize"
.But it is not available in UserControl.Any suugestion?
You have Width and Height set to Auto, so I guess you want to allow the control to take as much space as needed but not more.
Also, UserControl is not resizing by itself, but depends upon the layout that it's part of.
So, the quickest way to fix your issue would be to set HorizontalAlignment="Left" and VerticalAlignment="Top". But you should consider the whole layout of your application and how the UC is affected by-/affects on other components of the UI.
Then the Parent property of your UserControl is holding the Window instance. Most of times, it will be NavigationWindow. Try the below code in loaded event of your UserControl and it will work.
((NavigationWindow)this.Parent).ResizeMode = ResizeMode.NoResize

UI elements obscured from UIAutomation by UserControl

I have an automation client that uses the AutomationElement.FromPoint method to get the AutomationElement under the cursor:
AutomationElement element = AutomationElement.FromPoint(point);
Typically this works great, but I keep running into a problem with certain WPF applications. The problem occurs when there is a UserControl on the same UI level as another important UI element.
For example:
<Window x:Class="wpfTestApp.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" xmlns:c="clr-namespace:wpfTestApp">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource NameListData}}"
Height="300" HorizontalAlignment="Left" Margin="10,10,0,0" Name="listBox1"
VerticalAlignment="Top" Width="153" >
</ListBox>
<UserControl Name="exampleUserControl">
<TextBlock Visibility="Hidden">Loading...</TextBlock>
</UserControl>
</Grid>
</Window>
When I attempt to point to any of the listbox items (or even the listbox itself), all I get is the "exampleUserControl".
I know that there are other methods for obtaining AutomationElements that don't depend on location, but in this case that is my only option, since we are trying to get the element under the cursor. The problem is, in this case, the important element (i.e. the listbox items) are covered up by this unimportant item ("exampleUserControl" containing "Loading..." text).
Is there any alternative to the FromPoint method, or some way that I can get it to ignore such elements?
There are several methods can find/search the Element.
You can find these interfaces on the MSDN http://msdn.microsoft.com/en-us/library/ms606809
For instance:
AutomationElement.FindFirst - http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.findfirst
AutomationElement.FindAll - http://msdn.microsoft.com/en-us/library/system.windows.automation.automationelement.findall
// Sample: Find the first Element and the classname = ""SciCalc"
AutomationElement calcRoot = AutomationElement.RootElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.ClassNameProperty, "SciCalc"));
"AutomationElement.RootElement" is the parent of all the currently opened windows and controls.
For improve the performance you can find the target window first and then scan the controls on the AutomationElement of the Target window.
For instance: You can find and create the target WPF window via "AutomationElement.FindFirst" or "AutomationElement.FromHandle" first and search you listbox on the target window.
The clean solution to your problem is to set Visibility="Hidden" to the UserControl instead of the TextBlock

WPF Window.Measure not setting the DesiredSize

I am trying to setup a printable view of my WPF window and I'm having a very hard time getting the content to size properly (surprise!).
Specifically, Measure doesn't seem to be setting the DesiredSize property of my window.
The basic approach I'm trying to take is that I've created a separate Window object (PrintView) that contains the controls I wish to print. I'm now programmatically attempting to instantiate the Window and then send it to the printer.
PrintView printView = new PrintView(m_Model.Clone() as MyModel);
printView.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
printView.Arrange(new Rect(new Point(0, 0), printView.DesiredSize));
thePrintDialog.PrintVisual(printView, "Strategy");//blank page every time
Unfortunately, printView.DesiredSize on line 3 there is always 0,0 after a call to .Measure, which causes the printed page to be blank, of course. I thought that .Measure is supposed to set that property. Interestingly, if stick a call to .ShowDialog() in there instead of the Measure & Arrange, it works properly, so as I understand it, that means the issue is that the layout pass has not occured. I just can't figure out how to force it to occur.
Any help would be greatly appreciated.
EDIT: POSTING CODE FOR PrintView
public PrintView(MyModel p_Model)
{
InitializeComponent();
TabSetContent1.Initialize(p_Model, p_Model, new Model.Workbook());
TabCompareContent1.Initialize(p_Model, new Model.Workbook());
}
And the XAML:
<Window x:Class="Cmi.Analytics.DecisionPathway.Ui.UserControls.Printing.PrintView"
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:components="clr-namespace:MyNamespace">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<components:TabSetContent x:Name="TabSetContent1" Grid.Row="0" IsPrintView="true" />
<components:TabCompareContent x:Name="TabCompareContent1" Grid.Row="1" IsPrintView="true" />
</Grid>
</Window>
The calls to Initialize for each of the usercontrols kick off lots of stuff for binding, etc... There's quite a bit of code involved. These exact same controls are used on the main.xaml page with the same calls to .Initialize() and everything works as expected.
Maybe a call to UpdateLayout, at the point where showing the dialog worked, might do the trick.

How to get a WPF window's ClientSize?

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().

Resources