How to Dynamically size a data bound control by it's content

I've seen this question posted on the web, and I have been personally asked to do this a couple of times.

 

There comes a time when we will need to have buttons (or radioButtons, any control) that needs to have it's width dynamically assigned based on the content within, for example a button with the text "MyButton, should be wide enough to display all the text, but another button next to it should have the same padding but a smaller width if it's content was "Small". This gets even more confusing when we have a list of items that are data bound. You cannot set individual widths for items that are not yet created.

 

I'm going to show you how you can accomplish this using a MVVM Converter (https://msdn.microsoft.com/en-us/library/system.windows.data.binding.converter(v=vs.110).aspx).

 

First let's define our list: 

 

public ObservableCollection<string> ButtonNames       
{
     get { return _buttonNames; }
}

private ObservableCollection<string> _buttonNames = new ObservableCollection<string>();

 

Let's add some strings to our ButtonNames dependency property:

 

 _buttonNames.Add("Short");
_buttonNames.Add("LongerName");
OnPropertyChanged("ButtonNames");

 

Pretty simple.. now we'll create an ItemsControl to bind to our ButtonNames collection:

 

<ItemsControl ItemsSource="{Binding ButtonNames}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Width="50" Height="30" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

If we run this, we see our problem:

 

 

 

The buttons are stuck with the wrong width.(say that fast three times!)

 

To fix this, I am going to bind the width of our button to the string we are using for content. The problem is, the property is expecting an int, So we need a converter:

 

class WidthHelper : IValueConverter
{
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
                int nameLength = value.ToString().Length;
return (14 * nameLength) + 20;
}

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
                throw new NotImplementedException();
}
}

 

What's great about the implemented Convert function is that the value coming in is an object, so we can pass in a string.. or whatever we want. As long as we match the targetType.. we're good. So as you can see I multiply 14 (that's the font size I am using) times the number of characters in the name, I then put a 10 pixel buffer on each side of the text (+20).

I can now wire up ourconverter to our ItemsControl data template:

 

<Button Content="{Binding}" Width="{Binding Converter,Converter={StaticResource WidthHelper}}" Height="30" />

 

Our buttons now display with the dynamic width:

 

 

Pretty cool!

 

 Hope this helps!