See the following XAML:
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" MinHeight="150">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid x:Name="grid" VerticalScrollBarVisibility="Visible" />
<Button x:Name="button" Grid.Row="1" Content="hello" VerticalAlignment="Top" HorizontalAlignment="Center" Click="button_Click" />
</Grid>
</UserControl>
Corresponding code:
public partial class MainPage : UserControl
{
public class dataclass
{
public string data { get; set; }
}
ObservableCollection<dataclass> list;
public MainPage()
{
InitializeComponent();
grid.ItemsSource = list = new ObservableCollection<dataclass>();
}
private void button_Click(object sender, RoutedEventArgs e)
{
for (var i = 0; i < 5; i++)
list.Add(new dataclass
{
data = "hello" + i
});
}
}
How it works now: The grid takes up the entire screen height minus the height of the button. When too many new items are added, you scan start scrolling. The position of the button never changes, it's always at the bottom of the screen.
What I would like: The grid should take up as little space as possible, so when it's empty, only the header should be visible, and the button immediately below it. When too many items are added, and the button is already at the bott开发者_如何转开发om of the screen, it shouldn't grow any more, but start scrolling instead.
If I swap the two RowDefinition's, then the grid is small at first, but grows indefinitely, pushes the button off the screen and never starts scrolling. How can I do this nicely?
In order to achieve this, nest another Grid
in LayoutRoot
and then use that nested grid as your main one. Then set both rows to Auto
.
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="innerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="itemInTheFirstRow" Grid.Row="0" />
<Button x:Name="itemInTheSecondRow" Grid.Row="1" />
</Grid>
</Grid>
Finally, you need to track the size of the grid and change the sizing rules accordingly. This "pinning" code looks something like.
RowDefinition row = this.innerGrid.RowDefinitions[0];
if (row.Height.GridUnitType == GridUnitType.Auto)
{
if (this.innerGrid.ActualHeight > this.ActualHeight)
{
row.Height = new GridLength(1, GridUnitType.Star);
}
}
else
{
if (this.itemInTheFirstRow.DesiredSize.Height < row.ActualHeight)
{
row.Height = GridLength.Auto;
}
}
In my implementation, I wrap this code in an UpdateRowPinning
method that actually uses the dispatcher to call this code. I then call UpdateRowPinning
on resize events for the main grid and the inner grid as well as on adding and removing items from the grid and expand/collapse operations of grid groups. This ensures that the second row behaves properly by sitting at the base of the first row until the screen is full and then floating over it after that.
My answer here also covers this issue. I searched for a XAML only solution but it just doesn't seem possible (unless you write some XAML extensions, then you might be able to pull it off with XAML but that's kind of cheating).
精彩评论