0

I'm having a problem aligning string contents in a listbox. Here's a distilled example that demonstrates the problem my app is having:

Basic XAML UI:

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="687" Loaded="Window_Loaded">
    <Grid>
        <ListBox x:Name="lstClients" HorizontalAlignment="Left" Height="220" Margin="28,22,0,0" VerticalAlignment="Top" Width="625"/>
    </Grid>
</Window>

Code behind:

Class MainWindow
    Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
        Dim names() As String = {"Cook, Adam", "Renolds, Bridgette", "Smith, Carla", "Doe, Daniel",
                                "Redmond, Ebenezer", "Kelly, Francine"}
        Dim ids() As String = {"123456", "654321", "112345", "274587", "128493", "937401"}
        Dim dob() As String = {"1/2/80", "4/17/88", "8/31/72", "6/11/91", "10/27/81", "3/3/75"}
        Dim phones() As String = {"1234567890", "2536772364", "1537779004", "7586712164", "8548778384", "0987654321"}

        For i As Integer = 0 To 5
            lstClients.Items.Add(FormatClientString(names(i), ids(i), dob(i), phones(i)))
        Next
    End Sub

    Private Function FormatClientString(name As String, id As String, birthDate As String, phone As String)
        Dim clientString As String
        clientString = String.Format("{0, -52} {1, -17} {2, -20} {3}", name, id, birthDate, phone)
        Return clientString
    End Function
End Class

Output: output

And here is what I am actually trying to accomplish (crappy paint editing but basically I just want the columns to align, regardless of the length of the client's name): desiredOutput

From what I read, String.Format should do what I want, but the output is never right. I also tried using String.PadRight for each string (names, ids, dob, and phones) but got the same behavior (misaligned columns).

Am I overlooking something simple?

EDIT: The control in my actual app is not a ListBox. It is an AutoCompleteBox from the WPF Toolkit. I used the ListBox above as an example because it was exhibiting the same behavior formatting the strings and was easier to make a simple example with.

If anyone knows how to add columns to the AutoCompleteBox's dropdown suggestions, that would work too as this is ultimately the goal.

Lews Therin
  • 3,707
  • 2
  • 27
  • 53
  • 1
    Proportional width fonts doesn't have the same width for each letter and space. But why don't you use a ListView instead? – Steve Oct 22 '15 at 19:35
  • @Steve In actuality it is an AutoCompleteBox from the [WPF Toolkit](http://wpf.codeplex.com/releases/view/40535) but the mis-alignment behavior is the same in a ListBox (I'm using a `List(Of String)` for the AutoCompleteBox's `ItemsSource`). – Lews Therin Oct 22 '15 at 19:39
  • 1
    As I have said. The problem is caused by the font used by your control. It is a proportional font so you cannot create columns of a certain width using spaces to fill the missing characters. Try to set a fixed width font like Consolas and you will see the difference. The simple solution is to use a control that 'knows' what a 'multi columns' means. – Steve Oct 22 '15 at 19:45
  • 1
    I deleted my asnwer because you updated the question – Giangregorio Oct 22 '15 at 19:48
  • @Steve You nailed it. I changed it to Consolas and it worked. Submit this as an answer and I'll mark it. – Lews Therin Oct 22 '15 at 19:49

2 Answers2

1

The problem is caused by the font used by your control. It is a proportional font so you cannot create columns of a certain width using spaces to fill the column because every line has a different length in pixels of the text part.

Setting a fixed width font like Consolas (where every character has the same width in pixels) will fix the problem.

However the fixed width font is not very pleasant to see and a better solution is to use a control that 'knows' what a 'multi columns' means. (ListView, DataGrid)

Steve
  • 213,761
  • 22
  • 232
  • 286
1

For those who want to show string.Format with proportional fonts, try this XAML (which is just for fun, but may be used in some simple apps which don't require much flexibility in styling). Using this code, we may need to tweak the fixed Width depending on the chosen font:

<ListBox x:Name="lstClients" HorizontalAlignment="Left" Height="220" Margin="28,22,0,0" 
         VerticalAlignment="Top" Width="625" FontFamily="Lucida Calligraphy">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <ItemsControl ItemsSource="{Binding}">                        
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border Width="20" Margin="0,0,-12,0">
                                <TextBlock Text="{Binding}" TextAlignment="Center"/>
                            </Border>                                
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </DataTemplate>
        </ListBox.ItemTemplate>
</ListBox>

The idea here is each string is an array of characters. So we define an ItemTemplate foreach data item (a string). The template should be an ItemsControl to show a list of characters in a custom ItemsPanelTemplate to stack those characters horizontally (to join in a readable text). Because each character is a Border having fixed width, it can be used with any font with appropriate Width set for Border (and its Margin). For wrapping text, we may need to play with WrapPanel instead. But anyway it's some kind of funny trick and should not be used in important business apps.

enter image description here

King King
  • 61,710
  • 16
  • 105
  • 130