DataGridComboBoxColumn column displaying incorrect selected item

Dave Cotton 41 Reputation points
2022-09-19T21:00:04.487+00:00

c# WPF Visual Studio 2019 4.7.2 Framework

I started with DataGridComboBoxColumn. I switched to Template columns, these have the same behavior. All the combobox ItemsSource are bound and that part works.
Put a break point in the getter for RuleType and FieldSize Type. I observe the getter returning distinct values. When the grid is displayed however, for those combobox's connected via SelectedValue, the first item in the ItemsSource is displayed. For those combobox's connected by SelectedItem nothing is displayed.

If I edit the combobox in the grid, I see all the correct choices, and I observe that the breakpoint in the setter gets the correct value.

What I want to see is that the correct selected item is displayed in the grid at load.

        <DataGrid ItemsSource="{Binding CurrentMessage.DefinitionRows, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="True" CanUserSortColumns="False" CanUserReorderColumns="False" RowDetailsVisibilityMode="Visible" Margin="10,88,10,45" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">  
            <DataGrid.Columns>  
                <DataGridTemplateColumn Header="Size (Bytes)/Type" Width="auto">  
                    <DataGridTemplateColumn.CellTemplate>  
                        <DataTemplate>  
                            <ComboBox ItemsSource="{Binding Path=DataContext.FieldSizes, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="FieldName" SelectedValuePath="SelectedFieldSize" SelectedValue="{Binding SelectedFieldSize, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>  
                        </DataTemplate>  
                    </DataGridTemplateColumn.CellTemplate>  
                    <DataGridTemplateColumn.CellEditingTemplate>  
                        <DataTemplate>  
                            <ComboBox ItemsSource="{Binding Path=DataContext.FieldSizes, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="FieldName" SelectedValuePath="SelectedFieldSize" SelectedValue="{Binding SelectedFieldSize, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>  
                        </DataTemplate>  
                    </DataGridTemplateColumn.CellEditingTemplate>  
                </DataGridTemplateColumn>  
                <DataGridTemplateColumn Header="Rules" Width="auto">  
                    <DataGridTemplateColumn.CellTemplate>  
                        <DataTemplate>  
                            <ComboBox ItemsSource="{Binding Path=DataContext.Rules, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="TypeName" SelectedItem="{Binding SelectedRule, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>  
                        </DataTemplate>  
                    </DataGridTemplateColumn.CellTemplate>  
                    <DataGridTemplateColumn.CellEditingTemplate>  
                        <DataTemplate>  
                            <ComboBox ItemsSource="{Binding Path=DataContext.Rules, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="TypeName" SelectedItem="{Binding SelectedRule, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>  
                        </DataTemplate>  
                    </DataGridTemplateColumn.CellEditingTemplate>  
                </DataGridTemplateColumn>  
            </DataGrid.Columns>  
            <DataGrid.RowDetailsTemplate>  
                <DataTemplate>  
                    <DataGrid ItemsSource="{Binding Path=DependentRows}" Visibility="{Binding DependentRows, Converter={StaticResource ShowRowConverter}}" HorizontalAlignment="Stretch" AutoGenerateColumns="false" CanUserAddRows="{Binding SelectedRule, Converter={StaticResource RuleConverter}}" CanUserDeleteRows="True" CanUserSortColumns="False" CanUserReorderColumns="False">  
                        <DataGrid.RowStyle>  
                            <Style TargetType="DataGridRow" BasedOn="{x:Null}">  
                                <Setter Property="Margin" Value="20,0,0,0"/>  
                            </Style>  
                        </DataGrid.RowStyle>  
                        <DataGrid.Columns>  
                            <DataGridTemplateColumn Header="Size (Bytes)/Type" Width="auto">  
                                <DataGridTemplateColumn.CellTemplate>  
                                    <DataTemplate>  
                                        <ComboBox ItemsSource="{Binding Path=DataContext.FieldSizes, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="FieldName" SelectedItem="{Binding SelectedFieldSize}"/>  
                                    </DataTemplate>  
                                </DataGridTemplateColumn.CellTemplate>  
                                <DataGridTemplateColumn.CellEditingTemplate>  
                                    <DataTemplate>  
                                        <ComboBox ItemsSource="{Binding Path=DataContext.FieldSizes, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" DisplayMemberPath="FieldName" SelectedItem="{Binding SelectedFieldSize}"/>  
                                    </DataTemplate>  
                                </DataGridTemplateColumn.CellEditingTemplate>  
                            </DataGridTemplateColumn>  
                            <DataGridTemplateColumn Header="Rules" Width="auto">  
                                <DataGridTemplateColumn.CellTemplate>  
                                    <DataTemplate>  
                                        <ComboBox ItemsSource="{Binding Path=DataContext.Rules, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" SelectedItem="{Binding SelectedRule}"/>  
                                    </DataTemplate>  
                                </DataGridTemplateColumn.CellTemplate>  
                                <DataGridTemplateColumn.CellEditingTemplate>  
                                    <DataTemplate>  
                                        <ComboBox ItemsSource="{Binding Path=DataContext.Rules, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" SelectedItem="{Binding SelectedRule}"/>  
                                    </DataTemplate>  
                                </DataGridTemplateColumn.CellEditingTemplate>  
                            </DataGridTemplateColumn>  
                        </DataGrid.Columns>  
                    </DataGrid>  
                </DataTemplate>  
            </DataGrid.RowDetailsTemplate>  
        </DataGrid>  
  

The CurrentMessage property is the Selected Item of a combo box external to the DataGrid. Which has a property called DefinitionRows which is defined as:
ObservableCollection<MessageDetailRow>
A portion of the MessageDetailRow row class:

    [Serializable]  
    public class MessageDetailRow : INotifyPropertyChanged  
    {  
        private FieldSizeType selectedFieldSize;  
        public FieldSizeType SelectedFieldSize  
        {  
            get { return selectedFieldSize; }  
            set  
            {  
                if (selectedFieldSize != value)  
                {  
                    selectedFieldSize = value;  
                    NotifyPropertyChanged();  
                }  
            }  
        }  
  
        private FieldRuleTypes selectedRule;  
        public FieldRuleTypes SelectedRule  
        {  
            get { return selectedRule; }  
            set  
            {  
                if (selectedRule != value)  
                {  
                    selectedRule = value;  
                    NotifyPropertyChanged();  
                }  
            }  
        }  
  
        private ObservableCollection<MessageDetailRow> dependentRows = new ObservableCollection<MessageDetailRow>();  
        public ObservableCollection<MessageDetailRow> DependentRows { get { return dependentRows; } }  
  
        public event PropertyChangedEventHandler PropertyChanged;  
        protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")  
        {  
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
        }  
    }  
  

FieldRuleType is defined as:
public class FieldRuleTypes
{
public FieldRuleTypes() { }
public FieldRuleTypes(string typeName, int index)
{
Index = index;
TypeName = typeName;
SpecialConditions = 0;
}
public FieldRuleTypes(string typeName, int index, int specialConditions)
{
Index = index;
TypeName = typeName;
SpecialConditions = specialConditions;
}
public FieldRuleTypes(string typeName, int index, string dependentOn)
{
Index = index;
TypeName = typeName;
DependentOn = dependentOn;
}

        public int Index { get; set; }  
        public int SpecialConditions { get; set; }  
        public string DependentOn { get; set; }  
        public string TypeName { get; set; }  
    }  
  

FieldSizeType is defined:
public class FieldSizeType
{
public FieldSizeType() { }
public FieldSizeType(string fieldName, int sizeBytes)
{
SizeBytes = sizeBytes;
FieldName = fieldName;
}

    public int SizeBytes { get; set; }  
    public string FieldName { get; set; }  
    public string DisplayFieldSize { get { return $"{FieldName} ({SizeBytes})"; } }  
}  
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,681 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
768 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Dave Cotton 41 Reputation points
    2022-09-20T16:42:20.927+00:00

    NOT AN ANSWER
    Asked for more details and comments have a size limit.

        public class MessageModel : INotifyPropertyChanged, INotifyDataErrorInfo  
        {  
            public MessageModel()  
            {  
                PropertyChanged += MessageModel_PropertyChanged;  
                DefinedMessages.CollectionChanged += DefinedMessages_CollectionChanged;  
            }  
      
            private string newMessageID;  
            public string NewMessageID  
            {  
                get { return newMessageID; }  
                set  
                {  
                    if (newMessageID != value)  
                    {  
                        newMessageID = value;  
                        if (int.TryParse(newMessageID, out int tryVal))  
                        {  
                            if (tryVal >= 10000 && tryVal <= 19999)  
                            {  
                                ClearErrors();  
                            }  
                            else  
                            {  
                                AddError("Message ID must be in the range 10000 to 19999");  
                            }  
                        }  
                        else  
                        {  
                            AddError("Message ID must be numeric");  
                        }  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private DefinedMessage currentMessage = new DefinedMessage();  
            public DefinedMessage CurrentMessage  
            {  
                get { return currentMessage; }  
                set  
                {  
                    if (currentMessage != value)  
                    {  
                        Trace.WriteLine($"Setting CurrentMessage {(value == null ? "Null" : "Not Null")}");  
                        currentMessage = value;  
                        if (currentMessage != null && !string.IsNullOrWhiteSpace(currentMessage.MessageName))  
                        {  
                            MessageName = currentMessage.MessageName;  
                        }  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
            private ToolTip messageIDTip;  
            public ToolTip MessageIDTip  
            {  
                get { return messageIDTip; }  
                set  
                {  
                    if(messageIDTip!=value)  
                    {  
                        messageIDTip = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            //For serialization purposes  
            private string messageName;  
            public string MessageName  
            {  
                get { return messageName; }  
                set  
                {  
                    if (messageName != value)  
                    {  
                        messageName = value;  
                        if (currentMessage != null)  
                        {  
                            currentMessage.MessageName = MessageName;  
                        }  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private ObservableCollection<DefinedMessage> definedMessages = new ObservableCollection<DefinedMessage>();  
            public ObservableCollection<DefinedMessage> DefinedMessages { get { return definedMessages; } }  
      
            private List<FieldSizeType> fieldSizes = new List<FieldSizeType>()  
                {new FieldSizeType("Byte", 1), new FieldSizeType("Short", 2), new FieldSizeType("Int", 4),  
                new FieldSizeType("Date", 4), new FieldSizeType("Binary", -1), new FieldSizeType("Ascii", -1),  
                new FieldSizeType("StringNullEnd", -1), new FieldSizeType("Remainder", -2), };  
            public List<FieldSizeType> FieldSizes { get { return fieldSizes; } }  
      
            private DefinedFieldRuleTypes rules = new DefinedFieldRuleTypes()  
                { new FieldRuleTypes("Required", 0),        //Standard  
                new FieldRuleTypes("Array Length", 1),      //Array length (next item)  
                new FieldRuleTypes("Dependent", 2),         //Only present if previous length > 0  
                new FieldRuleTypes("Repeat Count", 3),      //Count of how iterations of child rows, must be followed by child row definition  
                new FieldRuleTypes("Enumeration", 4) };     //Enumeration  
            public DefinedFieldRuleTypes Rules { get { return rules; } }  
      
            private void AddError(string error, [CallerMemberName] string propertyName = "" )  
            {  
                if (!errorsByPropertyName.ContainsKey(propertyName))  
                    errorsByPropertyName[propertyName] = new List<string>();  
      
                if (!errorsByPropertyName[propertyName].Contains(error))  
                {  
                    errorsByPropertyName[propertyName].Add(error);  
                    string tipMsg = string.Empty;  
                    errorsByPropertyName[propertyName].ForEach(_ => tipMsg += tipMsg == string.Empty ? _ : "\r\n" + _);  
                    switch (propertyName)  
                    {  
                        case "MessageIDTip":  
                            MessageIDTip = new ToolTip();  
                            MessageIDTip.Content = tipMsg;  
                            break;  
                    }  
                    NotifyErrorsChanged(propertyName);  
                }  
            }  
      
            private void ClearErrors([CallerMemberName] string propertyName = "")  
            {  
                if (errorsByPropertyName.ContainsKey(propertyName))  
                {  
                    errorsByPropertyName.Remove(propertyName);  
                    switch(propertyName)  
                    {  
                        case "MessageIDTip":  
                            MessageIDTip = null;  
                            break;  
                    }  
                    NotifyErrorsChanged(propertyName);  
                }  
            }  
      
            public void SaveISMPDefinitions()  
            {  
                string exeFullPath = System.Reflection.Assembly.GetExecutingAssembly().Location;  
                string ISMPDefinitionsLocation = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(exeFullPath), "ISMPDefinedMessages.xml");  
                try  
                {  
                    XmlSerializer serial = new XmlSerializer(typeof(ObservableCollection<DefinedMessage>));  
                    using (StreamWriter writer = new StreamWriter(ISMPDefinitionsLocation))  
                    {  
                        serial.Serialize(writer, DefinedMessages);  
                    }  
                }  
                catch (Exception ex)  
                {  
                    System.Windows.MessageBox.Show($"Unable to load file ISMPDefinedMessages.xml\r\nError Message:\r\n{ex.Message}", "Error Loading XML ISMP Message Definitions", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);  
                }  
            }  
      
            private Dictionary<string, List<string>> errorsByPropertyName { get; } = new Dictionary<string, List<string>>();  
            public bool HasErrors { get { return errorsByPropertyName.Any(); } }  
            public bool PropertyErrors(string propertyName = "")  
            {  
                if(string.IsNullOrWhiteSpace(propertyName))  
                    return errorsByPropertyName.Any();  
                return errorsByPropertyName.ContainsKey(propertyName);  
            }  
      
            public IEnumerable GetErrors(string propertyName)  
            {  
                return errorsByPropertyName.ContainsKey(propertyName) ? errorsByPropertyName[propertyName] : null;  
            }  
      
            private void MessageModel_PropertyChanged(object sender, PropertyChangedEventArgs e)  
            {  
                switch(e.PropertyName)  
                {  
                    case "NewMessageID":  
                        if (!string.IsNullOrWhiteSpace(NewMessageID) && CurrentMessage == null && !PropertyErrors("NewMessageID"))  
                        {  
                            CurrentMessage = new DefinedMessage();  
                        }  
                        break;  
                }  
            }  
      
            private void DefinedMessages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)  
            {  
                switch (e.Action)  
                {  
                    case NotifyCollectionChangedAction.Remove:  
                        break;  
                }  
            }  
      
            public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;  
            protected void NotifyErrorsChanged(string propertyName)  
            {  
                ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));  
            }  
      
            public event PropertyChangedEventHandler PropertyChanged;  
            protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")  
            {  
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
            }  
        }  
      
        [Serializable]  
        public class MessageDetailRow : INotifyPropertyChanged  
        {  
            public MessageDetailRow()  
            {  
                DependentRows.CollectionChanged += DependentRows_CollectionChanged;  
            }  
      
            private decimal id;  
            public decimal ID  
            {  
                get { return id; }  
                set  
                {  
                    if (id != value)  
                    {  
                        id = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private string field;  
            public string Field  
            {  
                get { return field; }  
                set  
                {  
                    if (field != value)  
                    {  
                        field = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private FieldSizeType selectedFieldSize;  
            public FieldSizeType SelectedFieldSize  
            {  
                get { return selectedFieldSize; }  
                set  
                {  
                    if (selectedFieldSize != value)  
                    {  
                        selectedFieldSize = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private FieldRuleTypes selectedRule;  
            public FieldRuleTypes SelectedRule  
            {  
                get { return selectedRule; }  
                set  
                {  
                    if (selectedRule != value)  
                    {  
                        selectedRule = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private string description;  
            public string Description  
            {  
                get { return description; }  
                set  
                {  
                    if (description != value)  
                    {  
                        description = value;  
                        NotifyPropertyChanged();  
                    }  
                }  
            }  
      
            private ObservableCollection<MessageDetailRow> dependentRows = new ObservableCollection<MessageDetailRow>();  
            public ObservableCollection<MessageDetailRow> DependentRows { get { return dependentRows; } }  
      
            private void DependentRows_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)  
            {  
                switch(e.Action)  
                {  
                    case NotifyCollectionChangedAction.Add:  
                        break;  
                    case NotifyCollectionChangedAction.Remove:  
                        break;  
                }  
            }  
      
            public event PropertyChangedEventHandler PropertyChanged;  
            protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")  
            {  
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
            }  
        }  
      
        public class ChildRowVisibilityConverter : IValueConverter  
        {  
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
            {  
                ObservableCollection<MessageDetailRow> rows = value as ObservableCollection<MessageDetailRow>;  
                if (rows == null)  
                    return System.Windows.Visibility.Collapsed;  
                else if (rows.Any())  
                    return System.Windows.Visibility.Visible;  
                else  
                    return System.Windows.Visibility.Collapsed;  
            }  
      
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
            {  
                throw new NotImplementedException();  
            }  
        }  
      
        public class FieldRuleConverter : IValueConverter  
        {  
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)  
            {  
                FieldRuleTypes rule = value as FieldRuleTypes;  
                return DataGridRowDetailsVisibilityMode.VisibleWhenSelected;  
            }  
      
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
            {  
                throw new NotImplementedException();  
            }  
        }  
      
      
    
    0 comments No comments