WPF Dockpanel first child uses remaining space - wpf

In a window I have there is a list of DockPanels to specify a couple of files. Each DockPanel has a TextBox (for the path) and a button (to browse for a file).
I've recreated a simple WPF page to demostrate the problem here:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="150"
Height="22">
<DockPanel>
<TextBox HorizontalAlignment="Stretch"/> <!-- path to file -->
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
</DockPanel>
</Page>
The problem is that I want the button to the right of the textbox, but that causes the textbox to be really small since the LastChild of the DockPanel is the button it uses up the remaining space. Ive tried to change this by shuffling them around and setting LastChildFill="False" but that only causes the button to be small again, not making the TextBox wide (even with HorizontalAlignment="Stretch").
The reason I want the controls in that order is I want the user to reach the TextBox before the Button when using tab to navigate in the window. I looked into setting TabIndex but it feels hacky, favorite features of WPF is that tabindex are in the order the conrols are defined in the XAML. Not to mention that I probably would have to manually set TabIndex on everything in the Window.
To me, it seems that the setting of TextBox.HorizontalAlignment isn't respected. How can I make the first control use as much space as it can but still preserve tab order?

Make it like this:
<DockPanel LastChildFill="True">
<Button Content="..." DockPanel.Dock="Right"/> <!-- button to browse for file -->
<TextBox DockPanel.Dock="Left" HorizontalAlignment="Stretch"/> <!-- path to file -->
</DockPanel>

If you don't want the behavior of DockPanel, don't use a DockPanel.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox />
<Button Content="..." Grid.Column="1"/>
</Grid>

Related

WPF tab control with grid and shared content

I have a wpf tab control in which I would like to have two columns. One column would always show a graph control that will be used to display data depending on the tab selected - but it will always be the same graph control.
Unfortunately, due to the design of the graph control, it is not possible to have more than one graph control mainly because the performance is dismal. I have tried that and it does not work properly.
The other column would show items like combo boxes, radio buttons, etc., that are specific to the selected tab - an example is below
I have also had the tab control in the right-hand column, but the layout of the individual tabs is congested in the right-hand column making for a less than ideal user experience.
Right now, I have the tab control hosted in a grid that has two columns with the column span set to two. For the right-hand pane, I have various group boxes and I control the visibility of those group boxes with triggers using the IsSelected property of the corresponding tab item. This, however, is causing other problems that I have traced to the visibility of the problematic controls.
What I would like to do is modify the template of the control so that I can host all the present controls within the tab control so that the graph control always displays on the left, and that the content of the right-hand tab is controlled by the selected tab.
I figure that doing this will involve either the control template or another template for the tab control, however, I have been unable to find anything like this so far. Is there a way to do something like this and if so, is there a guide to doing so or some hints as to how I might accomplish this?
Thanks.
The way I would approach this requirement would be something like below. And I would apply a template/style to buttons so that they have a look of a TabHeader.
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<StackPanel.Resources>
<ControlTemplate x:Key="ButtonTemplate">
<!--Template style-->
</ControlTemplate>
</StackPanel.Resources>
<Button>Root bending</Button>
<Button>S-N curve bending</Button>
<Button>S-N curve contact</Button>
</StackPanel>
<Grid Grid.Row="1" Grid.Column="0">
<!--Your graph control goes here-->
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<!--Show/hide these based on buttons-->
<!--Control 1 with combo boxes, radio buttons, etc.-->
<!--Control 2 with combo boxes, radio buttons, etc.-->
<!--Control 3 with combo boxes, radio buttons, etc.-->
</Grid>
</Grid>
you can declare ChartControl as a Resource and use it in every Tab.
To confirm that ChartControl is the same, type something in TextBox and then select another Tab. Text stays the same. Initialization time shown in TextBlock stays the same.
<Window x:Class="XamlApp.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="TabsWindow"
Height="480" Width="640">
<Window.Resources>
<Grid x:Key="IamChartControl" Background="Khaki">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Source={x:Static system:DateTime.Now}}" Margin="5"/>
<TextBox Text="hello" Grid.Row="1" Margin="5"/>
</Grid>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding Source='12345'}">
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Content="{StaticResource IamChartControl}"/>
<TextBlock Grid.Column="1" Text="{Binding}"/>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Thanks for the suggestions. Given there is a large amount of existing code and since the graph control is not entirely WPF MVVM compliant, the better answer in this case would be the answer posted by Ritesh. Putting the chart in a resource would have required more of a rewrite of the code than I presently have time to do.
However, I figured out the problem that I was seeing - which was some controls were not showing bold text when I thought that they should be. This was entirely my fault.
On each tab for each field, I had multiple different labels that were made visible depending on the result set that the user chooses. It has been a long time since I visited this code, and what I was doing was adding the bold fontweight because it makes the values stand out better.
Embarrassingly, I forgot that I had implemented it this way.
Instead of the multiple different labels approach, I am going use a single label for each field and set the appropriate content binding in a multidata trigger as that will make the somewhat cleaner. Its a pretty complicated app.
I was going to delete this, but others have asked a similar question, however, I think Ritesh's answer is different than the other cases.

How to control button layout in Xaml

Im having trouble controlling the exact layout of a button control with XAML.
It seems that whatever i do the button is of a minimum width.
I have a simple button with only a textblock inside the button. But the button has a lot of margin and padding that i cant seem to get rid of (i know of negative margins and padding).
The things i want to know is:
1. Why in the world was it designed this way.
2. what are the groundrules for controlling the exact layout of a button?
My code is as follows:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"></StackPanel>
<Pivot Grid.Row="1">
<Pivot.Title>
<StackPanel Orientation="Horizontal" Margin="-15,-3,0,0" Background="red" Width="480">
<Button Background="Blue" x:Name="btnStudies" Click="btnMenuItem_Click" Width="20">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Green">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
</StackPanel>
</Pivot.Title>
</Pivot>
</Grid>
I want five buttons in a row but these are already too wide for the screen (windows phone). Changing the width doesnt seem to have any effect (why is it there).
The textBlock control within the button the button is as wide as the text on it, but i dont seem to have any control on the width of the button. In HTML you only have padding or margin when you define it but in xaml it just seems to be there and for me its unclear how to undo that.
*****EDIT*****
After reading Rachel's reply i decided to start from the ground up.
Using the code below i still have no control over how wide the button is because it uses a certain amount of padding that i cant seem to remove. The button has a width of about 110 when i define a width lower than that it doesnt change. Margins and paddings of 0 have no effect at all (dont want to use negative values just yet because that doesnt seem very intuitive). So the code below is very simple but still the button takes up an amount of space that i dont have any control over. I cant imagine a reason why it was designed this way.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<StackPanel Width="300" Background="Red" HorizontalAlignment="Left">
<Button Background="Blue" HorizontalAlignment="Left" Width="100" Margin="0" Padding="0">
<TextBlock Text="Title" Width="Auto" HorizontalAlignment="Left" />
</Button>
</StackPanel>
</Grid>
The type and size of the parent panel containing the control affects the size/layout of the child controls.
In your case, you have a Grid as your parent panel, and a Grid defaults to taking up all available space. In addition, children placed inside the grid default to taking up all available space as well unless you specify otherwise.
So your <Pivot> is being assigned a width equal to Grid.Width, and Pivot.Title sounds like it's being assigned a width equal to Pivot.Width, and StackPanel is being assigned a width equal to Pivot.Title.Width... you get the picture.
To specify that a control should not take up all available space, specify a HorizontalAlignment or VerticalAlignment property to tell it what side of the parent panel to dock the item on.
For example
<Pivot Grid.Row="1" HorizontalAlignment="Left">
or
<StackPanel OWidth="480" HorizontalAlignment="Left" ...>
If you're new to WPF's layout system, I would recommend reading through the codeproject article WPF Layouts: A Quick Visual Start to quickly learn what the main layout panels are for WPF.

Need ability to exit Alt key underline mode, possibly with ESC key

Question: How do you exit keyboard hot-key mode after pressing and releasing the Alt key? In Office the ESC works, in WPF it does not.
Details: I have various Label elements in a WPF application.
<StackPanel>
<Label Grid.Column="0" VerticalAlignment="Center" Content="_Textbox 1" Target="textbox1" />
<TextBox x:Name="textbox1" Width="50" />
<Label Grid.Column="0" VerticalAlignment="Center" Content="T_extbox 2" Target="textbox2" />
<TextBox x:Name="textbox2" Width="50" />
</StackPanel>
When I press and release the Alt key the P stays underlined and I can then separately press the P key and that Label takes its action. This is the same as how Word 2013 and Notepad work in that you don't have to press the Alt and the P at the same time.
Where WPF differs is I cannot press the ESC key to stop the underlining and stop the auto-navigation to Label controls. The only way I see to get out of keyboard navigation mode is to use the mouse and click somewhere, defeating the goal of keyboard navigation.
Adding to my original comment
Actually there is a slight difference in Notepad and Word 2013 behavior. The control's there with the hot-key are in a Menu which takes Focus as soon as you press Alt and you can see they get focus cos the text area caret goes missing and Esc then returns focus to the TextBox while clearing the "_". Now in your sample code if you put the two Label's or even one inside a and then press Alt, you get the same behavior as Word or Notepad. Without the Menu there is no control that wants to take the intermediate focus.
This just seems to be an issue where the Menu gets a deferred focus to handle these hotkeys in it's own FocusManager.IsFocusScope.
Another snippet from MSDN:
The following scenario illustrates how keyboard focus and logical focus change in a Windows Presentation Foundation (WPF) application that has a Window with a TextBox and a Menu which has a MenuItem. When keyboard focus changes from the TextBox to the MenuItem, the TextBox losses keyboard focus but retains logical focus for the Window focus scope. The MenuItem obtains keyboard focus and obtains logical focus for the Menu focus scope. When keyboard focus returns to the root Window, the element in Window focus scope with logical focus will obtain keyboard focus, which in this case is the TextBox. The TextBox now has keyboard focus and logical focus. The MenuItem loses keyboard focus, but retains logical focus for the Menu focus scope.
Solution:
If you just can't stand the fact that you're limited with the grouping requirement imposed by the Menu to achieve the hot-key functionality, you could do something like:
<Grid Margin="25">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="15" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- cannot use Visibility="Collapsed" or "Hidden" on the Menu to make it take focus on Alt press -->
<Menu Width="0"
Height="0">
<Label x:Name="label1"
Content="_Textbox 1"
Target="{Binding ElementName=textbox1}" />
<Label x:Name="label2"
Content="T_extbox 2"
Target="{Binding ElementName=textbox2}" />
<Label x:Name="label3"
Content="Another Loose Label to Link Text_Box 1"
Target="{Binding ElementName=textbox1}" />
</Menu>
<Label Grid.Row="0"
Grid.Column="0"
Content="{Binding ElementName=label1,
Path=Content}" />
<TextBox x:Name="textbox1"
Grid.Row="0"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="15 0 0 0"
VerticalAlignment="Center" />
<Label Grid.Row="2"
Grid.Column="0"
Content="{Binding ElementName=label2,
Path=Content}" />
<TextBox x:Name="textbox2"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="15 0 0 0"
VerticalAlignment="Center" />
<Rectangle Grid.Row="3"
Grid.RowSpan="2"
Grid.Column="1"
Margin="10"
Fill="Tomato" />
<Label Grid.Row="4"
Grid.Column="2"
Content="{Binding ElementName=label3,
Path=Content}" />
</Grid>
So in this setup we do not use any custom Style's for any of the control's. We pretty much create a new top level Menu and add all the desired hot-key Label's to it and set it's Width and Height to 0.
Thus in the actual Visible Grid layout we can position the visible Label's anywhere we choose and to avoid code duplication in specifying the Label.Content twice for each Label(Once in the Menu and next in actual layout) we use a Binding to get the Content for the Visual Label from it's corresponding Label in the Menu.
Update
Reply to OP's comment:
I'm not sure this will be practical in my application. I have hotkeys all over the app with some being duplicates inter-screen. It wouldn't be maintainable to implement this type of workaround.
Well I don't see how duplicates or having stuff all over makes any difference to this tbh.
If you have duplicate hot-key's in multiple Window's, then define Content for these duplicate item's as a String resource in xaml or your .resx you can refer to it everywhere you need it.
Maintainable - Well you do not have anything you do special for maintainability. Your layout is not impacted, your not restricted with anything. If anything you got one <Menu> sitting hidden at the Top level of each of your Window's that defines the hot-keys for that Window and within that Window any control that wants that functionality Binds to the corresponding Menu child item. I think it unifies the scope of hot-keys in a Window to a single place than in multiple different places.
Finally, I ain't trying to impose this approach on you, it's boils down to your personal preference. If your uncomfortable with it then you could get your Label's sub-classed and implement the functionality of deferred focus yourself or raise a Microsoft bug to see if they can maybe address it for you or find an alternate solution or let go of the hot-key functionality all-together.

WPF <StatusBar> is not positioned at the bottom of the window

We have a WPF executable that creates a and then dynamically loads several assemblies. Each assembly represents a screen (.xaml) that is displayed in one of the tabs. The Problem is that the is right under the and not at the bottom of the window. How do I force the to always be at the bottom of the
window? Thx!
UserControl
DockPanel
CheckBox
StatusBar
DockPanel
UserControl
In addition to ArsenMkrt's answer about including the DockPanel.Dock="Bottom" attribute, don't forget that the LAST element in a DockPanel will fill the area unless you explicitly tell it otherwise using a height command (regardless of the DockPanel.Dock attribute provided).
my suggestion is to do thus:
<UserControl>
<DockPanel>
<StatusBar DockPanel.Dock="Bottom" />
<CheckBox />
</DockPanel>
</Usercontrol>
I had the same problem just now. Thanks to Stephen Wrighton's tip that the last element added to a DockPanel fills the area left over, I figured out how to set up my Window. It was a bit weird since I added the Grid last but it was positioned in the middle.
<Window>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
</MenuItem>
</Menu>
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem Content="Filler" />
</StatusBar>
<Grid x:Name="rootGrid">
</Grid>
</DockPanel>
</Window>
Did you try?
<StatusBar DockPanel.Dock="Bottom" ... />
In my case adding DockPanel.Dock="Bottom" was not enough I was forced to add VerticalAlignment="Bottom" as well.
<StatusBar DockPanel.Dock="Bottom" VerticalAlignment="Bottom" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0"/>
<StackPanel Grid.Row="1">
<StatusBar>Status Text</StatusBar>
</StackPanel>
If a StatusBar control in WPF is not positioned at the bottom of the window, there are several potential causes for this issue:
The StatusBar is not placed inside the bottommost container of the window: Make sure the StatusBar is inside the bottommost container of the window, such as a Grid or StackPanel, and that it is placed in the last row of the container.
The container's vertical alignment is not set to "Bottom": Ensure that the container that holds the StatusBar has its VerticalAlignment property set to "Bottom".
The window's height is not being set correctly: Make sure that the window's height is being set correctly, and that it is large enough to accommodate the size of the StatusBar.
The layout of the window is not correct: Check that the layout of the window is correct and that all elements are positioned correctly. You can use the Snapping and Guides feature of the Visual Studio Designer to align elements correctly.
The window is using a template that does not include the StatusBar, if you are using a custom template for the window, check that it includes the StatusBar and that it is positioned correctly.
By troubleshooting these potential causes, you should be able to resolve the issue and position the StatusBar at the bottom of the window.
In my case the order of how I put the children into the Dockpanel had an influence. So, this was the correct order to fill proberly:
<Button DockPanel.Dock="Top" Height="50">Top</Button>
<Button DockPanel.Dock="Bottom" Height="50">Bottom</Button>
<Button DockPanel.Dock="Left" Width="50">Left</Button>
<Button DockPanel.Dock="Right" Width="50">Right</Button>

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>

Resources