views:

2223

answers:

3

I need to insert UIElements into a Grid that does not get generated until runtime. More specifically, I need to add UIElements to the RowDefinitions I create after I determine how many elements need to be displayed. Is there a way to contorl the Grid.Row and Grid.Column and Grid.RowSpan like in XAML for objects in C#? If I am going about this wrong, please let me know. I can not use a StackPanel (I am creating an dynamic accordian panel and it messes with the animation).

Right now what happens is that I generate the number of RowDefinitions at runtime and add UIElements as the children. This isn't working, all the UIElements end up in the first row layered on top of each other.

Here is an example of what I am trying:

public partial class Page : UserControl
{
    string[] _names = new string[] { "one", "two", "three" };
    public Page()
    {
        InitializeComponent();
        BuildGrid();
    }
    public void BuildGrid()
    {
        LayoutRoot.ShowGridLines = true;
        foreach (string s in _names)
        {
            LayoutRoot.RowDefinitions.Add(new RowDefinition());
            LayoutRoot.Children.Add(new Button());
        }
    }
}

Thanks!

A: 

I have never used this stuff, but shouldn't you be adding the Button to the RowDefinitions, instead of the Children? (just a logical observation)

leppie
Thats what I originally tried, but there is no real way to do that, that I know of. RowDefinitions are pretty simple classes from what I can tell.
Steve
+1  A: 

Apart from the Grid not really being the right tool for the job (a ListView would be) you need to tell the Button which row it belongs to.

public void BuildGrid()
{
    LayoutRoot.ShowGridLines = true;
    int rowIndex = 0;
    foreach (string s in _names)
    {
        LayoutRoot.RowDefinitions.Add(new RowDefinition());
        var btn = new Button()
        LayoutRoot.Children.Add(btn);
        Grid.SetRow(btn, rowIndex);
        rowIndex += 1;
    }
}
AnthonyWJones
+1  A: 

The best way to do what you're looking for is to follow the pattern below:

Page.xaml:

<UserControl x:Class="SilverlightApplication1.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <data:DataGrid x:Name="DataGridTest" />
    </Grid>
</UserControl>

Page.xaml.cs:

public partial class Page : UserControl
    {
        string[] _names = new string[] { "one", "two", "three" };

        public Page()
        {
            InitializeComponent();
            BuildGrid();
        }

        public void BuildGrid()
        {
            DataGridTest.ItemsSource = _names;
        }
    }

This builds the rows dynamically from the contents of your string array. In future an even better way would be to use an ObservableCollection where T implements INotifyPropertyChanged. This will notify the DataGrid to update it's rows if you remove or add an item from the collection and also when properties of T change.

To further customize the UIElements used to display things you can use a DataGridTemplateColumn:

<data:DataGridTemplateColumn Header="Symbol">
    <data:DataGridTemplateColumn.CellTemplate>
     <DataTemplate>
      <TextBlock Text="{Binding PutNameOfPropertyHere}" />
     </DataTemplate>
    </data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Peter McGrattan
This is probably how I should be doing it, but is more than I need right now, thank you though for the incredibly descriptive and well written answer. I'd up vote you but I don't have enough points.
Steve