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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20591023/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:12:51  来源:igfitidea点击:

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

c#wpfxamlwindows-phone-8visual-studio-2013

提问by WiteCastle

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:

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

XAML

XAML

<StackPanel x:Name="MainSP" />

C#

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:

我知道这是笨拙的编程。我真的很想在 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>

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

我试过使用上面的 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);
}

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.

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

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?

所以我已经通过按钮创建背后的纯代码弄清楚了,但我正在尝试使用 XAML 控件模板来使其工作。有什么想法吗?

Thank you!

谢谢!

采纳答案by Federico Berasategui

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

在 WPF 的程序代码中创建或操作 UI 元素几乎never是正确的方法。

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

有一些例外情况会很有用,但是您呈现的这种情况是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 DataContextwill be also set by WPF to the corresponding data item in the underlying collection.
  • No need to resort to horrible hacks of using the Tagproperty or anything like that.
  • The StackPanelis the default ItemsPanelfor the ItemsControl, but you can change that to another panel (such as WrapPanel) by setting the ItemsPanel
  • 使用 ItemsControl,您可以让 WPF 为每个项目创建 UI,而不是自己创建。
  • 每个 ButtonDataContext也将由 WPF 设置为基础集合中的相应数据项。
  • 无需诉诸可怕的黑客使用该Tag财产或类似的东西。
  • StackPanel是默认ItemsPanelItemsControl,但你可以改变到另一个面板(如WrapPanel通过设置)ItemsPanel

回答by AjS

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

您可以使用 XamlReader 创建具有动态内容的 xaml。这样您就可以在运行时设置任何属性。

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

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