Yusha
Yusha

Reputation: 1678

WPF Data Binding Errors Getting a System.Windows.Data Error: 40

I keep getting this Error: System.Windows.Data Error: 40 : BindingExpression path error:

System.Windows.Data Error: 40 : BindingExpression path error:

MainWindow.XAML

    <ComboBox Name="EventNameComboBox"
              DisplayMemberPath="EventName"
              HorizontalContentAlignment="Center"
              ItemsSource="{Binding Path=EventViewModels}"
              materialDesign:HintAssist.Hint="Select an Event"
              SelectionChanged="EventNameComboBox_SelectionChanged"
              Width="400">
        <ComboBox.SelectedItem>
            <Binding Path="ViewModels.EventViewModel.EventName" UpdateSourceTrigger="PropertyChanged">
                <Binding.ValidationRules>
                    <validationRules:EventNameValidationRule ValidatesOnTargetUpdated="True"/>
                </Binding.ValidationRules>
            </Binding>
        </ComboBox.SelectedItem>
        <ComboBox.ItemsPanel>
         <ItemsPanelTemplate>
             <VirtualizingStackPanel/>
         </ItemsPanelTemplate>
        </ComboBox.ItemsPanel>
    </ComboBox>

EventNameValidationRule.cs

public class EventNameValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        string eventName = value == null ? "" : value.ToString();

        return string.IsNullOrEmpty(eventName)
            ? new ValidationResult(false, "Please select a Event")
            : ValidationResult.ValidResult;
    }
}

Finally,

EventViewModel.cs

public class EventViewModel : INotifyPropertyChanged
{
    private int _eventId;
    private string _eventName;


    public int EventId
    {
        get { return _eventId; }
        set
        {
            _eventId = value;
            OnPropertyChanged("EventId");
        }

    }

    public string EventName
    {
        get { return _eventName; }
        set
        {
            _eventName = value;
            OnPropertyChanged("EventName");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

I'm not sure what is going on.

UPDATE

MainWindow.xaml.cs

private List<EventViewModel> _eventViewModels;

public List<EventViewModel> EventViewModels
{
    get { return _eventViewModels; }
    set { _eventViewModels = value; OnPropertyChanged("EventViewModels"); }
}

public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}


public MainWindow()
{
    InitializeComponent();

    EventViewModels = new List<EventViewModel>();
    int year = 2008;
    for (int i = 1; i <= 10; i++)
    {
        EventViewModel viewModel = new EventViewModel();
        viewModel.EventId = i;
        viewModel.EventName = $"{year} Test Event";

        ++year;

        EventViewModels.Add(viewModel);
    }

    DataContext = this;
} 

Upvotes: 1

Views: 1323

Answers (1)

Lance
Lance

Reputation: 2933

There are two things in your code that needs attention:

  1. The binding path ViewModels.EventViewModel.EventName is incorrect. The binding path should be based on the binding or the DataContext of the control, which in your case is the MainWindow.xaml.cs and that class doesn't have a property "ViewModels.EventViewModel.EventName".

  2. You cannot bind the EventName property to your SelectedItem because the SelectedItem should be bound to a type of EventViewModel because the item source is a list of EventViewModel

What you need to do:

  1. Create the correct binding. To do that you have to declare an EventViewModel property in the MainWindow class, let's call it SelectedEvent. Add the following to you MainWindow.xaml.cs
    public EventViewModel SelectedEvent
    {
        get { return _SelectedEvent; }
        set { _SelectedEvent = value; OnPropertyChanged("SelectedEvent"); }
    }
  1. You need to change the binding path of the SelectedItem in the ComboBox. Bind it to the new Property SelectedEvent. Now, every time you select an item in the combo box, the SelectedEvent property will change.
<ComboBox.SelectedItem>
    <Binding Path="SelectedEvent" UpdateSourceTrigger="PropertyChanged">
        <Binding.ValidationRules>
        <local:EventNameValidationRule ValidatesOnTargetUpdated="True"/>
        </Binding.ValidationRules>
    </Binding>
</ComboBox.SelectedItem>
  1. Since the binding was changed, the validation rule will now expect an EventViewModel in the value parameter. Therefore you need to adjust the ValidateMethod a little bit.
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
    if (value == null)
        return new ValidationResult(false, "Please select a Event");
    if (value is EventViewModel eventVm)
    {
        string eventName = eventVm.EventName == null ? "" : value.ToString();
        return string.IsNullOrEmpty(eventName)
            ? new ValidationResult(false, "Please select a Event")
            : ValidationResult.ValidResult;
    }
    throw new Exception("Invalid binding!"); 
}

Upvotes: 1

Related Questions