您如何在 WPF 中动态(通过代码)添加在 XAML 中制作的自定义控件?

Posted

技术标签:

【中文标题】您如何在 WPF 中动态(通过代码)添加在 XAML 中制作的自定义控件?【英文标题】:How do you dynamically (via code) add custom Controls that were made in XAML in WPF? 【发布时间】:2019-07-12 03:57:06 【问题描述】:

我已经通过 Visual Stduio(和 Blend)构建了一个 XAML“模板”控件,它当然是在 XAML 中。当我搜索如何将控件动态添加到您的界面时,答案总是显示动态构造控件然后添加它。我的问题是如何添加一个已经制作好的控件。

总的来说,我对 WPF 和 C# 非常陌生。也许我错过了一些东西。我开始思考我可以使用它的 GUI 在 Visual Studio 中创建界面,然后执行后端代码,但似乎还有更多我不理解的地方。我正在尝试设计一个基本上是“搜索缩略图”的控件,或者换句话说,一个带有图像和文本框的彩色窗格。我将图像设置为空,将框文本设置为空,认为我可以在代码中更改这些值。我正在获取搜索结果列表,并尝试在我假设是 WrapPanel 的每个结果中添加缩略图控件(我不确定这是否正是我想要的,但我相信它是)。

这是我的缩略图控件,它是 XML

<UserControl x:Class="ChaCha.SearchThumbnail"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:ChaCha"
         mc:Ignorable="d" 
         d:DesignHeight="250 " d:DesignWidth="150"
                              TextElement.Foreground="DynamicResource MaterialDesignBody"
        Background="#FF3C3C3C"
    TextElement.FontWeight="Medium"
    TextElement.FontSize="14"
    FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
    >
<Grid x:Name="thumbnailGrid" Background="#FF004D40">
    <Image Source="Binding thumbnailPath" HorizontalAlignment="Left" Height="130" Margin="10,10,0,0" VerticalAlignment="Top" Width="130"/>
    <TextBlock x:Name="txt" HorizontalAlignment="Left" Margin="10,145,0,0" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="14" Height="95" Width="130" FontWeight="Normal" Text="Binding thumbnailTxt" TextAlignment="Center"/>

</Grid>

/// <summary>
/// Interaction logic for SearchThumbnail.xaml
/// </summary>
public partial class SearchThumbnail : UserControl


    public ImageSource ThumbnailPath  get; set; 
    public string ThumbnailTxt  get; set; 

    public SearchThumbnail()
    
        InitializeComponent();
        DataContext = this;
    

我知道这段代码不符合 MVVM 格式的规定,但我只是在尝试快速而肮脏的方法来快速解决这个问题。一旦我对 WPF 更熟悉了,我显然会在未来将其更改为更受人尊敬的方法。

在 for 循环中获取正确检索的结果:

                    // Getting a compatible Image object for the SearchThumbnail image Pane. Code from another stack overflow thread
                    var imgUrl = new Uri(thumbnail);
                    var imageData = new WebClient().DownloadData(imgUrl);

                    var bitmapImage = new BitmapImage  CacheOption = BitmapCacheOption.OnLoad ;
                    bitmapImage.BeginInit();
                    bitmapImage.StreamSource = new MemoryStream(imageData);
                    bitmapImage.EndInit();

                    // OBVIOUSLY FAILED LAST ATTEMPT HERE.
                    var thumb = new SearchThumbnail()
                    
                    ThumbnailTxt = title,
                    ThumbnailPath = bitmapImage
                    ;
                    this.wrapPanel.Children.Add(thumb);

我预计完全失败,但我将控件添加到我的主窗格中,我假设边距为 0。不显示图像,也不更改文本。

【问题讨论】:

不要向我们展示您的 XAML 图像。它不可读,动画很烦人。相反,将 XAML 添加到您的问题中,就像您的代码一样。 @Clemens 没有考虑添加它。已修复,谢谢。 【参考方案1】:

UserControl 应该像这样公开依赖属性:

public partial class SearchThumbnail : UserControl

    public SearchThumbnail()
    
        InitializeComponent();
    

    public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
        nameof(Image), typeof(ImageSource), typeof(SearchThumbnail));

    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        nameof(Text), typeof(string), typeof(SearchThumbnail));

    public ImageSource Image
    
        get  return (ImageSource)GetValue(ImageProperty); 
        set  SetValue(ImageProperty, value); 
    

    public string Text
    
        get  return (string)GetValue(TextProperty); 
        set  SetValue(TextProperty, value); 
    

请注意,您不能在其构造函数中显式设置 UserControl 的 DataContext,否则任何标准的基于 DataContext 的依赖属性绑定都将不起作用。

在其 XAML 中,您可以通过例如绑定到 UserControls 自己的属性。相对源绑定:

<UserControl ...>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Image Grid.Row="0"
            Source="Binding Image, RelativeSource=RelativeSource AncestorType=UserControl"/>
        <TextBlock Grid.Row="1"
            Text="Binding Text, RelativeSource=RelativeSource AncestorType=UserControl"/>
    </Grid>
</UserControl>

请注意,创建 BitmapImage 的方式看起来很奇怪。只要您有图片 URL,这就足够了:

var bitmapImage = new BitmapImage(imgUrl);

【讨论】:

完美运行。谢谢你。绝对有见地以及这个框架的使用。 进一步阅读的良好起点是Control Authoring Overview

以上是关于您如何在 WPF 中动态(通过代码)添加在 XAML 中制作的自定义控件?的主要内容,如果未能解决你的问题,请参考以下文章

WPF的TreeView通过数据库动态添加修改删除节点功能

如何动态添加/删除行? [WPF]

C# WPF后台代码动态添加控件

C# wpf listview 如何动态添加 高手回答下

WPF C# 如何在动态添加的grid控件中添加某个网格中的image控件的单击事件?

C# WPF后台动态添加控件(经典)