How to horizontally align buttons and distribute evenly across phone window xaml

I have an ObservableCollection that contains a ViewModel that defines the definitions of my buttons in turn.

I've been doing this for hours, reading article after article, to no avail. I've tried using Listbox and this is the closest to me. My buttons are going horizontally, but assuming I am showing 3 buttons, I want one to be displayed on the left, one displayed in the middle, and one displayed on the right.

<ListBox Grid.Row="2" ItemsSource="{Binding Links}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate ScrollViewer.HorizontalScrollBarVisibility="Disabled">
            <StackPanel Background="Beige" Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Button Grid.Column="{Binding Column}" 
                    Grid.Row="0" 
                    Width="90" 
                    Height="90">
                <ContentControl>
                    <StackPanel Orientation="Vertical">
                        <Image Source="{Binding Image}" Width="36" Height="36"
                               Margin="5" Stretch="UniformToFill" 
                               HorizontalAlignment="Center"/>
                        <TextBlock Text="{Binding Description}" 
                                   Foreground="#0F558E" 
                                   FontSize="18" 
                                   HorizontalAlignment="Center"/>
                    </StackPanel>
                </ContentControl>
            </Button>
        </DataTemplate>
    </ListBox.ItemTemplate>
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

      

As you can see, I am dynamically setting the column using a property from my ViewModel, but I no longer have a grid as I couldn't get it to work, but ideally I would like to use a grid so that I can specify which column to set the button to ...

When I use the StackPanel it works, but the buttons are next to each other rather than spaced evenly across the entire width of the screen.

I did something similar above using ItemsControl

and using a grid and I can see how each button is added, but they overlap each other on row 0, col 0. If I bind a row to the Column property for testing purposes it builds it correctly. but my buttons are showing on different lines, which is not what I want. I want each button to be horizontally aligned.

How can I achieve this? I know that I could just hard-code 3 buttons and just change my icons and text and handle the appropriate code by passing the appropriate button as the bound parameter, but ideally I would like to dynamically build the buttons in a grid and position them using a column.

Note that the number of columns will be fixed, i.e. 3, since I will never have more of this.

Thank.

+3


source to share


2 answers


I eventually found the answer to my problem in an article I found on the internet.

You can check it out here: Using Grids with ItemsControl in XAML

In short, you need to subclass itemsControl, and you need to rewrite the GetContainerForItemOverride method, which will take care of copying the ItemTemplate "data" into the ContentPresenter. In this case, row and column, but for my requirement it is just a column since my row will always be 0.

Here's the bulk of the code, if you don't want to check the whole article that solves the problem of setting controls horizontally in the grid using ItemsControl, but note that the article also takes care of dynamically creating rows and columns, which I'm not interested in my project.

public class GridAwareItemsControl : ItemsControl
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        ContentPresenter container = (ContentPresenter)base.GetContainerForItemOverride();
        if (ItemTemplate == null)
        {
            return container;
        }

        FrameworkElement content = (FrameworkElement)ItemTemplate.LoadContent();
        BindingExpression rowBinding = content.GetBindingExpression(Grid.RowProperty);
        BindingExpression columnBinding = content.GetBindingExpression(Grid.ColumnProperty);

        if (rowBinding != null)
        {
            container.SetBinding(Grid.RowProperty, rowBinding.ParentBinding);
        }

        if (columnBinding != null)
        {
            container.SetBinding(Grid.ColumnProperty, columnBinding.ParentBinding);
        }

        return container;
    }
}

      

The final xaml looks like this:

<controls:GridAwareItemsControl ItemsSource="{Binding Links}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid Background="Pink">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
            </Grid>
       </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button
                 Grid.Column="{Binding Column}" 
                 Grid.Row="0" 
                 Width="120" Height="120">
                 <ContentControl>
                     <StackPanel Orientation="Vertical">
                         <Image Source="{Binding Image}" Width="36" Height="36" Margin="5"
                                Stretch="UniformToFill" HorizontalAlignment="Center"/>
                         <TextBlock Text="{Binding Description}" Foreground="#0F558E" 
                          FontSize="18" HorizontalAlignment="Center"/>
                     </StackPanel>
                 </ContentControl>
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</controls:GridAwareItemsControl>

      

As soon as I used the new control, my buttons were correctly positioned inside the grid and therefore evenly spaced as the grid took care of this with ColumnDefinitions.



If anyone knows how to achieve the same without creating a new control and overriding a method (in other words, pure XAML), please post it as I would be very interested to see how this can be done.

Thanks and thanks to Robert Garfoot for sharing this awesome code!

PS: Please note that I simplified my xaml to create a test case without any styling on the buttons, so they are pretty big if you try to use this sample.

UPDATE:

Minor typo correction, but the grid column definition was defined as

 <Grid.ColumnDefinitions>
     <ColumnDefinition Width="*"/>
     <ColumnDefinition Width="Auto"/>
     <ColumnDefinition Width="*"/>
 </Grid.ColumnDefinitions>

      

but as @OmegaMan suggested, to be evenly distributed, it had to be defined as

 <Grid.ColumnDefinitions>
     <ColumnDefinition Width="Auto"/>
     <ColumnDefinition Width="*"/>
     <ColumnDefinition Width="Auto"/>
 </Grid.ColumnDefinitions>

      

+1


source


but ideally I would like to use a grid so that I can specify which Column to set the button to.

Confused why you can't just use this grid like this semi-pseudo code below where the Grid is consuming all the horizontal space. Then, when the center grid column is star-sized and the rest of the remaining space is consumed after the buttons "1" and "3" are scaled up in their space:

<Grid>

   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
   </Grid.ColumnDefinitions>

   <Button Grid.Column="0"/>
   <Button Grid.Column="1" HorizontalAlignment="Center"/>
   <Button Grid.Column="2"/>

</Grid>

      



If that fails, set the horizontal alignment of button to the left and button three to "right".


As with the list box, it may not stretch to fit the entire horizontal screen size. Check out my answer to WP8 sizing question: ListBoxItem HorizontalContentAlignment .

+8


source







All Articles