onsourceinitialized后,wpf combobox selecteditem为null

时间:2010-03-01 11:53:11

标签: c# wpf combobox

我已经覆盖了OnSourceInitialized方法,我遇到了一个问题。在使用来自c#代码的source属性填充我的组合框之后,我想自动地在加载页面时在组合框中显示一个项目被选中(默认值)但是由于某种原因在onsourceinitialized方法之后,组合框选择的项目变为null。

修改

首先,非常好的解释谢谢。

我会尝试解释更多,然后发布一些代码。我做了一些修改但没有成功。它继续不起作用。

我的目标是在加载窗口并显示窗口时显示在组合框中选择的默认值。

最初,当用户在菜单应用程序中选择一个选项时,我会执行以下操作:

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;
        }

        else 
                    {
            _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;
                      }
                      else
                      {
                         //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(我举例说明了一个组合框):

            <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

我看到的问题是,当执行方法fillControls时,一切正常,组合框的selectedvalue和text属性是正确的(我已经验证了它并且是正确的)但是在执行OnSourceInitialized方法之后,组合框的selectedvalue属性获得null值

另外我注意到,当窗口打开时,组合框会显示我想要的默认值,但我很快就会看到由于某些原因,它们的值在组合框中变为空。这就像某个事件(我想在执行OnSourceMethod之后,因为我已经调试并看到它如何更改为null)使组合框中显示为ok的选定默认值变为空。

我已经测试了组合框是否正确填充,因为一旦显示窗口,我点击组合框就可以看到它们已经填充好了。

编辑2

此外,我通过执行以下操作强制在fillControls方法中为combobox选择了索引:

cbLimHorasExtra.SelectedIndex = 1;

但没有成功......

组合框中填充了值:0到8都包括在内。

4 个答案:

答案 0 :(得分:3)

<强>原因

这似乎是问题所在:

SelectedItem="{Binding Path=Id}" 

如果DataContext中的“Id”属性不是ItemsSource中的项目,则SelectedItem将设置为null。

<强>时序

当调用InitializeComponent时,它会解析设置SelectedItem绑定的XAML。如果未设置DataContext,则最初将为null。稍后设置DataContext时,将重新评估绑定。如果此时列表中有Id,则会设置SelectedItem。否则将其设置为null。

InitializeComponent期间最初无法评估的任何绑定都是使用调度程序在所有事件触发后重新计算的。如果没有关于如何设置DataContext的详细信息,我不能给出具体细节,但我的猜测是你的一个绑定被延迟,所以你的{Binding Path=Id}绑定会在调度程序回调中进行评估。

调度程序回调不是事件 - 它是优先级的工作队列。如果您遇到这种情况,您的选择是:

  1. 更改绑定,以便在初始化期间评估它们
  2. 使用Dispather.BeginInvoke安排自己的回调在Binding完成后执行
  3. 让Binding负责设置SelectedItem而不是手动设置代码
  4. 附加说明

    您对SelectedValueSource的使用看起来很可疑。您对SelectedItem的绑定似乎表明ItemsSource 中的每个项目都是“ID”,但您对SelectedValueSource的定义似乎表明每个项目都在ItemsSource 包含“Id”。很难找到一种数据结构,其中结构本身被另一个结构称为“Id”,但它本身具有“Id”字段。因此我怀疑这里有些混乱。如果没有看到您的实际数据结构,我就不能多说了。

    您对OnSourceInitialized的使用也会让您觉得有误解。 OnSourceInitialized名称中的“Source”是指“演示源”,例如Win32 hWnd,而不是数据源。 OnSourceInitialized的目的是与Windows操作系统进行较低级别的交互,或者根据应用程序的呈现位置更新应用程序。您的使用似乎与此完全无关。我建议你远离OnSourceInitialized。通常,初始化ComboBoxes的最佳时间是在视图模型中提供它并让数据绑定处理它。只要视图模型可用,就会填充数据而不需要代码。

答案 1 :(得分:0)

在覆盖结束时设置SelectedIndex属性,顺便说一句,我似乎无法找到OnSourceInitialised,只有Initialised。但是如果你在代码的末尾设置它,它仍然可以工作。

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

答案 2 :(得分:0)

我对你的问题没有真正的答案,但OnSourceInitialized似乎在初始化过程中还为时过早。

同样,我没有尝试过您的确切方案,但是通过在Loaded事件中调用FillControls(即设置所选项目)而不是之前的事件来解决许多像这样的问题。

答案 3 :(得分:0)

我已经解决了!

问题在于绑定EditPreferences.xaml中的SelectedItem属性:

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

解决方案是改为:

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