Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have overrided the method OnSourceInitialized and I have one problem. After populating my combobox with source property from c# code I want automatically an item will appear selected in the combobox when a page is loaded (default value) but for some reason after onsourceinitialized method, the combobox selected item change to null.

First of all, very good explanation thanks.

I'll try to explain more and I post some code following. I have made some modifications but without success. It continues not working.

My goal is to show a default value selected in the combobox when window is loaded and it is shown.

Initially, when user selects a option in menu application I do the following:

WinMain.xaml.cs:

    namespace MyNamespace
      public partial class WinMain : Window
          private void mnuItemPreferences_Click(object sender, RoutedEventArgs e)
            MyNamespace.Windows.EditPreferences editPrefWnd = 
                   new MyNamesapece.Windows.EditPreferences();
            // Modal window that I want to open with default values in comboboxes
            editPrefWnd.ShowDialog();
        } // end WinMain class
      } // end namespace

EditPreferences.xaml.cs:

           namespace MyNamespace.Windows
              public partial class EditPreferences : Window
                 // My constructor
                 public EditPreferences()
                    // Handlers
                    Loaded += PreferencesWindow_Loaded;
                    Closing += PreferencesWindow_Closing;
        InitializeComponent();
        if (System.Environment.OSVersion.Version.Major < 6) 
            this.AllowsTransparency = true;
            _bolAeroGlassEnabled = false;
            _bolAeroGlassEnabled = true;
                    this.ShowInTaskbar = false;
                 } // end constructor
         private void PreferencesWindow_Loaded(object sender,
                                                     System.Windows.RoutedEventArgs e)
                   if (this.ResizeMode != System.Windows.ResizeMode.NoResize)
                     //this work around is necessary when glass is enabled and the 
                     //window style is None which removes the chrome because the 
                     //resize mode MUST be set to CanResize or else glass won't display
                     this.MinHeight = this.ActualHeight;
                     this.MaxHeight = this.ActualHeight;
                     this.MinWidth = this.ActualWidth;
                     this.MaxWidth = this.ActualWidth;
                  // Populate comboboxes
                  cbLimHorasExtra.ItemsSource = Accessor.GetLimHorasExtraSorted();
                  cbFracHorasExtra.ItemsSource = Accessor.GetFracHorasExtraSorted();
                  // Fill controls with default values (see below)
                  FillControls();
                  // Install other handlers
                  rdoBtnOTE.Checked += this.rdoBtnOTE_Checked;
                  rdoBtnOTM.Checked += this.rdoBtnOTM_Checked;
                  chkboxRestrict.Checked += this.chkboxRestrict_Checked;
                  expAdditionalDetails.Collapsed += 
                                    this.expAdditionalDetails_Collapsed;
                  expAdditionalDetails.Expanded += this.expAdditionalDetails_Expanded;
                  cbLimHorasExtra.SelectionChanged += 
                       this.cbLimHorasExtra_SelectionChanged;
                  cbFracHorasExtra.SelectionChanged += 
                       this.cbFracHorasExtra_SelectionChanged;
                 protected override void OnSourceInitialized(System.EventArgs e)
                     base.OnSourceInitialized(e);
                     if (_bolAeroGlassEnabled == false)
                        //no aero glass
                        this.borderCustomDialog.Background =  
                              System.Windows.SystemColors.ActiveCaptionBrush;
                        this.tbCaption.Foreground = 
                              System.Windows.SystemColors.ActiveCaptionTextBrush;
                        this.borderCustomDialog.CornerRadius = 
                              new CornerRadius(10, 10, 0, 0);
                        this.borderCustomDialog.Padding = 
                              new Thickness(4, 0, 4, 4);
                        this.borderCustomDialog.BorderThickness = 
                              new Thickness(0, 0, 1, 1);
                        this.borderCustomDialog.BorderBrush =
                              System.Windows.Media.Brushes.Black;
                         //aero glass
                         if (VistaAeroAPI.ExtendGlassFrame(this, 
                                 new Thickness(0, 25, 0, 0)) == false)
                            //aero didn't work make window without glass
                            this.borderCustomDialog.Background = 
                                 System.Windows.SystemColors.ActiveCaptionBrush;
                            this.tbCaption.Foreground = 
                                 System.Windows.SystemColors.ActiveCaptionTextBrush;
                            this.borderCustomDialog.Padding = 
                                 new Thickness(4, 0, 4, 4);
                            this.borderCustomDialog.BorderThickness = 
                                 new Thickness(0, 0, 1, 1);
                            this.borderCustomDialog.BorderBrush = 
                                 System.Windows.Media.Brushes.Black;
                            _bolAeroGlassEnabled = false;
                 private void FillControls()
                     tblPreferencias tbl_pref = null;
                     // Obtain data (a record with fields)
                     // Accessor is a class where I define the methods to 
                     // obtain data of different tables in my database
                     tbl_pref = Accessor.GetActualPreferencias();
                     // Only returns one register
                     if (tbl_pref != null)
                        rdoBtnOTE.IsChecked = (bool)tbl_pref.OTE;
                        rdoBtnOTM.IsChecked = (bool)tbl_pref.OTM;
                        chkboxRestrict.IsChecked = 
                                              (bool)tbl_pref.RestriccionHExtraTipoA;
                        // Here the value assigned is always in the range of the values
                        // which combo has been populated. 
                        // With one 0 ... 8
                        // I debbugged it and works.
                        // selected value (no null) and text gets the correct value I 
                        // want but after OnSourceInitialized method is executed I note
                        // that for some rease selected value property gets value null
                        cbLimHorasExtra.Text = tbl_pref.LimiteHorasExtra.ToString();
                        cbFracHorasExtra.Text = 
                                         tbl_pref.FraccionDeMinutosExtra.ToString();
              } // end EditPreferences class
           } // end namespace

EditPreferences.xaml (I put as example one of the comboboxes):

            <Window x:Class="MyNamespace.Windows.EditPreferences"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Title="EditPreferences" Height="Auto" Width="500"
             Background="{x:Null}"
             SnapsToDevicePixels="True" SizeToContent="Height" 
             WindowStartupLocation="CenterScreen"
             ResizeMode="NoResize" 
             WindowStyle="None"
             Margin="0,0,0,0"  
             <ComboBox x:Name="cbLimHorasExtra" 
                       DisplayMemberPath="LimHora"
                       SelectedValuePath="Id"  
                       SelectedItem="{Binding Path=Id}"
                       VerticalAlignment="Center" 
                       HorizontalContentAlignment="Right"
                       Width="50"/>
             </Window>

Accessor.cs:

             namespace GesHoras.Classes
               class Accessor
                // This method is used to populate the combobox with its values
                // tblLimHorasExtra is a table in my SQL Database
                // Its fields are:
                // Id : int no null (numbers 1 ... 9)
                // LimHora: int no null (numbers 0 ... 8)
                public static System.Collections.IEnumerable GetLimHorasExtraSorted()
                   DataClassesBBDDDataContext dc = new
                                                   DataClassesBBDDDataContext();
                   return (from l in dc.GetTable<tblLimHorasExtra>()
                           orderby l.LimHora
                           select new { Id=l.Id, LimHora=l.LimHora });
               // tblPreferencias is a table in my SQL Database
               // Its fields are:
               // Id : int no null
               // Descripcion : varchar(50) no null
               // OTE : bit no null
               // OTM : bit no null
               // LimiteHorasExtra : int no null
               // FraccionDeMinutosExtra : int no null
               // RestriccionHExtraTipoA : bit no null
               public static tblPreferencias GetActualPreferencias()
                    DataClassesBBDDDataContext dc = new
                                                    DataClassesBBDDDataContext();
                     return (from actP in dc.GetTable<tblPreferencias>()
                             where (actP.Id == 3)
                             select actP).SingleOrDefault<tblPreferencias>();
             } // end class
           } // end namespace

The problem I see is that when method fillControls is executed all is ok, selectedvalue and text property for the combobox is correct (I have debbugged it and is correct) but after executing OnSourceInitialized method, selectedvalue property for the combobox gets null value.

Also I note that, when window opens, the comboboxes appear with the default values selected that I want but quickly I see that for some reason their values selected turns to empty in the comboboxes. It's like some event (I think after executing OnSourceMethod because I have debugged and see how it change to null) makes the selected default values that appears ok in the comboboxes turn to empty.

I have tested that comboboxes are populated correctly because once the window is shown I click in the comboboxes and I can see they are populated ok.

EDIT 2

Also I have forced selected index for combobox in fillControls method by doing:

cbLimHorasExtra.SelectedIndex = 1;

but without success...

The combobox is populated with values: 0 to 8 both included.

If the "Id" property in the DataContext is not an item in the ItemsSource, SelectedItem will be set to null.

Timing

When InitializeComponent is called, it parses the XAML which sets the SelectedItem binding. If the DataContext is not set, then initially this will be null. Later when DataContext is set, the binding is re-evaluated. If Id is in the list at that point, the SelectedItem is set. Otherwise it is set to null.

Any binding that cannot be evaluated initially during InitializeComponent is scheduled using the dispatcher to be re-evaluated once all events have fired. Without details on how your DataContext is being set I can't give specifics, but my guess is that one of your binding is getting deferred so your {Binding Path=Id} binding is evaluated in a dispatcher callback.

A dispatcher callback is not an event - it is a prioritized work queue. If you have this kind of situations your choices are:

  • Change the bindings so they can be evaluated during initialization
  • Use a Dispather.BeginInvoke to schedule your own callback to execute after the Binding completes
  • Let the Binding take care of setting the SelectedItem rather than setting manually in code
  • Additional notes

    Your use of SelectedValueSource looks suspicious. Your binding to SelectedItem seems to indicate that each item in the ItemsSource is an "Id", but your definition of SelectedValueSource seems to indicate that each item in the ItemsSource contains an "Id". It is rare to find a data structure where the structure itself is called "Id" by another structure, yet it itself has an "Id" field. Thus I suspect some confusion here. Without seeing your actual data structures I can't say more.

    Your use of OnSourceInitialized also makes it appear you have a misunderstanding. The "Source" in the name of OnSourceInitialized refers to a "presentation source" such as a Win32 hWnd, not a source of data. The purpose of OnSourceInitialized is to interact at a low level with the Windows operating system, or to update your application based on where it is being presented. Your use seems completely unrelated to this. I would recommend you stay away from OnSourceInitialized. Generally the best time to initialize ComboBoxes and such is to just provide it in your view model and let data binding take care of it. As soon as the view model is available the data will be populated with no code required.

    Set the SelectedIndex property at the end of your override, by the way, i can't seem to find OnSourceInitialised, only Initialised. But it should still work if you set it at the end of your code.

        private void MyListBox_Initialized(object sender, EventArgs e)
            // Run some code
            if (MyListBox.Items.Count > 0)
                MyListBox.SelectedIndex = 0;
    

    I don't have a real answer to your question, but OnSourceInitialized seems to be too early in the initialization process.

    Again, I have not tried your exact scenario, but many problems like this one are solved by calling FillControls (i.e. setting the selected item) in the Loaded event instead of earlier.

    I have solved it!

    The problem was in binding the SelectedItem property in EditPreferences.xaml:

             <ComboBox x:Name="cbLimHorasExtra" 
                       DisplayMemberPath="LimHora"
                       SelectedValuePath="Id"  
                       SelectedItem="{Binding Path=Id}"
                       VerticalAlignment="Center" 
                       HorizontalContentAlignment="Right"
                       Width="50"/>
    

    The solution is to change to:

             <ComboBox x:Name="cbLimHorasExtra" 
                       DisplayMemberPath="LimHora"
                       SelectedValuePath="Id"  
                       SelectedItem="Id"
                       VerticalAlignment="Center" 
                       HorizontalContentAlignment="Right"
                       Width="50"/>