Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you create an element programmatically from a data template defined in XAML?

I'm trying to create multiple button with text underneath and have them added to a Stackpanel in code during runtime. I create the buttons based on a List of data. Here's my code:

XAML

<StackPanel x:Name="MainSP" />

C#

foreach (item in ItemList)
{
    Button newBtn = new Button();
    Image buttonImage = new Image();
    buttonImage.Width = 100;
    buttonImage.Height = 100;
    buttonImage.Stretch = Systems.Windows.Media.Stretch.Uniform;
    buttonImage.Source = new BitmapImage(pokemon.ImageURI);
    newBtn.Tag = item.Id;
    newBtn.Name = String.Format("{0}Button", item.Name);
    newBtn.Click += new RoutedEventHandler(newBtn_Click);

    FamilyStackPanel.Children.Add(newBtn);
}

I know this is clunky programming. I really want to set up a ControlTemplate in XAML then reuse that when creating the buttons in code. Here's the XAML:

<ControlTemplate x:Key="ButtomTemplate" TargetType="Button">
        <Grid Height="108" Width="Auto" Margin = "6,6">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Image Width = "54" Height="54" Stretch="UniformToFill" Grid.Row="0">
                <Image.Source>
                    <BitmapImage UriSource="{Binding ImageUri}" CreateOptions="BackgroundCreation"/>
                </Image.Source>
            </Image>
            <TextBlock Text="{Binding Name}"
                    Foreground ="{StaticResource PhoneAccentBrush}" />
        </Grid>
</ControlTemplate>

I've tried using the controlTemplate like above and then for the bindings, I set the newbtn.DataContext in code.

foreach (Families item in ItemList)
{
    Button newBtn = new Button();
    newBtn.Template = this.Resources["ButtonTemplate"] as ControlTemplate;
    newBtn.Tag = item.Id;
    newBtn.Name = String.Format("{0}Button", item.Name);
    newBtn.Click += new RoutedEventHandler(newBtn_Click);
    newBtn.DataContext = item;

    FamilyStackPanel.Children.Add(newBtn);
}

For the data context I've also attempted newBtn.DataContext = ItemList[itemIndex] but that didn't seem to work. All I get is a blank screen.

So I've figured it out through pure code behind button creation, but I'm trying to use XAML control templates to make it work. Have any ideas?

Thank you!

like image 443
Ebsan Avatar asked Oct 02 '22 04:10

Ebsan


2 Answers

Creating or manipulating UI elements in procedural code in WPF is almost never the correct way.

There are exceptions where that would be useful, but this case you're presenting is the simplest case for the ItemsControl.

<ItemsControl ItemsSource="{Binding ItemList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Click="Button_OnClick">
                <Button.Template>
                    <ControlTemplate TargetType="Button">
                        <Grid Height="108" Width="Auto" Margin = "6,6">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Image Width = "54" Height="54" Stretch="UniformToFill" Grid.Row="0">
                                <Image.Source>
                                    <BitmapImage UriSource="{Binding ImageUri}" CreateOptions="BackgroundCreation"/>
                                </Image.Source>
                            </Image>
                            <TextBlock Text="{Binding Name}"
                                       Foreground ="{StaticResource PhoneAccentBrush}" />
                        </Grid>
                    </ControlTemplate>
                </Button.Template>
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Code Behind:

    private void Button_OnClick(object sender, RoutedEventArgs e)
    {
        var button = sender as Button;
        var dataitem = button.DataContext as [YourItemclass];

        //... here you can perform actions on the dataitem.
    }
  • Using an ItemsControl, your let WPF create the UI for each item instead of creating it yourself.
  • Each Button's DataContext will be also set by WPF to the corresponding data item in the underlying collection.
  • No need to resort to horrible hacks of using the Tag property or anything like that.
  • The StackPanel is the default ItemsPanel for the ItemsControl, but you can change that to another panel (such as WrapPanel) by setting the ItemsPanel
like image 92
Federico Berasategui Avatar answered Oct 09 '22 02:10

Federico Berasategui


You can use XamlReader to create xaml with dynamic content. This way you will be able to set any of the property at runtime.

http://msdn.microsoft.com/en-us/library/system.windows.markup.xamlreader(v=vs.110).aspx

like image 32
AjS Avatar answered Oct 09 '22 04:10

AjS