Reputation: 1171
I've got one page in my WPF app that should display some "tiles" in number as I specify before. 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. How can I accomplish that? I guess I should create my own UserControl like this:
<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?
Upvotes: 0
Views: 1533
Reputation: 1427
The follwing example shows how to create multiple of the tiles you have been posting using a DataTemplate
and WrapPanel
. The DataTemplate specifies how an object (in this case a TileItem
) is visualized. You can create multiple TileItem
s and then add them to an collection, in order to visualize them all.
Assuming your UI resides in MainWindow, you can create a collection with three items in it.
/// <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. The class for TileItems
s can be found below.
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. In this example all properties do this by default.
You can then update the XAML to bind to a collection. The ItemsControl
acts as a container for the tiles. 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.
<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 e.g. Category, point to the Category of an TileItem.
To increase reusability it would be possible to move the code into its own usercontrol and optionally to add DependencyProperty
s for better control.
Upvotes: 1
Reputation: 5758
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 :
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
Hope this helps :)
Upvotes: 1