How can a WPF StackPanel fill vertically from bottom to top? - wpf

I need to be able to fill a stackpanel with buttons but the buttons must appear at the bottom of the stackpanel first and populate upwards. The buttons are created dynamically and there's an unknown number of them so visual hackery just won't work. I've tried experimenting with vertical alignments but to no avail.

Like so:
<StackPanel VerticalAlignment="Bottom">
...
</StackPanel>
and to populate with buttons upward you must insert the buttons at position 0, instead of adding them.

Or you can rotate the StackPanel 180 degrees to get the buttons to stack from the bottom to the top and then rotating the buttons another 180 degrees to get them right-side-up again:
<StackPanel>
<!-- rotate all buttons inside this panel -->
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="180"/>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<!-- rotate the stack panel -->
<StackPanel.LayoutTransform>
<RotateTransform Angle="180"/>
</StackPanel.LayoutTransform>
<!-- content -->
<Button>1</Button>
<Button>2</Button>
</StackPanel>

Another alternative is to use a DockPanel instead.
Just set the LastChildFill to false on the DockPanel.
Then set the attached Dock property to each button you are adding to Bottom before adding to the DockPanel.
example :
var button = new Button();
DockPanel.SetDock(button, Dock.Bottom);

The best way to solve the problem is to implement custom container derived from stackpanel
but quick and dirty solution if elements are added at runtime is
public Window1()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
Button btn = new Button();
btn.Content = "Button " + i;
MyStack.Children.Insert(0, btn);
}
}
Just insert item at 0 position instead of adding them.

Try putting the StackPanel inside another container (not a StackPanel; maybe a DockPanel) and bottom-aligning it. Then when you populate the buttons, put each new one into the first position.

I found that using a UniformGrid with Column=1 gives a neat filling stack, or set Rows=1 give a neat horizonatally filled stack. And adding from the index 0 will work from bottom up.

Love the transforms solution by Nir. I was wondering if it could be done using transforms.
One caveat, though: Don't use the transforms trick on a ScrollView based control such as a ListBox because the scroll bar operation will be inverted from the content. Hilarious to watch, as long as you're not the end user. ;>

Related

AvalonDock Layout Without DocumentPane

I am trying to create a layout with avalon dock which looks something like this:
On the left side, the user is supposed to select the content which is displayed in the red box. This sort of layout can be easily achieved if the red box in the middle is declared as Document pane, and the content is displayed as a tab.
However, usage of tabs in this case is not needed because there is only one content, so I want to display the red box as an anchorable instead as a document.
So, basically, I want a layout which does not contain the document pane at all.
This is my code so far:
<ad:DockingManager
LayoutUpdateStrategy="{StaticResource dockingLayoutStrategy}"
AnchorablesSource="{Binding Path=Anchorables}"
ActiveContent="{Binding Path=ActiveContent, Mode=TwoWay}"
>
<ad:DockingManager.LayoutItemContainerStyleSelector>
<local:DockingPaneStyleSelector>
<local:DockingPaneStyleSelector.AnchorableStyle>
<Style TargetType="{x:Type adc:LayoutAnchorableItem}">
<!-- a bunch of properties-->
</Style>
</local:DockingPaneStyleSelector.AnchorableStyle>
<local:DockingPaneStyleSelector.PaneStyle>
<!-- a bunch of properties-->
</local:DockingPaneStyleSelector.PaneStyle>
</local:DockingPaneStyleSelector>
</ad:DockingManager.LayoutItemContainerStyleSelector>
<adl:LayoutRoot>
<adl:LayoutPanel Orientation="Horizontal">
<adl:LayoutAnchorablePane Name="ContentSelectionPane" DockWidth="100"/>
<adl:LayoutPanel Orientation="Vertical" DockWidth="*" IsMaximized="True">
<adl:LayoutPanel Orientation="Horizontal">
<adl:LayoutAnchorablePane Name="MainPane" IsMaximized="True" />
<adl:LayoutAnchorablePane Name="PropertyPane" DockWidth="300"/>
</adl:LayoutPanel>
<adl:LayoutAnchorablePane Name="StuffPane" DockHeight="150"/>
</adl:LayoutPanel>
</adl:LayoutPanel>
</adl:LayoutRoot>
The first problem I am experiencing is that when the left pane is loaded, it takes all available space, and it never allows the right side to show. If I commment out the left side pane, the right side of the window is shown.
The second problem is that the panes do now have the size which I declared, but always take the half of the available size. E.g. if I have:
<adl:LayoutRoot>
<adl:LayoutPanel Orientation="Horizontal">
<adl:LayoutAnchorablePane Name="ContentSelectionPane" DockWidth="100"/>
<adl:LayoutAnchorablePane Name="{x:Static si:Panes.DocumentPane}" DockWidth="*" IsMaximized="True" />
</adl:LayoutPanel>
</adl:LayoutRoot>
Both panes take exactly half of the availalbe place.
Any ideas on how I might achieve the "DocumentPane" size behaviour which does not display tabs, but achorables?
Well, I have a major "hack" that I did which worked for me. I set a negative top-margin on the top of the container holding the document pane. The negative margin pulls the content up hiding the document tab well.
It is tricky because you have to do it after the content is rendered/loaded, because the Xaml panes are not the actual visuals put into the Visual Tree.
Here is my Xaml for the document area:
<xcad:LayoutDocumentPane
x:Name="LayoutDocumentPaneArea"
CanRepositionItems="False"
DockMinHeight="0"
DockMinWidth="0"
IsMaximized="True">
<xcad:LayoutDocument
CanClose="False"
CanFloat="False"
IsMaximized="True">
<Grid x:Name="DocumentArea" />
</xcad:LayoutDocument>
</xcad:LayoutDocumentPane>
Then in the window's Content_Rendered event call this function to strip away the document tabs and in my case a 1px border of the content I had hosted there:
private void TrimDocumentArea()
{
var border = DocumentArea.FindVisualAncestor<Border>();
if (border != null)
{
border = border.FindVisualAncestor<Border>();
if (border != null && border.Name == "ContentPanel")
{
border.Margin = new Thickness(-1, -21, -1, -1);
}
}
}

How to align the button at the leftmost side in a listbox control?

I am developing window phone 7 application. I am new to the silverlight. I am dynamically creating the button control and binding these all button controls to the listbox.
<ListBox x:Name="lstButtons" Margin="393,-28,-12,28" Background="Orange">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="{Binding ElementName}" Width="100" HorizontalAlignment="Right"></Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now I want to display the button control at the leftmost side means I want to display the button control near to the left border of the listbox. I want to display the button control which is aligned to the left. With my above code the the button control appears in the center in the listbox ? How should I align it to the left ? Can you please provide me any code or link through which I can resolve the above issue ? If I am doing anything wrong then please guide me.
Take a look at the sample XAML in the MSDN resource page here. You need to play with the HorizontalAlignment property of the element you are trying to align within the parent, perhaps fine tune the alignment by setting the margin property of the element.
This might not be very helpful, but I have found that using expression blend for layout/ui things like this is the easiest way to do it. You will have to drill into the list template then you can play around with alignments.
For this scenario you need to align the button dynamically. In the following code the button is aligned at the left side in the listbox control. It is aligned close to the left border of the listbox control.
Button AlphabetButton = new Button();
AlphabetButton.Margin = new Thickness(-10,-27,0,0);
AlphabetButton.Width = 80;
AlphabetButton.Height = 80;

Change the ComboBox Popup control to be right-aligned with the its parent

I'm trying to get a ComboBox to display its Popup right aligned rather than left-aligned as is the default.
I mean I want the right edge of the Popup to be aligned with the right edge of its parent.
I tried overriding the ComboBox class but that didn't quite work as the Popup HorizontalOffset seems to be reset somehow.
I thought that would be a fairly easy change but I can't find any resource talking about this issur.
In the ComboBox style change the flowDirection of the popup to RightToLeft.
<Style TargetType="ComboBox">
<Popup x:Name="Popup" FlowDirection="RightToLeft">
</Style>

WPF Datagrid zoom issue

I'm working with the WPF DataGrid from the WPFToolkit and I'm having issues with trying to zoom the entire datagrid. My initial thought was that it was going to be really easy and I would just apply a scale transform to the grid and animate the ScaleX, ScaleY properties when the used clicked a button. This did not work however because the scrollbar was zoomed in making it larger. I need fixed headers and fixed columns on the datagrid so I can't simple use a scrollviewer outside of the datagrid to handle the scrolling. The second thing I tried was to just scale the font size in the grid, but this failed because on shrinking the font size the columns stay at the original width and do not shrink.
Finally I thought I had it working by using the below code which goes into the view tree for the datagrid and adds a scale transform to the Scroll Content Presenter. (Also not shown in this code, I apply a transform to the visual tree item for the headers in the same manner so that it scales as well). I thought this was working great until I tested out the horizontal scrolling after zooming. (Vertical scrolling works perfectly.) Before zooming at all the horizontal scrolling is fine, but after zooming, when I scroll horzonitally the display freaks out. It is hard to tell exactly what it is doing, but it sort of looks like the content that is scrolling off the left of the screen is "folding over" and coming back in on the left. Maybe it is just all smashing up on the left side. Does anyone have any ideas how I can get this working, hopefully without throwing out my whole datagrid that is already working quite well otherwise.
ScrollContentPresenter sp = (ScrollContentPresenter)
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(dgMatrix, 0), 0), 0),2);
ScaleTransform st = new ScaleTransform(1, 1);
sp.LayoutTransform = st;
DoubleAnimation a = new DoubleAnimation();
a.By = 1.5;
a.AutoReverse = false;
a.Duration = new Duration(TimeSpan.Parse("0:0:0.25"));
st.BeginAnimation(ScaleTransform.ScaleXProperty, a);
st.BeginAnimation(ScaleTransform.ScaleYProperty, a);
I figured out a solution. Don't know if this is the same way you did it or not:
<toolkit:DataGrid.CellStyle>
<Style TargetType="{x:Type toolkit:DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type toolkit:DataGridCell}">
<ContentPresenter>
<ContentPresenter.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=Value, ElementName=ZoomFactor}"
ScaleY="{Binding Path=Value, ElementName=ZoomFactor}" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</toolkit:DataGrid.CellStyle>
Where ZoomFactor is a slider:
<Slider x:Name="ZoomFactor"
Value="1"
Minimum=".25"
Maximum="5"
Width="180" Margin="0,0,5,0"/>

How do I make an item in a toolbar fill all available space in WPF

I'm trying to make an item on ToolBar (specifically a Label, TextBlock, or a TextBox) That will fill all available horizontal space. I've gotten the ToolBar itself to stretch out by taking it out of its ToolBarTray, but I can't figure out how to make items stretch.
I tried setting Width to Percenatage or Star values, but it doesn't accept that. Setting Horizontal(Content)Alignment to Stretch in various places seems to have no effect either.
Unfortunately it looks like the default ControlTemplate for ToolBar doesn't use an ItemsPresenter, it uses a ToolBarPanel, so setting ToolBar.ItemsPanel won't have any effect.
ToolBarPanel inherits from StackPanel. By default its Orientation is bound to the parent ToolBar.Orientation, but you can override this and set its Orientation to Vertical with a Style and this will allow items to stretch horizontally:
<DockPanel>
<ToolBar DockPanel.Dock="Top">
<ToolBar.Resources>
<Style TargetType="{x:Type ToolBarPanel}">
<Setter Property="Orientation" Value="Vertical"/>
</Style>
</ToolBar.Resources>
<ComboBox HorizontalAlignment="Stretch" SelectedIndex="0">
<ComboBoxItem>A B C</ComboBoxItem>
<ComboBoxItem>1 2 3</ComboBoxItem>
<ComboBoxItem>Do Re Mi</ComboBoxItem>
</ComboBox>
</ToolBar>
<Border Margin="10" BorderBrush="Yellow" BorderThickness="3"/>
</DockPanel>
You can then use a Grid or something in place of the ComboBox above if you want multiple items on a line.
Try putting a horizontal StackPanel in the ToolBar and then the element you want inside of that StackPanel.
Have you tried wrapping your item in a Grid, not in a StackPanel?
You need a special custom panel like auto stretched stack panel, and replace the toolbarpanel. Check this out you can find one panel there
http://blendables.com/products/productsLayoutmix.aspx
I've had this same problem for a while, and there is very little help available online.
Since it doesn't sound like you need all the extra functionality of a Toolbar (collapsible/movable trays), why not just use a Top-docked Grid, and tweak the background a little to make it look like a standard toolbar?

Resources