Simple WPF 'panel' issue - wpf

a 'simple' about controls layout in wpf. There is a custom control with a grid in wich there is 'panel' on this panel there are three elements two buttons and slider between them. Right button must be anchored to right side of the 'panel', left button to the left side of 'panel' and the slider must FILL ALL THE FREE SPACE BETWEEN buttons. The width(and height) of buttons and grid will be set dinamycaly in the code after. The question is - what kind of 'panel'I must use and how to make it to operate with given task? (stack, dock - have no such functionality even with this "horizontal stratching")
In WinForms - there are no problem width of slider = widthOfGrid - (widtOfBothButtons)
Is this possible to do it in wpf? Or I must compose the code like above in some constructor-like functions?
(offtop- As for me this is a typical task for wpf control and I am surprized that it has too little automatic to solve it)
the code:
<UserControl x:Class="WpfApplication1.UserControl2"
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"
mc:Ignorable="d"
d:DesignHeight="42" d:DesignWidth="291">
<Grid x:Name="gridCtrl">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="clnmLbl" Width="Auto"/>
<ColumnDefinition x:Name="clnmPnl" />
</Grid.ColumnDefinitions>
<Label x:Name="lblText" Grid.Column="0" Content="" VerticalAlignment="Center">
</Label>
<DockPanel x:Name="pnlDock" Grid.Column="1">
<Button x:Name="btnLeft" HorizontalAlignment="Left" DockPanel.Dock="Left">
</Button>
<Border x:Name="BorderOfSlider" BorderBrush="#FF000000" BorderThickness="3,3,3,3" CornerRadius="8,8,8,8" >
<Slider x:Name="sldSlider" HorizontalAlignment="Stretch" VerticalAlignment="Center" >
</Slider>
</Border>
<Button x:Name="btnRight" HorizontalAlignment="Right" DockPanel.Dock="Right">
</Button>
</DockPanel>
</Grid>
</UserControl>

That sounds exactly like what a DockPanel does.... it will Dock controls to the sides of the panel, and by default the last control added will stretch and fill all remaining space
<DockPanel>
<Button DockPanel.Dock="Left" Content="Left Button" />
<Button DockPanel.Dock="Right" Content="Right Button" />
<Slider />
</DockPanel>
Also if you're new to WPF, I'd recommend taking a look at this site for a quick visual view of WPF's layout controls.
Edit
Make sure your control that fills all remaining space is the last item added to your DockPanel. If not, it will use a default value of DockPanel.Dock="Left"

There are several ways to do it:
1) DockPanel. Left button will occupy some place at the left side of DockPanel, right button will occupy required space on the right side of remaining space. The rest will be used to display Slider.
<DockPanel>
<Button DockPanel.Dock="Left">Left button</Button>
<Button DockPanel.Dock="Right">Right button</Button>
<Slider />
</DockPanel>
2) Grid. Create grid with 3 columns. Left and right columns will take only required space, the rest will be given to the center column.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Left button</Button>
<Slider Grid.Column="1" />
<Button Grid.Column="2">Right button</Button>
</Grid>

Related

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.

SilverLight: how to align buttons in stackpanel center and right

In the bottom "row" (inside of StackPanel with Horizontal orientation?) of my SilverLight control I would like to display two things:
"Save" button - to center-aligned
Text field with small text (kind of Version) - to be aligned to the right
How to do that? If I put both inside of stackpanel their alignement will be the same... Wrap panel just tie them together...
Please advise.
Thank you.
Within your stack panel create a grid with 2 columns then align each column independantly.
<StackPanel x:Name="Layout" Background="Black">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Name="Left" HorizontalAlignment="Left" grid.column=1/>
<Button x:Name="Button2" Content="And me!" HorizontalAlignment="Center" ></Button>
</Grid>
</StackPanel>
No the perfect syntax but its just to demonstrate the idea.
Hope this helps!

WPF: Star in ColumnDefinition not expanding columns

I have a user control that need the 1st and 3rd column to have the same width at all time.
My code is a follows:
<UserControl x:Class="UserControls.ListBoxSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox x:Name="ListBox_Source" Grid.Column="0" Grid.Row="0" />
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Vertical">
<Button Content=">" Click="Button_Add_Click"/>
<Button Content="<" Click="Button_Remove_Click" />
</StackPanel>
<ListBox x:Name="ListBox_Destination" Grid.Column="2" Grid.Row="0" />
</Grid>
</UserControl>
The result is not as expected as column 3 (ListBox_Destination) is not expanded at all.
Isn't the 5* in ColumnDefinition enough to force the 2 listbox to the same width??
UPDATED : Sorry that I forgot to mention that the problem only occurs when I put the control inside a RibbonGroup using Microsoft Ribbon for WPF
Sometimes, when you put your control in certian types of layout controls (like a StackPanel), it won't size as expected because the parent layout will only size the child to it's minimum desired size (just enough to show the content). This may be why you are seeing this when you put it in the RibbonGroup. Try giving your Grid a Width or MinWidth and see if that makes a difference.
yes it forces the columns 1 and 3 to be of the same size, but it doesnt gaurentee the content (listboxes) inside the colulms will be of the same size. You have to set the size of content to take up whole space

WPF buttons same/recommended width

Suppose you have a window with multiple buttons such as Ok/Cancel or Yes/No/Cancel. All the buttons need to be the same width. Obviously this could be done by just guessing a number and hardwiring all of them to that number.
Is there a better way to do it, one that would take into account preferred/recommended sizes (just how wide should an Ok button be anyway? This is not a rhetorical question, I actually don't know the answer!), what's needed by the text of the longest caption, what happens if the font size is increased etc?
Another, perhaps simpler, way to do this is to use the SharedSizeGroup property on the ColumnDefinition and RowDefinition classes.
Columns (and Rows) in a WPF Grid can automatically resize to fit their contents - when SharedSizeGroup is used, columns with the same group name share their resizing logic.
The Xaml would look something like this ...
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Ok"
Margin="4" />
<Button Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Cancel"
Margin="4" />
<Button Grid.Column="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Long Button Caption"
Margin="4" />
</Grid>
There are several ways to do this:
1) Use a Grid for layout. Each Button gets its own Column, which is Star-sized. That way, all columns are the same size:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Yes</Button>
<Button Grid.Column="1">No</Button>
<Button Grid.Column="2">Cancel</Button>
</Grid>
2) You can have one item as "master size" and bind the width of all others to this item's width.
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton" Width="100">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="Width"/>
</Button.Width>
No
</Button>
</StackPanel>
EDIT: In actual code, you probably will have Width="Auto". Since the other widths are based on the "master width", the button with the widest width (widest text) should be chosen.
Use a "master" control, like in Daniel's answer, but bind to the "ActualWidth" attribute instead of "Width":
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="ActualWidth"/>
</Button.Width>
No
</Button>
</StackPanel>
This way, the value is taken from the master control at run time, after minimum and maximum width and all other layout calculations have been taken into account. Binding to "Width" binds to whatever you happen to put into the attribute at compile time, which may not be the width that is really used.
Also, the binding can be written shorter like
<Button Width="{Binding ElementName=MasterButton, Path=ActualWidth}"/>
According to the MS User Experience Interaction Guidelines for Windows 7 and Windows Vista (p61), standard dimensions for command buttons are 50x14 DLU actual size (75x23 pixels). The guidelines further suggest you "try to work with [these] default widths and heights." Obviously, if you need more width to fit a clear label, then take more width.
These answers are great if you have a fixed number or fixed layout for the buttons, but if like me there is a dynamic number of buttons coming from a binding and contained in a ItemsControl then this is not feasible. But there is a simple way and it still involves used the sharedsize property of Grid.
DataTemplate:
<DataTemplate x:Key="ODIF.Mapping">
<Button HorizontalContentAlignment="Left" Background="#FFEEEEEE" BorderBrush="#FFBDBDBD">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="PluginButtonsWidth"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsIconHeight"/>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsNameHeight"/>
</Grid.RowDefinitions>
<Image Width="32" Height="32" Source="{Binding PluginIcon}" RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Grid.Row="1" Text="{Binding PluginName}"/>
</Grid>
</Button>
</DataTemplate>
Parent container:
<ItemsControl ItemsSource="{Binding MappingPlugins, ElementName=page}" ItemTemplate="{StaticResource ODIF.Mapping}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Essentially the button's content can itself be a Gird which then you can place your labels and icons as needed in, but even though the buttons do not reside in the same grid (they each are their own) the grid can still share it size so long as you set the root container's (ItemsControl) property of Grid.IsSharedSizeScope to True.
This will force the content grid of each button to be the same exact size based on the largest one while not having to have the Buttons themselves in a predefined grid.
In the most general case, you want to create a
Style in your section, then apply this style as desired. Now when you change the style, all buttons change.
Or you can change the Content of the button so that it autosizes to the text.

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