Srange result about wpf grid row removal - wpf

I have a question about grid removal:
whenever I delete the last row or all rows in the grid, there is still something(actually the last row) displayed on the grid. I did actually checked the grid.rowdefinitions.count is 0. the method I used is removeAt and removeRange(0, grid.rowdefinitions.count). what's wrong with that? Can anybody tell why? Many thanks!
//------------
<Grid DockPanel.Dock="Top" HorizontalAlignment="Left" Name="grid1" ShowGridLines="true" Width="200" Height="200">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="100" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="200" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="300" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="400" />
<TextBlock Grid.Row="4" Grid.Column="0" Text="500" />
</Grid>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Width="200" DockPanel.Dock="Top">
<Button Width="80" Click="rem5Row">Remove 5 Rows</Button>
</StackPanel>
//--------------
private void rem5Row(object sender, RoutedEventArgs e)
{
if (grid1.RowDefinitions.Count < 5)
{
Console.WriteLine("less than 5!");
}
else
{
grid1.RowDefinitions.RemoveRange(0,grid1.RowDefinitions.Count);
}
}

I'm not sure I understand your question correctly (and I'm not sure the code reflects what you say you're doing). But: how do you know the "final row" is displaying?
What I suspect is happening is this: you are removing rows and expecting the controls in those rows to be removed also. However, this is not how a WPF Grid functions: the controls are not the child of their row, and they are not being removed when you remove the RowDefinition.
So, all of the child controls will still be rendering but, because their Grid.Row is invalid, they will render in the "Row 0" position. They are rendered in markup-order, so the final result will appear as though the "last row" is being displayed because this is the control that's drawn last. In fact, all of your controls are still present; they're just all in the same place on screen.
If you want to confirm this, you could use Snoop to examine your runtime UI. Alternatively, set widths for your textboxes so that the row 0 box is wider - you will see the overlap clearly then.

Related

Preventing StackPanel items from overlapping

I am designing a Windows Phone application and I would like to stack a TextBlock and a Button next to each other horizontally. This is my pseudo-code on what I would like to achieve:
<StackPanel Name="titlePanel" Orientation="Horizontal" Margin="0,-6.5,0,26.5">
<TextBlock Name="titleBox" Text="{Binding Title}" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
<Button Content="press" Width="whatever space is left" Length="what the width is"/> <!--how do I do this?-->
</StackPanel>
I got an error when I tried to put Width="*", so I'm looking to do something like Width="titlePanel.Width - titleBox.Width" and Length="this.Width", only I can't seem to be able to reference other objects inside XAML. I don't want to put into the code-behind file to format the size of the Button every time that page comes up... how would this be achieved?
You can't do it with StackPanel because, the stack panel measures every child element with positive infinity as the constraint for the axis that it is stacking elements along. The child controls have to return how big they want to be (positive infinity is not a valid return from the MeasureOverride in either axis) so they return the smallest size where everything will fit. They have no way of knowing how much space they really have to fill.
So you should use grid to achieve desired behavior.
You can use grid control for this kind of purpose
try this one
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Name="titleBox" Grid.Column="0" Margin="5,0" HorizontalAlignment="Stretch" TextWrapping="Wrap" Text="Your text goes here." />
<Button Content="press" Grid.Column="1" Margin="5,0" HorizontalAlignment="Stretch"/>
</Grid>
Hope this helps.

How to align buttons horizontally and spread equally with xaml windows phone

I have an ObservableCollection which contains ViewModel which in turns defines my buttons definitions.
I've been at it for hours, reading articles after articles but to no avail. I've tried using a Listbox and this is the closest I've got to. My buttons are getting build horizontally but assuming I'm displaying 3 buttons, I want one 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 set the column dynamically using a property from my ViewModel but I no longer have a grid as I couldn't get it to work, but ideally I'd like to use a grid so that I can specify in which Column to set the button.
When I use a StackPanel, it works but the buttons are right next to each other rather than being split evenly through the entire width of the screen.
I've done something similar to the above using ItemsControl and using a grid, and I can see each button getting added but they overlap each other in row 0, col 0. If I bind the row to the Column property for testing purposes, it build it correctly but my buttons are displayed on different rows which is not what I want. I want each button to be aligned horizontally.
How can I achieve this? I know I could just hard code 3 buttons and just change their icons and text and handle the relevant code by passing the relevant button as binded parameter, but ideally I'd like to build the buttons dynamically in a grid and position them using the column.
Note that the number of column would be fixed i.e. 3, as I'll never have more than this.
Thanks.
but ideally I'd like to use a grid so that I can specify in which
Column to set the button.
In any Xaml variant, why not just use that Grid, such as shown below, where the Grid is set to consumes all the horizontal space.
Then with the grid's center column to be star sized and to have the rest of the remaining space be consumed after button 1 and button 3, which auto size into their own spaces:
<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 button one's HorizontalAlignment to be Left and button three's as Right.
As an aside with the list box, it may not be stretching to the whole horizontal size of the screen. Check out my answer to a WP8 sizing issue:
ListBoxItem HorizontalContentAlignment.
I eventually found the answer to my problem in an article I found on the web.
You can check it out here: Using Grids with ItemsControl in XAML
In short, you need to subclass the itemsControl and you need overwrite the GetContainerForItemOverride method which will take care of copying the "data" of the ItemTemplate to the ContentPresenter. In this instance, the row and column, but for my requirement, it is just the Column, since my row will always be 0.
Here is core part of the code if you don't want to check the full article which resolve the problem of setting controls horizontally in a grid using ItemsControl but note the article takes care of creating rows & columns dynamically as well, which I'm not interested in for 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>
Once I used the new control, my buttons were correctly placed inside the grid, and therefore were evenly spaced out as the grid took care of that wit the ColumnDefinitions.
If anyone knows how to achieve the same without having to create a new control and overriding the method (in other words, pure XAML), please post it as I'd be very interested to see how this can be done.
Thanks and thank you to Robert Garfoot for sharing this great code!
PS: Note that I've simplified my xaml in order to create a test sample without any style on the buttons, so these are rather large if you try based on this sample.
UPDATE:
Small typo correction but my grid column definition was defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
but as #OmegaMan suggested, to be evenly spread, it should have been defined as
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
I was able to accomplish this with a stackpanel inside of a grid, avoiding columns altogether. If you set the stackepanel's HorizontalAlignment to "center", it will center itself inside the grid and grow as buttons are added, still staying centered inside of the grid. Then it's just a matter of margins to have the buttons equally spaced:
<Grid>
<StackPanel
VerticalAlignment="Stretch"
HorizontalAlignment="Center"
Orientation="Horizontal"
>
<Button HorizontalAlignment="Center" Content="Add" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Edit" Width="104" Height="32" Margin="24,0"/>
<Button HorizontalAlignment="Center" Content="Remove" Width="104" Height="32" Margin="24,0"/>
</StackPanel></Grid>

wpf - Binding issue ActualWidth

I have the following XAML code:
<Grid Grid.Row="2" Name="grid_StatusBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{Binding ElementName=wrapPanel, Path=ActualWidth}" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<ProgressBar Grid.Column="0" HorizontalAlignment="Stretch" Margin="5,5,5,5" Name="progressBar1" VerticalAlignment="Stretch" />
<WrapPanel Grid.Column="1" Name="wrapPanel" HorizontalAlignment="Right">
<Label Content="1" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_Dataset" VerticalAlignment="Stretch" Visibility="Collapsed" />
<Label Content="/20" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_TotalDatasets" VerticalAlignment="Stretch" Visibility="Collapsed" />
<Label Content="ID:" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="label_IDText" VerticalAlignment="Stretch" />
<Label Content="no id" Height="28" HorizontalAlignment="Right" Margin="0,0,0,0" Name="labelID" VerticalAlignment="Stretch" />
</WrapPanel>
<Button Grid.Column="2" Name="button_Help" Height="30" Width="30" Content="?" HorizontalAlignment="Right" VerticalAlignment="Stretch" Click="button_Help_Click" >
</Grid>
What i am trying to show the progressbar as wide as possible while changing the visibility of some of the labels and setting different texts (and therefore different lengths/widths.
I have then different functions:
At program start the "no id" text of the labelID Label is replaced with an internal value. The width is updated: OK
With the code running I change the visibility of the first two labels, and the ColumnDefinition.Width is not updated and only the first two labels are shown (because there is not place enough for all the 4 of them to fit in the wrapPanel ActualWidth: ERROR!
If I change the Visibility property from the first two Labels from Collapsed to Visible, from start the visibility of all the Labels are Visible, and all the Labels are shown: OK
From the previous state I change visibility of the first two Labels to Collapsed, the ColumnDefinition.Width is updated and the ProgressBar is as wide as possible and all the text is shown: OK
Could anyone please help me? I do not understand why the width is not updated...
NOTE: The visibility is changed using two buttons in the window running the following code:
label_Dataset.Visibility = System.Windows.Visibility.Visible;
label_TotalDatasets.Visibility = System.Windows.Visibility.Visible;
EDIT: My target is to show the Visible Labels using the minimum space (in one line) in order to have the ProgressBar using the maximum width possible.
It is not possible to change the width of a parent element based on the width of a child. In your case, the column is the parent of the WrapPanel, so the ActualWidth of the WrapPanel is not available until after the grid/column has already been sized.
Even if you wrote code to try and circumvent this, you would still run into an issue, as the measure/layout sequence would become re-entrant. Whenever the size of the column changed, it would re-layout all the child controls, one of which is the WrapPanel. This would cause the ActualWidth of the WrapPanel to be changed -- effectively causing an infinite loop. To prevent this stack overflow scenario, the framework immediately detects any re-entrancy in the layout/measure cycle and throws an exception.
I'm not exactly sure what you are trying to achieve. Why do would you want the Labels in a WrapPanel? Surely you would always want the 4 labels to be on the same line, or at least on two lines.
If this is the case, I would set the width of the second Column to "Auto" and put the labels in another container, i.e. one of the following:
If you want all the labels to wrap, as in a WrapPanel being forced to its minimum width, use:
<StackPanel Grid.Column="1" Orientation="Vertical">
... your labels here ...
</StackPanel>
If you want all the labels to stay on the same line, use:
<StackPanel Grid.Column="1" Orientation="Horizontal">
... your labels here ...
</StackPanel>
If you want two lines use:
<StackPanel Grid.Column="1" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
... 1st 2 labels here ...
</StackPanel>
<StackPanel Orientation="Horizontal">
... 2nd 2 labels here ...
</StackPanel>
</StackPanel>

How to bind Font Size to variable Grid Size

I have a grid with 2 columns and 2 rows. A single character (Unicode U+2699) was placed inside the bottom right grid field. It looks like this:
I´d like the character to automatically adjust its font size to fit the grid field it has been placed in (in this case it should be bound to the height of the second grid row, but since in some cases it could be unclear if the height or the width of the grid is smaller, it would be also nice to know how to bind to the lowest of those 2 values).
My implementation so far is something like the following (I simplified it a bit for this example):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition x:Name="heightToBind" Height="40"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button FontSize="{Binding ElementName=heightToBind, Path=Height.Value, Mode=OneWay}" Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
The problem here is, that it only works if the height is a fixed value inside the RowDefinitions. I want it to work with the following definition:
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition x:Name="heightToBind" Height="*"/>
</Grid.RowDefinitions>
As a bonus question I´d also be interested why it might be that the character is placed too low so it is getting cut off at the bottom (I tried VerticalAlignment="Center" for the button but with no effect).
You can try using a ViewBox as the button's content:
<Button Grid.Row="1" Grid.Column="1">
<Button.Content>
<Viewbox StretchDirection="Both" HorizontalAlignment="Stretch">
<TextBlock Text="⚙" />
</Viewbox>
</Button.Content>
</Button>
A ViewBox can stretch and scale his child to fill all the available space...
You could try binding to the ActualHeight instead of the Height:
<Button FontSize="{Binding ElementName=heightToBind, Path=ActualHeight.Value, Mode=OneWay}"
Content="⚙" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" />
This should work.
The * on the grid definition means take the available space as the height so it's only determined when the page layout has been prepared for layout. If the height is either unset or changed then the real height is returned in the ActualHeight property.

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

Resources