Matching size of controls in WPF - c#

This is recurring problem I'm going into. How to match size of elements in WPF? Let's look at the following scenario:
<Grid>
<Popup>
<StackPanel>
<Button x:Name="btn1" />
<Button x:Name="btn2" />
<Button x:Name="btn3" />
</StackPanel>
</Popup>
<Border x:Name="border1">
Ala ma kota
</Border>
</Grid>
Now I'd like the btn1, btn2 and btn3 to be at least as wide as border1; contents of border changes dynamically, so no measuring and hardcoding is acceptable.
How can I achieve that?

Bind the buttons' Width property to the Border's Width property.
Something like:
<Button x:Name="btn1" Width="{Binding ElementName=border1, Path=Width}" />

Related

Resize WPF TextBlock with WrapWithOverflow

I have a textblock on a window which is contained in a scroll viewer. For the textlblock I have set the text wrapping to "WrapWithOverflow". In addition I bound the textblock width property to the actual width of the scroll viewer.
My desired behavior is, that I can resize my window and the textblock should wrap the content. The window should only show the scrollbars when other controls (e.g. the buttons in the example xaml get cut)
Xaml:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DockPanel Margin="5">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="First Button" Margin="0,0,10,0"/>
<Button Content="Second Button"/>
</StackPanel>
<DockPanel>
<TextBlock VerticalAlignment="Center" IsHitTestVisible="False" TextAlignment="Center" TextWrapping="WrapWithOverflow"
Width="{Binding RelativeSource={RelativeSource AncestorType=ScrollViewer}, Path=ActualWidth}" MaxWidth="260">
Just a small line<LineBreak />
This is the long line which will wrap during resize</TextBlock>
</DockPanel>
</DockPanel>
</ScrollViewer>
But I see the scroll bars even befor the buttons get cut. I guess this is because of the margin in the dockpanel which is required in my case.
I guess this is because of the margin in the dockpanel
Correct.
...which is required in my case.
Why? You should move the Margin to the StackPanel:
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
...
...or the buttons:
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="First Button" Margin="5,5,5,5"/>
<Button Content="Second Button" Margin="5,5,0,5"/>
</StackPanel>
...
This is necessary as the margins are included in the ActualWidth that you bind to.

Making a Scrollviewer fill the available space in a DockPanel

I somewhat of a WPF noob and am using a DockPanel to display the text content of an email in a TextBox in a ScrollViewer. The panel has a button bar and area for the email headers at the top, a button bar at the bottom, a panel at the right and the email itself should fill the remaining space dynamically:
[Banner at top, below which is a button bar and box with email headers. At the bottom is another full width button bar. The space between them is divided into a fixed-width panel at the right and a large text box for the email contents.]
http://rowlandsoftware.com/Screenshots/LongEmail.png
(The DockPanel sits inside a grid that provides the bit at the very top and is used when the email is not visible.)
The problem is that if the email is too short or too narrow, the textbox fails to fill the remaining width. In this screenshot, you can see some of the underlying stuff between the box and the panel:
[Between the text box and the panel is a column that is not filled. Part of what lies under it can be seen.]
http://rowlandsoftware.com/Screenshots/TooNarrow.png
The XAML is:
<DockPanel x:Name="EmailCanvas" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Visibility="Collapsed" Height="Auto">
<DockPanel x:Name="topButtonBar" DockPanel.Dock="Top" Height="50" Background="White">
<Button x:Name="btnReturn" DockPanel.Dock="Left" Content="Return to List" Click="btnReturn_Click" />
<Image Width="15" Source="Images/transparent.png" />
<Button x:Name="btnTextToggle" Content="Plain text" Click="btnTextToggle_Click" />
<Button x:Name="btnZoomOut" Content="Zoom Out" />
<Button x:Name="btnZoomIn" Content="Zoom In" />
<Image Width="150" DockPanel.Dock="Right" Source="Images/transparent.png" />
<Button x:Name="btnPrint" DockPanel.Dock="Right" Content="Print" Width="100"/>
<Image DockPanel.Dock="Right" Source="Images/transparent.png" />
</DockPanel>
<StackPanel x:Name="EmailHeaders" Orientation="Horizontal" DockPanel.Dock="Top" Width="Auto" Background="Linen">
<StackPanel Orientation="Vertical">
<TextBlock Text="Subject:" FontSize="16" />
<TextBlock Text="Time Sent:" FontSize="16"/>
<TextBlock Text="Other Recipients: " FontSize="16"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock x:Name="labSubject" Text="Subject:" FontSize="16" />
<TextBlock x:Name="labDateTime" Text="Sent:" FontSize="16"/>
<TextBlock x:Name="labSpare" Text="Other Recipients:" FontSize="14"/>
</StackPanel>
</StackPanel>
<DockPanel x:Name="bottomButtonBar" DockPanel.Dock="Bottom" Height="80" >
<Image Width="150" DockPanel.Dock="Left" Source="Images/transparent.png" />
<Button x:Name="btnDelete" DockPanel.Dock="Left" Content="Delete" />
<Image Width="150" Source="Images/transparent.png" />
<Button x:Name="btnSave" Content="Save" />
<Image Width="150" Source="Images/transparent.png" />
<Button x:Name="btnReply" Content="Reply" />
<Image Width="150" DockPanel.Dock="Right" Source="Images/transparent.png" />
</DockPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Vertical" Background="White" Width="150">
<Button x:Name="btnAttachments" Content="Attachment"/>
</StackPanel>
<ScrollViewer x:Name="eTextViewer" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<TextBox x:Name="eText" Background="White" HorizontalAlignment="Stretch" TextWrapping="Wrap" FontFamily="Verdana" FontSize="18" IsReadOnly="True" />
</ScrollViewer>
<!--Image Width="Auto" Height="Auto" DockPanel.Dock="Bottom" Source="Images/white.png" /-->
<WebBrowser x:Name="eHTML" Height="Auto" DockPanel.Dock="Top" Visibility="Collapsed" />
</DockPanel>
The box is extended only to fill the available height, not the width. The documentation for the DockPanel.LastChildFill Property says,
If you set the LastChildFill property to true, which is the default setting, the
last child element of a DockPanel always fills the remaining space, regardless
of any other dock value that you set on the last child element.
msdn.microsoft.com/en-us/library/system.windows.controls.dockpanel.lastchildfill%28v=vs.110%29.aspx
That does not appear to be the case: in my program it is only filling height, not width. LastFillChild=true is the default, but explicitly setting it to True or False doesn't make a blind bit of difference.
Interestingly, if you set DockPanel.Dock="Top" on the ScrollViewer, it fills the available width but not the height, leaving some of the underlying stuff showing between the text box and the bottom button bar. Again this is contrary to the documentation which says that the dock value set on the last child element is ignored.
Setting HorizontalAlignment="Stretch" to both the ScrollViewer and the TextBox and HorizontalContentAlignment="Stretch" to the ScrollViewer makes not a jot of difference.
So my question is, how can I ensure that the textbox fills the available space in the DockPanel even when the contents of the email are too short?
UPDATE: I have just noticed that if I remove the WebBrowser that is set initially to Visibility="Collapsed", then it works fine. I guess that its presence fools the DockPanel into regarding it as the LastChild even though it is Collapsed.
However, I need to toggle between displaying the email in the web browser and the text box, so removing it isn't an option. Both need to be treated as last child when they are visible. Any ideas?
UPDATE 2: So I wrap the scrollviewer and the web browser in another container, e.g. grid, which is the Last Child. Then I can toggle between the the two but they both fill the space.
Thanks guys. I wouldn't have got there without humiliating myself in public ;)
DockPanel will work, but beware of Collapsed items. Although the documentation says that a collapsed item has no effect on layout, with DockPanel it does if it is the LastChild.
The solution in my case was to add another container as the last child, and place the two items that I wanted to toggle between in that container. Then both will fill the remaining space in the panel.

Force Size on a Viewbox's Children

So I have a rather interesting question. I have a viewbox that has a few elements in it (a custom user control for an image, a canvas, a label, and a textbox). What I want is to try and have all elements scale with the viewbox, but I want the label and the textbox to have a "Max Size." I have tried using a max width and height on these controls but they seem to ignore it. If someone could take a look at my code below an slap me for what I am doing wrong that would be appreciated.
<Viewbox Name="myViewBox" Stretch="Uniform">
<!--Grid used to track mouse movements in this element for other reasons -->
<Grid Name="grdViewboxGrid" MouseMove="trackMouse">
<Canvas Name="cvsViewboxCanvas" MinWidth="270" MinHeight="270"
VerticalAlignment="Top" HorizontalAlignment="Center"
Panel.ZIndex="1" Background="Black"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove">
<Grid>
<!--Would rather not post here for Intellectual Property reasons-->
<!-- Extension of the image control -->
<CustomImageUserControl />
<Grid>
<Grid Width="{Binding LabelWidthPercentage}"
MaxWidth="50"
Height="{Binding LabelHeightPercentage"
MaxHeight="26"
SnapsToDevicePixels="True" VerticalAlignment="Top"
HorizontalAlignment="Left" Margin="5" IsHitTestVisible="False">
<Label Name="lblViewboxLabel" HorizontalAlignment="Left"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
</Grid>
<Grid>
<Grid Width="{Binding TextBoxWidthPercentage}"
MaxWidth="156"
Height="{Binding TextBoxHeightPercentage}"
MaxHeight="45"
SnapsToDevicePixels="True" Vertical="Top"
HorizontalAlignment="Right" Margin="5" IsHitTestVisible="False">
<Border Style="{DynamicResource CustomBorder}" />
<Grid>
<Textbox Name="txtViewboxTextBox" Text="{Binding txtViewbox}" />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Canvas>
</Grid>
</Viewbox>
If I am not including something that is needed please let me know and I will update my question. Any help would be greatly appreciated this is now day 4 on this issue sadly :-(
I am not sure why you need so many overlapping Grids, but I hope that I can answer your question nevertheless:
In order to have the label left of the text box and to assign a maximum width to each of these two controls, use a Grid with two columns and set the MaxWidth property for each column. Then, assign the label to the left column (the one with index 0) and assign the text box to the right column (index 1). The corresponding code fragment looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="30"/>
<ColumnDefinition MaxWidth="156" MinWidth="30"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" x:Name="lblViewboxLabel" HorizontalAlignment="Left" Foreground="Yellow"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
<TextBox Grid.Column="1" x:Name="txtViewboxTextBox" Text="{Binding txtViewbox}" Background="Orange"/>
</Grid>
I also have assigned a MinWidth to the right column; this is necessary to make sure that the text box does not completely disappear if it contains no text.

WPF Button with multiple text elements for 10 foot gui

I am trying to create buttons for a 10-foot GUI using WPF. Each button requires a little more data than just a single text string and an image, maybe 2-3 strings located in different positions and some imagery.
I have tried
<Button Height="52" HorizontalAlignment="Stretch" Name="button1" Width="407">
<Button.Content>
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch">
<TextBlock Name="textBloczk2" Text="ABC" TextAlignment="Left" DockPanel.Dock="Left"/>
<TextBlock Name="textBlxock1" Text="CDE" TextAlignment="Right" DockPanel.Dock="Right"/>
</DockPanel>
</Button.Content>
</Button>
But no matter which inner container I use, the button seems to disregard the layout from the DockPanel and the combined text ends up in the middle of the button. Am I doing something wrong or should I be using a different outer container ?
The problem seems to be that the DockPanel's width is so small that the Right and Left panels are the same width as your TextBlocks.
This seems to work as expected (setting the width of the DockPanel):
<Button Height="52" HorizontalAlignment="Stretch" Name="button1" Width="407">
<Button.Content>
<DockPanel LastChildFill="True" Width="300">
<TextBlock Name="textBloczk1" Text="Left" DockPanel.Dock="Left" />
<TextBlock Name="textBlxock2" Text="Right" DockPanel.Dock="Right" />
<TextBlock Name="textBlxock3" Text="Top" DockPanel.Dock="Top" />
<TextBlock Name="textBlxock4" Text="Bottom" DockPanel.Dock="Bottom" />
</DockPanel>
</Button.Content>
</Button>
Try to add "HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" to your button. This way dockpanel will occupy all space inside the button.

How to align multiple StatusBarItems to the right side in XAML?

I have a StatusBar with 4 items in it on my C# application. I basically want to float the last two StatusBarItems to the right. I've tried it by setting them both with HorizontalAlignment="Right", but that did only work for the last item.
<StatusBar Name="statusBar1" Height="23" HorizontalAlignment="Stretch"
VerticalAlignment="Bottom">
<StatusBarItem />
<StatusBarItem />
<StatusBarItem HorizontalAlignment="Right" />
<StatusBarItem HorizontalAlignment="Right" />
</StatusBar>
I started googling and I came up with the following URL:
Blogspot
Is this really the only solution for this, or is there an easier way?
You can take advantage of the fact that the default ItemsPanel for the StatusBar is the DockPanel. The DockPanel will, by default, try to fill the remaining space with the last item. So the last StatusBarItem you add to the StatusBar will fill the remainder of the space. To take advantage of this, you can simply nest StatusBarItems like this:
<StatusBar Name="statusBar1" Height="23" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<StatusBarItem Content="Item 1"/>
<StatusBarItem Content="Item 2" />
<StatusBarItem HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<StatusBarItem Content="Item 3"/>
<StatusBarItem Content="Item 4"/>
<ProgressBar Height="15" Width="50" IsIndeterminate="True" Margin="5,0"/>
</StackPanel>
</StatusBarItem>
</StatusBar>
Note that the HorizontalAlignment of the 3rd StatusBarItem is set to Right so that it's content will be right-aligned.
Of course, you don't have to have Item 3 and Item 4 be StatusBarItems, they could be other controls, such as Buttons or ProgressBar as I've demonstrated above as well. The StatusBarItem is simply a container that wraps items in a StatusBar, similar to how a ComboBoxItem wraps items inside of a ComboBox.
The StatusBar will wrap it's contents in StatusBarItems automatically, if you don't use them, so items 1 and 2 could just as easily be TextBoxes. The primary reason to use StatusBarItems is in the case where you want to control how the StatusBarItem works, like in the 3rd StatusBarItem where it sets the HorizontalAlignment manually, rather than rely on the default.
As mentioned, the default container is DockPanel. As such, you can set as many items as needed to DockPanel.Dock="Right". Just be sure that the fill item is last.
<StatusBar>
<StatusBarItem DockPanel.Dock="Right">
<Slider Width="100" />
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right">
<Label>Zoom: 100 %</Label>
</StatusBarItem>
<StatusBarItem>
<TextBlock>Ready</TextBlock>
</StatusBarItem>
</StatusBar>
You can also set LastChildFill to false and then use DockPanel.Dock=Right just like the solution above, without having to worry about the last item consuming all the space:
<StatusBar>
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel LastChildFill="False" />
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
</StatusBar>
Another interesting way of achieving this is to replace default panel of StatusBar with Grid, which will give you far more control over the layout of items.
<StatusBar Height="30">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock Text="{Binding ProgressMessage, Mode=OneWay}" />
</StatusBarItem>
<StatusBarItem Grid.Column="1">
<ProgressBar Value="{Binding ProgressValue, Mode=OneWay}" Width="100" Height="10" />
</StatusBarItem>
<StatusBarItem Grid.Column="2">
<Ellipse Width="12" Height="12" Stroke="Gray" Fill="Red" />
</StatusBarItem>
</StatusBar>

Categories