Introduction to the problem
I have a DataGrid
that has a DataGridTemplateColumn
, the template of which contains a ComboBox
control. My problem is that when the displayed member of the selected item is too long to fit into the width of the ComboBox
, then the width of the ComboBox
does not expand to accommodate the width of the displayed memeber, as it does if the same ComboBox
is not in the DataGrid
.
Working Example
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<DataGrid Height="150"
Margin="0,4,0,0"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
HorizontalAlignment="Stretch"
ColumnWidth="SizeToCells"
HeadersVisibility="Column"
AutoGenerateColumns="False"
RowHeaderWidth="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Entities, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=SelectedEntity}"
SelectionUnit="FullRow"
SelectionMode="Single">
<DataGrid.Resources>
<DataTemplate x:Key="TextBox_Template">
<TextBox Text="{Binding Path=Text}" Margin="2,2,2,2"/>
</DataTemplate>
<DataTemplate x:Key="ComboBox_Template">
<ComboBox Margin="2,2,2,2"
Width="Auto"
ItemsSource="{Binding Path=DataContext.AvailableActions,
Mode=OneTime,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
SelectedValue="{Binding Path=Action, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
IsEditable="False"
IsSynchronizedWithCurrentItem="False"/>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Width="*"
CanUserReorder="False" CanUserResize="False"
CellTemplate="{StaticResource TextBox_Template}"
Header="Text Field"/>
<DataGridTemplateColumn Width="Auto"
CanUserReorder="False" CanUserResize="False"
CellTemplate="{StaticResource ComboBox_Template}"
Header="Action"/>
</DataGrid.Columns>
</DataGrid>
<Separator Margin="0,5,0,5"/>
<StackPanel Orientation="Horizontal">
<Button Content="Add Row" Margin="2,2,2,2"
Command="{Binding AddRowCommand}"/>
<Button Content="Remove Row" Margin="2,2,2,2"
Command="{Binding RemoveRowCommand}"/>
</StackPanel>
<Separator Margin="0,5,0,5"/>
<ComboBox Width="Auto"
ItemsSource="{Binding Path=AvailableActions}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
IsEditable="False"
HorizontalAlignment="Left"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static Dictionary<ActionType, string> AvailableActions { get; set; }
public EntityClass SelectedEntity { get; set; }
public ObservableCollection<EntityClass> Entities { get; set; }
public AddRowCommandClass AddRowCommand { get; set; }
public RemoveRowCommandClass RemoveRowCommand { get; set; }
static MainWindow()
{
AvailableActions = new Dictionary<ActionType, string>()
{
{ActionType.Accept, "Accept the text"},
{ActionType.Reject, "Reject the text"},
{ActionType.Refer, "Refer the text"},
{ActionType.Postpone, "Postpone the text"},
};
}
public MainWindow()
{
Entities = new ObservableCollection<EntityClass>()
{
new EntityClass() { Text = "First Example Text", Action = ActionType.Accept},
new EntityClass() { Text = "Second Example Text", Action = ActionType.Reject},
new EntityClass() { Text = "Third Example Text", Action = ActionType.Refer},
};
AddRowCommand = new AddRowCommandClass(this);
RemoveRowCommand = new RemoveRowCommandClass(this);
InitializeComponent();
}
public enum ActionType
{
Accept,
Reject,
Refer,
Postpone,
}
public class EntityClass
{
public string Text { get; set; }
public ActionType Action { get; set; }
}
public class AddRowCommandClass : ICommand
{
public event EventHandler CanExecuteChanged;
private MainWindow _window;
public AddRowCommandClass(MainWindow window)
{
_window = window;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_window.Entities.Add(new EntityClass() { Text = "Hello World!", Action = ActionType.Postpone });
}
}
public class RemoveRowCommandClass : ICommand
{
public event EventHandler CanExecuteChanged;
private MainWindow _window;
public RemoveRowCommandClass(MainWindow window)
{
_window = window;
}
public bool CanExecute(object parameter)
{
return _window.SelectedEntity != null;
}
public void Execute(object parameter)
{
_window.Entities.Remove(_window.SelectedEntity);
_window.SelectedEntity = null;
_window.PropertyChanged?.Invoke(_window, new PropertyChangedEventArgs("SelectedEntity"));
}
}
}
}
Steps to reproduce
- First observe that the
ComboBox
at the bottom of the page changes size as the size of it's selected value changes. - Next add a new row to the
DataGrid
or edit theComboBox
in one of the already present rows to select the"Postpone the text"
option; notice how theComboBox
doesn't change size to fit the larger text, leading to the text being clipped. - Finally resize the window to a larger size with one of the rows having selected
"Postpone the text"
, and notice how theComboBox
now increases in width to accommodate the longer text without clipping.
Conclusion/Question
My question is how can I force the ComboBox
controls to automatically accommodate the width of their selected item, and increase the width of the grid row if necessary to do so. I want it such that the selected text will never be clipped, even if this means modifying the width of particular columns in the DataGrid
to accommodate.