I'm trying to build a GUI similar to the one on the first screenshot:
Basically it's a view of tiles, displayed in a bordered grid that dynamically changes number of columns as the window is resized. The items are selectable. Those may look like pictures, but they are just Unicode characters.
I've made a List of custom objects, and bound the list to a ListBox control successfully. Naturally, this gave me a vertically stacked list of characters.
So I googled some, and found out I could set a custom ItemsPanelTemplate for the list box:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="0"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
In order to set the border and size of the tiles, I've also added an ItemTemplate:
<ListBox Margin="0,0,0,0" Padding="0,0,0,0" Grid.Row="0" Grid.Column="0" x:Name="tw"
SelectionChanged="tw_SelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="0,0,0,0"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Label
Padding="0,0,0,0"
Margin="0,0,0,0"
Width="50"
Height="50"
FontSize="20"
Background="White"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
BorderThickness="1"
BorderBrush="Black"
Content="{Binding Path=LiteralString}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The result was the following:
This is as far as I got.
The first problem is, as you can see, there are spaces between the tiles that I can't get rid of. You'll notice in the markup I've tried manually setting Margin and Padding to 0 wherever possible. The spaces are still there.
The second problem is, you can't really see which item is selected. The 3rd character from the bottom and 2nd from the right is selected, but the blue color is covered by the label. You can only just see it in the space to the left of the character. I have no idea how to fix this.
Another potential problem is performance. The view will eventually have filters, but when all the filters are off, there are some 6500 items to display. When I had just a basic ListBox, the application was taking up some 28MB of RAM while it was running. When I added the ItemsPanelTemplate with the WrapPanel, memory usage jumped to about 136MB. When I added an ItemTemplate with Labels, memory usage jumped to 183MB, and what's worse, the window now takes much longer to load.
I've tried replacing Labels with TextBlocks, since I've read they're much more lightweight. The thing got faster, and memory usage dropped back to around 130MB, but TextBlocks are no good. I can't center text in them, or give them borders. And as you can see in the screenshot, the selected item just gets messed up:
Granted, I have a slow computer, and 183MB is not life-ending, but still, it's a bit inefficient. I intend to put a lot more functionality in this application, and if this one thing takes up so much RAM, the resulting application will likely be an unusable resource vampire. Also, I believe the application I'm mimicking is also written in .NET, (although I'm not sure if it's WPF) and that one only uses 17.5MB of RAM while displaying the view.
Is there another way to do this, one that's both more efficient and more easily customizable?
Thank you.
EDIT:
I have found a slightly better way. I removed the ItemTemplate altogether, and instead I'm using ItemContainerStyle:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Setters>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Height" Value="50"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True" >
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Background" Value="SteelBlue" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
This produces much better results. There are no visible spaces between the tiles, and I can customize the look of both selected and deselected tiles.
The only thing I don't know how to do is have a border line with the thickness of 1 pixel. I get two lines with thickness of 1 touching, resulting in a thickness of 2. I've tried setting margins and padding to 0 and -1. No luck. Curiously, setting BorderBrush to Black and BorderThickness to 0.5 doesn't produce a combined black line with thickness of 1, but a grey line with thickness of 2.
Memory usage dropped somewhat to about 150MB compared to when I was using labels, but load times are still atrocious, so I'm still looking for a better solution.
EDIT 2:
I played with the margins for a while, and I finally nailed it. I got the correct border thickness by setting the margin of the ListBoxItem to -0.5, squishing the tiles ever so slightly together. I would like to keep the question open, so that hopefully somebody will present a more efficient solution. The UI is responsive once it's running, but the load time is terrible. During the use of my application the ListBox will be repeatedly re-filled, as filters are adjusted and queries are made. I would like that to happen quickly. Also, memory usage is unnecessarily high in my opinion.