在WPF C#中动态添加UserControl

时间:2018-03-27 18:28:22

标签: c# wpf

我的WPF应用程序中有一个页面应该按照我之前指定的数量显示一些“tile”。 Tile看起来像这样:

enter image description here

所以我的页面应该是这样的: enter image description here

当然可以通过手动克隆磁贴来实现,但我想避免这种情况(以更加编程的方式实现它)。因此,我应该只选择一个克隆而不是创建6个克隆,然后根据需要添加剩余的克隆。我怎么能做到这一点?我想我应该像这样创建自己的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>

然后将它们添加到我的页面。我还想提一下,在每个磁贴中都有4个文本框显示从Json解析的一些数据,所以也许一些自动绑定可以完成这项工作吗?

2 个答案:

答案 0 :(得分:1)

就这么简单。首先,你可以做的是,创建一个UserControl,其中包含所有控件,如TextBlock s和其他。然后,决定你想要哪种类型的容器控件用来保存你的UserControl。让我们假设它是一个网格。你可以为每个用户控件指定/设置网格的列/行。示例:

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

您可以在设计时创建网格行,或者也可以在代码后面创建网格行:

MyGrid.RowDefinitions.Add(new RowDefinition);

如果您想改用列,只需应用相同的代码,但使用Row / Rowdefinition

更改Column / ColumnDefinition

希望这会有所帮助:)

答案 1 :(得分:1)

以下示例显示了如何使用DataTemplateWrapPanel创建多个已发布的图块。 DataTemplate指定如何可视化对象(在本例中为TileItem)。您可以创建多个TileItem,然后将它们添加到集合中,以便将它们全部可视化。

假设您的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; }
}

您可以从JSON加载项目,并为JSON文档中的每个项目创建TileItemTileItems的课程可以在下面找到。

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

请注意,为了将datachanges传播到UI,在代码中更新它们时应在UI中更新的所有属性都需要引发属性更改事件。在此示例中,默认情况下所有属性都会执行此操作。

然后,您可以更新XAML以绑定到集合。 ItemsControl充当瓷砖的容器。如果您进一步向下滚动,您可能会注意到在调整控件大小时使用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>

每个Tile绑定到TileItem,这意味着绑定指向例如类别,指向TileItem的类别。

为了提高可重用性,可以将代码移动到自己的用户控件中,并可选择添加DependencyProperty以便更好地控制。