Search code examples
c#wpfdatagrid

WPF DatagridComboboxcolumn not in combobox style


This has been a long problem of mine, and even though there are a lot of solutions out there, none has seemed to work for me.

How do I make the DataGridComboBoxColumn display as Combobox when it is not focused? I tried using DataGridTemplateColumn and I've almost been successful in it except that when trying to identify the selected item in the Combobox, the value returns back to previous data, and honestly made it a lot harder for me to work on this compared to just using DataGridComboBoxColumn.

How can I do this without using DataGridTemplateColumn? The DataGrid is found in a UserControl which is dynamically added to the MainWindow.

XAML

<!-- This is my preferred way-->
<DataGridComboBoxColumn Header TagType x:Name="TagTypeCombo" SelectedValueBinding="{Binding TagType}"/>

<!--This is similar to all of solutions that I've seen-->
<DataGridTemplateColumn Header="Tag Type" x:Name="ComboTemplate">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox Width="135" x:Name="Tagger" ItemsSource="{Binding tagType,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}" SelectedItem="{Binding Tags}" SelectedValuePath="TagType"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Code-Behind

public int counter;
List<Tags> tags = new List<Tags>();
public event EventHandler SaveButtonClicked;
public List<string> tagType = new List<string>() { "Modbus Coil(0x)", "Discrete Input(1x)", "Input Register(3x)", "Holding Register(4x)" };
public ModbusCard2()
{
    InitializeComponent();
}
public class Tags
{
    public int TagAddress { get; set; }
    public string TagType { get; set; }
    public string TagID { get; set; }
}
private void DeleteButton_Click(object sender, RoutedEventArgs e) => ((WrapPanel)this.Parent).Children.Remove(this);
private void Tagger_Loaded(object sender, RoutedEventArgs e)
{
    var cx = sender as ComboBox;
    cx.ItemsSource = tagType;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    for (int i = 1; i <= counter; i++)
    {
        tags.Add(new Tags() { TagAddress = 0, TagID = $"M{i}", TagType = "Discrete Input(1x)" });
    }
   DevTags.ItemsSource = tags;
}

private void SaveButton_Click(object sender, RoutedEventArgs e)
{
    SaveButtonClicked?.Invoke(sender, e);
}

private void EditButton_Click(object sender, RoutedEventArgs e) => DevTags.IsReadOnly = false;

//For the SaveButtonClicked found in MainWindow
private void SaveButtonClicked(ModbusCard2 d)
{
    foreach (var a in d.DevTags.Items.OfType<Tags>().ToList())
    {
        if (a.TagType == d.tagType[1])
        {
            a.TagID = $"M{DICount}";
            DICount++;
        }
        if (a.TagType == d.tagType[2])
        {
            a.TagID = $"R{AICount}";
            AICount++;
        }
        d.DevTags.Items.Refresh();
    }
    d.DevTags.IsReadOnly = true;
}

Solution

  • Try this with your ComboBox that's currently inside your TemplateColumn.

    It adds logic in your binding to update your source trigger, while also removing your SelectedValuePath tag. I'm assuming based on your lack of a DisplayMemberPath tag that your ComboBox ItemsSource is a string collection. If this is the case the SelectedValuePath can actually wreck your bindings (I have no idea why though).

    <ComboBox Width="135" x:Name="Tagger" ItemsSource="{Binding tagType,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}" SelectedItem="{Binding Tags, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    

    Let me know if you still have binding update issues after, and will go from here.