0

I'm currently developing a C# WPF app an encountered a very weird error. I shifted from working with several windows to working with UserControls and experience one very odd behaviour. I got a UserControl called DataEntryControl, which has a ComboBox with SelectionChanged trigger in it, the XAML looking like this:

<StackPanel Grid.Row="1" Grid.Column="3" VerticalAlignment="Center">
    <ComboBox Name="cbEntry" Width="200" HorizontalAlignment="Left" SelectionChanged="cbEntry_SelectionChanged">
        <ComboBoxItem Content="Auto - Benziner" IsSelected="True" />
        <ComboBoxItem Content="Auto - Diesel" />
        <ComboBoxItem Content="Auto - Hybrid" />
        <ComboBoxItem Content="Auto - Elektro" />
        <ComboBoxItem Content="ÖPNV" />
        <ComboBoxItem Content="Fahrrad" />
        <ComboBoxItem Content="E-Bike" />
        <ComboBoxItem Content="Homeoffice" />
    </ComboBox>
</StackPanel>

The C#-Code for the DataEntryControl looks like this (excerpt):

public partial class DataEntryControl : UserControl
{
    Benutzer currentUser = MainWindow.currentUser;
    DBContext db = new DBContext();
       public DataEntryControl()
    {
        InitializeComponent();
    }

       private void cbEntry_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (currentUser == null) return;
        double saved = (db.Anreisetypen.Find(currentUser.AnreisetypenID).co2ProKm * currentUser.distanz -
            db.Anreisetypen.Find(MainWindow.fahrzeugID((cbEntry.SelectedItem as ComboBoxItem).Content.ToString())).co2ProKm * currentUser.distanz);
        tbSaved.Text = "Dadurch gespart:\n" + saved.ToString() + "g CO2";
        tbTotal.Text = "Einsparung gesamt:\n" + (currentUser.ersparnis + saved).ToString() + "g CO2";
    }
}

The Control is created when a user clicks a button in the MainWindow but here I encountered two very weird behaviours depending on how I activate the UserControl.

Version 1:

public void dataEntryClick(object sender, RoutedEventArgs e)
{
    ControlGrid.Children.Clear();
    ControlGrid.Children.Add(new DataEntryControl());
}

This causes the the cbEntry_SelectionChanged method to get called instantly when the control is created, throwing a NullPointerException for tbSaved and tbTotal (two TextBoxes). I wonder how can this happen? How can an event trigger get triggered if the belonging XAML has not been loaded yet??

After this I figured that I can just check if those objects are null, this removes the error however the Text isn't set for these two textboxes which is annoying. I can work around this by emulating a click after ignoring the first firing of the event tho, I'm just curious.

Another thing I tried is creating the DataEntryControl instance way before it get's loaded (when creating the MainWindow instance), making my C#-Code in the MainWindow look like this:

Version 2:

public partial class MainWindow: Window {
    DataEntryControl d { get; set; } = new DataEntryControl();
    //... constructor and stuff
    public void dataEntryClick(object sender, RoutedEventArgs e)
    {
        ControlGrid.Children.Clear();
        ControlGrid.Children.Add(d);
    }
}

This however causes the cbEntry_SelectionChanged to never get triggered. It doesn't get triggered when initializing (which is good) but also doesn't get triggered when changing the selection, which is weird since it listens to the SelectionChanged event.

As I prevsiouly wrote: I built a not-so-nice workaround for this problem by just aborting the selection function if either the ComboBox or the TextBoxes are null - this leaves me with 99% of the functionality I want to achieve but imo doesn't make a lot of sense, hence I still want to know why WPF behaves like this in this situation. I also did not encounter this problem when I used a DataEntryWindow instead of a DataEntryControl, which makes it even weirder. I hope somebody can help me with this, thanks!

Zer0
  • 1,580
  • 10
  • 28
  • 1
    The SelectionChanged event is fired during the execution of the InitializeComponent method - while the TextBlocks have not yet been created. You can avoid this problem by attaching the event handler in code behind instead of XAML, after InitializeComponent: `cbEntry.SelectionChanged += cbEntry_SelectionChanged;`. Even better would be to bind the ComboBox's SelectedIndex property to an integer property in a view model. You would not need an event handler method at all. – Clemens Aug 27 '20 at 08:40
  • Yeah I changed a bit of the code again and now create the Items in the CodeBehind as I figured out loading them from the Database would be better than hardcoding it. Thanks for the tip tho! – Zer0 Aug 27 '20 at 08:43

1 Answers1

1

The first problem you described is because you have in your XAML:

<ComboBoxItem Content="Auto - Benziner" IsSelected="True" />

This will cause the ComboBox to select the first item immediately after it's creation leading to the selection changing from 'Null' to the first item.

andy meissner
  • 1,202
  • 5
  • 15
  • Oh yeah, this actually makes a lot of sense, thanks for the answer! I didn't think that setting the default would cause the SelectionChange event to trigger but I get why this happens – Zer0 Aug 27 '20 at 08:40
  • To fix this you could just select your item in code after creation – andy meissner Aug 27 '20 at 08:42