Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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!
Comments
- Anonymous
December 18, 2014
Kind of makes you wish that there was a method named something like MeasureString that would figure out the dimensions a string of text would take up if rendered using a given font. :)