如何从在XAML中定义的数据模板中以编程方式创建元素?

5
我将为您翻译以下内容:

我正在尝试在运行时使用代码创建多个带有文本的按钮,并将它们添加到Stackpanel中。我根据数据列表创建这些按钮。以下是我的代码:

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);
}

我知道这是笨拙的编程方式。我真的想在XAML中设置一个ControlTemplate,然后在代码中创建按钮时重复使用它。以下是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>

我尝试使用像上面那样的控件模板,然后在绑定方面,我在代码中设置了newbtn.DataContext。

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);
}

对于数据上下文,我也尝试过 newBtn.DataContext = ItemList[itemIndex],但似乎没有起作用。我只得到了一个空屏幕。

所以我通过纯代码创建按钮来解决了这个问题,但我正在尝试使用XAML控件模板使其工作。你有什么想法吗?

谢谢!

2个回答

3

在WPF中,使用程序代码创建或操作UI元素几乎从不是正确的方式。

虽然有一些例外情况可能会用到这种方法,但你所提出的情况是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>

代码后台:

    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.
    }
  • 使用ItemsControl时,让WPF为每个项创建UI而不是自己创建。
  • 每个按钮的DataContext也将由WPF设置为基础集合中相应的数据项。
  • 无需诉诸于使用Tag属性或其他任何可怕的黑客方法。
  • StackPanelItemsControl的默认ItemsPanel,但您可以通过设置ItemsPanel来将其更改为另一个面板(如WrapPanel

1

哎?不确定,但你为什么认为它那么糟糕呢。XamlReader非常强大,可以通过简单的字符串操作创建/加载动态XAML。在这个特定的问题中,它可以非常有用。也许它不是最好的方法,但肯定不是“可怕”的。 - AjS
我的观点仍然成立。你提供的解决方案完全不适合手头的问题。请看我的答案。而且,仅仅给出一个指向外部资源的链接作为答案也是非常糟糕的。 - Federico Berasategui
请发布相关代码,以解决OP的问题(包括将事件处理程序钩入“Button.Click”路由事件),使用您提出的解决方案,我将取消投票。 - Federico Berasategui

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接