简体   繁体   中英

How do you dynamically (via code) add custom Controls that were made in XAML in WPF?

I've constructed an XAML "template" Control via Visual Stduio (and Blend) and it's in XAML of course. When I search for how to dynamically add Controls into your interface, the answers always reveal constructing the Control dynamically then adding it. My question is how does one add an already made Control.

I'm very very new to WPF and C# in general. Perhaps I'm missing something. I went into this thinking I can create interfaces in Visual Studio with it's GUI and then do back-end code, but it seems there's a bit more to it that I'm not understanding. I'm trying to design a Control that's basically a "Search Thumbnail" or in other words, a colored Pane with an Image and a TextBox. I set the image to nothing and the box text to null thinking I could change these values in code. I'm getting a list of search results and trying to add a thumbnail Control for each result in what I'm assuming is a WrapPanel (I'm not sure if that's exactly what I want, but I believe it is).

Here's my thumbnail Control with it's 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;
    }
}

I am aware this code does not comply with the MVVM format thing, but I'm just trying out the quick and dirty way to get around this fast. I will obviously change it to the more respectable method in the future once I'm more comfortable with WPF.

Inside for loop for results that are getting retrieved properly:

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

I expected complete failure, but instead I got the Controls added into my main pane with what I'm assuming is 0 margins. No image is displayed and no text is changed.

结果

A UserControl should expose dependency properties like this:

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

Note that you must not explicitly set the UserControl's DataContext in its constructor, otherwise any standard DataContext-based Binding of the dependency properties won't work.

In its XAML, you bind to the UserControls own properties by eg RelativeSource Bindings:

<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>

As a note, the way you create a BitmapImage looks rather odd. Provided that you have a image URL, this is sufficient:

var bitmapImage = new BitmapImage(imgUrl);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM