简体   繁体   English

在WPF C#中动态添加UserControl

[英]Add UserControl dynamically in WPF C#

I've got one page in my WPF app that should display some "tiles" in number as I specify before. 我的WPF应用程序中有一个页面,该页面应该显示一些“平铺”,如我之前指定的那样。 Tile looks like this: 磁贴看起来像这样:

在此处输入图片说明

So my page should look something like this: 所以我的页面应该看起来像这样: 在此处输入图片说明

It is achievable of course by manually cloning tiles, but I want to avoid this (achieve it in more programmatic way). 当然,可以通过手动克隆图块来实现,但是我想避免这种情况(以更多编程方式实现)。 So instead of creating 6 clones I should stick to only one and then if needed add remaining ones. 因此,我应该只保留一个克隆,而不是创建6个克隆,然后根据需要添加其余的克隆。 How can I accomplish that? 我该怎么做? I guess I should create my own UserControl like this: 我想我应该像这样创建自己的UserControl:

 <Grid HorizontalAlignment="Left" Height="199" VerticalAlignment="Top" Width="207" Background="Black">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="0*"/>
        </Grid.RowDefinitions>
        <Image x:Name="image1" HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="207" Stretch="UniformToFill"/>
        <Grid HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="112" Background="#FFC78D10">
            <TextBox  IsReadOnly = "True"  x:Name="CategoryOfEvent" Height="30" TextWrapping="Wrap" Text="Category" Width="112" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="White" FontSize="18" SelectionBrush="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Top" >
                <TextBox.Template>
                    <ControlTemplate TargetType="{x:Type TextBox}">
                        <ScrollViewer Name="PART_ContentHost"/>
                    </ControlTemplate>
                </TextBox.Template>

            </TextBox>
        </Grid>
        <TextBox  IsReadOnly = "True"  x:Name="HourOfEvent"  HorizontalAlignment="Left" Height="28" Margin="0,42,0,0" TextWrapping="Wrap" Text="Hour" VerticalAlignment="Top" Width="148" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="#FFE2E2E2" FontSize="22" SelectionBrush="{x:Null}" FontWeight="Bold" TextChanged="HourOfEvent_TextChanged">
            <TextBox.Template>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <ScrollViewer Name="PART_ContentHost"/>
                </ControlTemplate>
            </TextBox.Template>
        </TextBox>
        <TextBox  IsReadOnly = "True" x:Name="TitleOfEvent" HorizontalAlignment="Left" Height="88" Margin="0,82,0,0" TextWrapping="Wrap" Text="Title" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="20" SelectionBrush="{x:Null}" FontWeight="Bold">
            <TextBox.Template>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <ScrollViewer Name="PART_ContentHost"/>
                </ControlTemplate>
            </TextBox.Template>
        </TextBox>
        <TextBox  IsReadOnly = "True"  x:Name="PlaceOfEvent" HorizontalAlignment="Left" Height="24" Margin="0,175,0,0" TextWrapping="Wrap" Text="Where" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="White" FontSize="14" SelectionBrush="{x:Null}">
            <TextBox.Template>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <ScrollViewer Name="PART_ContentHost"/>
                </ControlTemplate>
            </TextBox.Template>
        </TextBox>

    </Grid>

and just add them to my page. 并将它们添加到我的页面中。 I would like also to mention that in every tiles there are 4 textboxes which are displaying some data parsed from Json, so maybe some automatic binding should do the job? 我还要提及的是,在每个图块中都有4个文本框,这些文本框显示了从Json解析的一些数据,因此也许可以执行一些自动绑定吗?

It is as simple as that.Firstly,what you can do is,create a UserControl with all your controls inside like TextBlock s and others.Then,decide which type of container control you want to use to hold your UserControl .Let's assume it's a grid.You can specify/set grid's column/rows for each user control.A sample : 就是这么简单。首先,您可以做的是创建一个UserControl其中包含所有控件,例如TextBlock和其他控件。然后,确定要用于保存UserControl的容器控件类型。让我们假设它是一个您可以为每个用户控件指定/设置网格的列/行。

private void addControl()
{
UserControl1 MyCon = new UserControl1;
MyGrid.Children.Add(MyCon);
Grid.SetRow(MyCon , 1); ////replace 1 with required row count
}

You can create grid rows in design time,or u can do it in code behind as well : 您可以在设计时创建网格行,也可以在后面的代码中创建网格行:

MyGrid.RowDefinitions.Add(new RowDefinition);

If you want to use columns instead,just apply same code but change Row / Rowdefinition with Column / ColumnDefinition 如果你想使用的列,而不是,只是采用相同的代码,但改变Row / RowdefinitionColumn / ColumnDefinition

Hope this helps :) 希望这可以帮助 :)

The follwing example shows how to create multiple of the tiles you have been posting using a DataTemplate and WrapPanel . 以下示例显示了如何使用DataTemplateWrapPanel创建多个已发布的图块。 The DataTemplate specifies how an object (in this case a TileItem ) is visualized. DataTemplate指定如何显示对象(在本例中为TileItem )。 You can create multiple TileItem s and then add them to an collection, in order to visualize them all. 您可以创建多个TileItem ,然后将它们添加到集合中,以使它们全部可视化。

Assuming your UI resides in MainWindow, you can create a collection with three items in it. 假设您的UI位于MainWindow中,则可以创建一个包含三个项目的集合。

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        TileItemCollection = new ObservableCollection<TileItem>(new []
        {
            new TileItem(){Category = "Alpha", Hour = "10", Title = "Hello World", Where = "Office"}, 
            new TileItem(){Category = "Beta", Hour = "15", Title = "Test", Where = "Home"}, 
            new TileItem(){Category = "Gamma", Hour = "44", Title = "My Title", Where = "Work"}, 
        });

        DataContext = this;
    }

    public ObservableCollection<TileItem> TileItemCollection { get; }
}

You could load your Items from JSON and create an TileItem for each one in the JSON document. 您可以从JSON加载项目,并为JSON文档中的每个项目创建一个TileItem The class for TileItems s can be found below. TileItems的类可以在下面找到。

public class TileItem : INotifyPropertyChanged
{
    private string _hour;
    private string _title;
    private string _where;
    private string _category;

    public string Category
    {
        get => _category;
        set
        {
            if (value == _category) return;
            _category = value;
            OnPropertyChanged();
        }
    }

    public string Hour
    {
        get => _hour;
        set
        {
            if (value == _hour) return;
            _hour = value;
            OnPropertyChanged();
        }
    }

    public string Title
    {
        get => _title;
        set
        {
            if (value == _title) return;
            _title = value;
            OnPropertyChanged();
        }
    }

    public string Where
    {
        get => _where;
        set
        {
            if (value == _where) return;
            _where = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Note that in order for datachanges to be propagated to the UI, all properties which should be updated in the UI when you update them in code need to raise the property changed event. 请注意,为了将数据更改传播到UI,在代码中更新属性时应在UI中更新的所有属性都需要引发属性更改事件。 In this example all properties do this by default. 在此示例中,所有属性默认情况下都执行此操作。

You can then update the XAML to bind to a collection. 然后,您可以更新XAML以绑定到集合。 The ItemsControl acts as a container for the tiles. ItemsControl充当图块的容器。 If you scroll down further you may notice the use of WrapPanel which is responsible for the item wrapping effect when you resize the control. 如果向下滚动,您可能会注意到WrapPanel的使用,当您调整控件的大小时, WrapPanel会导致项目包装效果。

<ItemsControl ItemsSource="{Binding TileItemCollection}" Margin="20">
    <ItemsControl.ItemTemplate>
        <DataTemplate  DataType="{x:Type local:TileItem}" >
            <Grid HorizontalAlignment="Left" Height="199" VerticalAlignment="Top" Width="207" Background="Black">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition Height="0*"/>
                </Grid.RowDefinitions>
                <Image x:Name="image1" HorizontalAlignment="Left" Height="175" VerticalAlignment="Top" Width="207" Stretch="UniformToFill"/>
                <Grid HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="112" Background="#FFC78D10">
                    <TextBox IsReadOnly="True" Height="30" TextWrapping="Wrap" Text="{Binding Path=Category}" Width="112" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="White" FontSize="18" SelectionBrush="{x:Null}" HorizontalAlignment="Left" VerticalAlignment="Top" >
                        <TextBox.Template>
                            <ControlTemplate TargetType="{x:Type TextBox}">
                                <ScrollViewer Name="PART_ContentHost"/>
                            </ControlTemplate>
                        </TextBox.Template>
                    </TextBox>
                </Grid>
                <TextBox IsReadOnly="True" HorizontalAlignment="Left" Height="28" Margin="0,42,0,0" TextWrapping="Wrap" Text="{Binding Path=Hour}" VerticalAlignment="Top" Width="148" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="#FFE2E2E2" FontSize="22" SelectionBrush="{x:Null}" FontWeight="Bold">
                    <TextBox.Template>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <ScrollViewer Name="PART_ContentHost"/>
                        </ControlTemplate>
                    </TextBox.Template>
                </TextBox>
                <TextBox IsReadOnly="True" HorizontalAlignment="Left" Height="88" Margin="0,82,0,0" TextWrapping="Wrap" Text="{Binding Path=Title}" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="White" FontSize="20" SelectionBrush="{x:Null}" FontWeight="Bold">
                    <TextBox.Template>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <ScrollViewer Name="PART_ContentHost"/>
                        </ControlTemplate>
                    </TextBox.Template>
                </TextBox>
                <TextBox IsReadOnly="True"  x:Name="PlaceOfEvent" HorizontalAlignment="Left" Height="24" Margin="0,175,0,0" TextWrapping="Wrap" Text="{Binding Path=Where}" VerticalAlignment="Top" Width="207" Background="{x:Null}" BorderBrush="{x:Null}"  Foreground="White" FontSize="14" SelectionBrush="{x:Null}">
                    <TextBox.Template>
                        <ControlTemplate TargetType="{x:Type TextBox}">
                            <ScrollViewer Name="PART_ContentHost"/>
                        </ControlTemplate>
                    </TextBox.Template>
                </TextBox>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
            <ScrollViewer>
                <ItemsPresenter />
            </ScrollViewer>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>

Each Tile is bound to an TileItem which means that the Bindings which point to eg Category, point to the Category of an TileItem. 每个图块都绑定到一个TileItem ,这意味着指向例如Category的Bindings指向TileItem的Category。

To increase reusability it would be possible to move the code into its own usercontrol and optionally to add DependencyProperty s for better control. 为了提高可重用性,可以将代码移到其自己的usercontrol中,并可以选择添加DependencyProperty以获得更好的控制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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