How to achieve CSS Position Absolute - Position Relative combination in WPF? - wpf

I have a need to reproduce this CSS in WPF/XAML:
<div style="position: relative;">
<div style="position: absolute; top: 0; left: 0;">Foo bar</div>
<div style="position: absolute; top: 0; left: 0;">Foo bar</div>
</div>
In essence, I need to have two elements positioned on top of each other within their container.
So far I have:
<StackPanel>
<TextBlock Text="Foo bar">
<TextBlock Margin="0,-16,0,0" Text="Foo bar">
</StackPanel>
The problem with above is that it does not scale. I don't want to hard code any margin figures.

Place then on the same cell of a grid:
<Grid>
<TextBlock Text="Foo bar">
<TextBlock Text="Bar foo">
</Grid>
Note: Since there's no RowDefinitions or ColumnDefinitions specified, the grid have a default 1 row / 1 column. Since the elements don't have the property Grid.Row or Grid.Column set, they are placed on the 0,0 cell

When I need to do non-pixel positioning, I usually put my items in a grid with rows or columns on the sides with a percentage or "star" value for padding. That does scale.
So something like:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Height="10*"/>
<ColumnDefinition Height="80*"/>
<ColumnDefinition Height="10*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="FooBar" Grid.Column="1"/>
</Grid>
Will render a textbox whose left edge is 10% from the left, no matter what the zoom.

Related

WPF: Calculating the size of a Grid Column

I have a grid that contains three columns.
<Grid Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="2*" Name="ManualControlsSplit" />
</Grid.ColumnDefinitions>
The first column contains a Grid and a chart.
The second column contains a GridSplitter.
The third column contains a StackPanel which contains a number of TextBlocks, Buttons and Grids containing TextBlocks and Buttons. Text size is dynamic and based on translation resources.
I need to calculate the minimum width that the contents of the third column would ideally like to be able to be drawn into so that the contents are not clipped.
My knowledge of WPF is limited to what I can google so any help would be appreciated.
With layout in WPF, you can allow one if not more of the Grid.Columns to be <ColumnDefinition Width="Auto"/>. This essentially tells WPF to allow as much space as the contained controls want. The use of Auto cascades too, so in StackPanel you refer to, you can make that controls width Auto as well, likewise with the items it contains; if the stack panel merely contains a TextBlock (via some template or whatever), then you can also set this width to Auto and the width with set itself according to the contained text.
<Grid Background="AliceBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
...
<StackPanel Grid.Column=2
Width="Auto">
<TextBlock Width="Auto"
Text="This is TextBlock"/>
...
</StackPanel>
...
</Grid>
In this case the Text of the TextBlock sets the width of the StackPanel, which in turn sets the width of the 3rd grid column.
I hope this helps.
My WPF XAML setup is as follows (bits removed to keep this simple):
<UserControl ....>
<Grid Background="AliceBlue" Name="TopGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="12*" Name="Graph" />
<ColumnDefinition Width="5" Name="GridSplitter" />
<ColumnDefinition Width="2*" Name="ManualControlsSplit" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Background="AliceBlue"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- A Graph and simple controls -->
</Grid>
<!-- GRID SPLITTER -->
<GridSplitter Grid.Column="1" Width="5"
HorizontalAlignment="Stretch" Name="Splitter" />
<Label Grid.Column="1" Content="⁞" Foreground="White"
Background="DarkGray"
VerticalAlignment="Center" FontSize="26" FontWeight="Bold"
IsHitTestVisible="False"/>
<!-- end GRID SPLITTER -->
<StackPanel Grid.Column="2" Grid.Row="0" Margin="5"
Name="TemperatureControls">
<!-- Load of Controls -->
</StackPanel>
</Grid>
</UserControl>
To calculate the desired width I use the following code:
// get my UserControl object
var manualControlView = userControl as HeatingController.Views.ManualControlView;
// Query the current width of THIRD column
double actualWidth = manualControlView.ManualControlsSplit.ActualWidth;
// Set up a BIG size (this has to be bigger size than the contents of the
// THIRD column would ever need)
Size size = new Size(400, manualControlView.TopGrid.ActualHeight);
// Ask WPF layout to calculate the Desired size.
manualControlView.TemperatureControls.Measure(size);
double width = manualControlView.TemperatureControls.DesiredSize.Width;
if (actualWidth <= width)
{
// Too small - do something
}
else
{
// big enough - do something else.
}
The variable 'width' now contains the value I wanted to calculate.

How to bind Font Size to variable Grid Size

I have a grid with 2 columns and 2 rows. A single character (Unicode U+2699) was placed inside the bottom right grid field. It looks like this:
I´d like the character to automatically adjust its font size to fit the grid field it has been placed in (in this case it should be bound to the height of the second grid row, but since in some cases it could be unclear if the height or the width of the grid is smaller, it would be also nice to know how to bind to the lowest of those 2 values).
My implementation so far is something like the following (I simplified it a bit for this example):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition x:Name="heightToBind" Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button FontSize="{Binding ElementName=heightToBind, Path=Height.Value, Mode=OneWay}" Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
The problem here is, that it only works if the height is a fixed value inside the RowDefinitions. I want it to work with the following definition:
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition x:Name="heightToBind" Height="*"/>
</Grid.RowDefinitions>
As a bonus question I´d also be interested why it might be that the character is placed too low so it is getting cut off at the bottom (I tried VerticalAlignment="Center" for the button but with no effect).
You can try using a ViewBox as the button's content:
<Button Grid.Row="1" Grid.Column="1">
<Button.Content>
<Viewbox StretchDirection="Both" HorizontalAlignment="Stretch">
<TextBlock Text="⚙" />
</Viewbox>
</Button.Content>
</Button>
A ViewBox can stretch and scale his child to fill all the available space...
You could try binding to the ActualHeight instead of the Height:
<Button FontSize="{Binding ElementName=heightToBind, Path=ActualHeight.Value, Mode=OneWay}"
Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
This should work.
The * on the grid definition means take the available space as the height so it's only determined when the page layout has been prepared for layout. If the height is either unset or changed then the real height is returned in the ActualHeight property.

How to prevent TextBox from stretching vertically inside Grid

In the markup below, the text box vertically to fills the entire grid row, no matter how big the row's height is. For a single line text box this does not look good. I need it to center vertically instead and have the height just enough to fit the current font. Setting the Height property on the text box helps but I do not want to hard code the height in case the font changes.
<Grid FocusManager.FocusedElement="{Binding ElementName=TitleBox}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Name="TitleBox"
Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" />
<Button Command="{Binding CreateCommand}"
IsDefault="True"
Grid.Column="1">Create</Button>
</Grid>
Set the VerticalAlignment of the text box to Center. The default is Stretch which explains why it stretches to fill the entire grid cell:
VerticalAlignment="Center"

Align items in a stack panel?

I was wondering if I can have 2 controls in a horizontal-oriented StackPanel so that the right item should be docked to the right side of the StackPanel.
I tried the following but it didn't work:
<StackPanel Orientation="Horizontal">
<TextBlock>Left</TextBlock>
<Button Width="30" HorizontalAlignment="Right">Right<Button>
</StackPanel>
In the snippet above I want the Button to be docked to the right side of the StackPanel.
Note: I need it to be done with StackPanel, not Grid etc.
You can achieve this with a DockPanel:
<DockPanel Width="300">
<TextBlock>Left</TextBlock>
<Button HorizontalAlignment="Right">Right</Button>
</DockPanel>
The difference is that a StackPanel will arrange child elements into single line (either vertical or horizontally) whereas a DockPanel defines an area where you can arrange child elements either horizontally or vertically, relative to each other (the Dock property changes the position of an element relative to other elements within the same container. Alignment properties, such as HorizontalAlignment, change the position of an element relative to its parent element).
Update
As pointed out in the comments you can also use the FlowDirection property of a StackPanel. See #D_Bester's answer.
Yo can set FlowDirection of Stack panel to RightToLeft, and then all items will be aligned to the right side.
For those who stumble upon this question, here's how to achieve this layout with a Grid:
<Grid>
<TextBlock Text="Server:"/>
<TextBlock Text="http://127.0.0.1" HorizontalAlignment="Right"/>
</Grid>
creates
Server: http://127.0.0.1
Could not get this working using a DockPanel quite the way I wanted and reversing the flow direction of a StackPanel is troublesome. Using a grid is not an option as items inside of it may be hidden at runtime and thus I do not know the total number of columns at design time. The best and simplest solution I could come up with is:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<!-- Right aligned controls go here -->
</StackPanel>
</Grid>
This will result in controls inside of the StackPanel being aligned to the right side of the available space regardless of the number of controls - both at design and runtime. Yay! :)
This works perfectly for me. Just put the button first since you're starting on the right. If FlowDirection becomes a problem just add a StackPanel around it and specify FlowDirection="LeftToRight" for that portion. Or simply specify FlowDirection="LeftToRight" for the relevant control.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" FlowDirection="RightToLeft">
<Button Width="40" HorizontalAlignment="Right" Margin="3">Right</Button>
<TextBlock Margin="5">Left</TextBlock>
<StackPanel FlowDirection="LeftToRight">
<my:DatePicker Height="24" Name="DatePicker1" Width="113" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" />
</StackPanel>
<my:DatePicker FlowDirection="LeftToRight" Height="24" Name="DatePicker1" Width="113" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" />
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Left" />
<Button Width="30" Grid.Column="1" >Right</Button>
</Grid>
If you are having a problem like the one I had where labels were centered in my vertical stack panel, make sure you use full width controls. Delete the Width property, or put your button in a full-width container that allows internal alignment. WPF is all about using containers to control the layout.
<StackPanel Orientation="Vertical">
<TextBlock>Left</TextBlock>
<DockPanel>
<Button HorizontalAlignment="Right">Right</Button>
</DockPanel>
</StackPanel>
Vertical StackPanel with Left Label followed by Right Button
I hope this helps.
for windows 10
use relativePanel instead of stack panel, and use
relativepanel.alignrightwithpanel="true"
for the contained elements.
Maybe not what you want if you need to avoid hard-coding size values, but sometimes I use a "shim" (Separator) for this:
<Separator Width="42"></Separator>

Filling Browser Window with Silverlight Application

I want my Silverlight app to fill the entire browser window. I've set the plugin object width and height to 100%, and set my LayoutRoot container's height and width to Auto, but still no luck. Any suggestions?
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:Silverlight ID="Silverlight1" runat="server"
Source="~/ClientBin/Client.xap"
MinimumVersion="2.0.30818.0"
AutoUpgrade="true"
Height="100%"
Width="100%">
</asp:Silverlight>
</form>
<UserControl
x:Class="Client.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto"
Width="Auto">
<Grid
x:Name="LayoutRoot"
Background="#084E85"
ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="280" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="80" />
<RowDefinition Height="Auto" />
<RowDefinition Height="600" />
</Grid.RowDefinitions>
...Remaining content here...
</Grid>
</UserControl>
Disclaimer: I searched for an answer first, finding this thread. However, as you can see by my code that isn't working for me.
First, I don't set the height/width in the user control. Instead, I set the DesignHeight and DesignWidth (in the "http://schemas.microsoft.com/expression/blend/2008" namespace) and I set the alignment to stretch
<UserControl ...
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
d:DesignHeight="1050" d:DesignWidth="1680">
In my HTML, I set the Height and Width to 100% like this...
<div style="height: 100%; width: 100%; position: fixed;">
<asp:Silverlight runat="server" Source="~/ClientBin/My.xap" ID="MyId"
Width="100%" Height="100%" />
</div>
At that point, everything works for me to have it take the entire window.
Remove the Height and Width attributes from the :
<UserControl
x:Class="Client.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Auto Height and Width with resize the window to fit the content. If you remove the Height and Width attributes, your Silverlight Application will grow to fit the window.
Hmm
I'm thinking your Grid is set-up to be as small as possible (no "star" column or row).
Can you try with one more column and row with a "star" size ?
as other have pointed out, you also need to remove the "auto" sizes, since they auto-size to content.
also try to set a background color on the page, so you see where it actually extends.
If you've got a fairly complex HTML layout and can't rely on fixed positioning then you can make the #silverlightControlHost div resize using the following:
private bool _hasResized;
protected override Size ArrangeOverride(Size finalSize)
{
if (!_hasResized)
{
HtmlPage.Document.GetElementById("silverlightControlHost").SetStyleAttribute("height", finalSize.Height.ToString());
_hasResized = true;
}
return base.ArrangeOverride(finalSize);
}
You can put this inside MainPage.cs or if you have nested UserControls, then the control that requires the most height. I'm also using the following XAML and the default HTML Visual Studio provides
<UserControl ...
VerticalAlignment="Stretch"
Height="Auto"
Width="Auto">
I haven't tested it without these settings, as far as I know Auto is the default.

Resources