wpf combobox выбранный элемент в null после инициализации - PullRequest
1 голос
/ 01 марта 2010

Я переопределил метод OnSourceInitialized, и у меня есть одна проблема. После заполнения поля со списком со свойством исходного кода из кода C # я хочу, чтобы элемент автоматически отображался в поле со списком при загрузке страницы (значение по умолчанию), но по какой-то причине после метода onsourceinitialized выбранный элемент со списком изменится на ноль.

EDIT

Прежде всего, очень хорошее объяснение, спасибо.

Я попытаюсь объяснить больше и выложу следующий код. Я сделал некоторые изменения, но безуспешно. Он продолжает не работать.

Моя цель - показать значение по умолчанию, выбранное в выпадающем списке, когда окно загружено, и оно отображается.

Первоначально, когда пользователь выбирает опцию в приложении меню, я делаю следующее:

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 для выпадающего списка получает нулевое значение .

Также отмечу, что при открытии окна появляются комбинированные списки с выбранными значениями по умолчанию, которые я хочу, но быстро вижу, что по каким-то причинам их выбранные значения становятся пустыми в комбинированных списках. Это похоже на какое-то событие (я думаю, что после выполнения OnSourceMethod, потому что я отладил и вижу, как оно меняется на null), выбранные значения по умолчанию, которые отображаются нормально в выпадающих списках, становятся пустыми.

Я проверил, что комбинированные списки заполнены правильно, потому что, как только окно показывается, я щелкаю в комбинированных списках и вижу, что они заполнены нормально.

РЕДАКТИРОВАТЬ 2

Также я принудительно выбрал индекс для выпадающего списка в методе fillControls, выполнив:

cbLimHorasExtra.SelectedIndex = 1;

но безуспешно ...

Поле со списком заполняется значениями: от 0 до 8, оба включены.

Ответы [ 4 ]

3 голосов
/ 01 марта 2010

Причина

Похоже, это проблема:

SelectedItem="{Binding Path=Id}" 

Если свойство "Id" в DataContext не является элементом в ItemsSource, SelectedItem будет иметь значение null.

Сроки

Когда вызывается InitializeComponent, он анализирует XAML, который устанавливает привязку SelectedItem. Если DataContext не установлен, то изначально это будет нулевым. Позже, когда установлен DataContext, привязка переоценивается. Если в этот момент в списке присутствует Id, то устанавливается SelectedItem. В противном случае оно установлено в ноль.

Любая привязка, которая не может быть первоначально оценена в течение InitializeComponent, запланирована с использованием диспетчера для повторной оценки после запуска всех событий. Без подробностей о том, как устанавливается ваш DataContext, я не могу дать подробности, но я предполагаю, что одна из ваших привязок откладывается, поэтому ваша привязка {Binding Path=Id} оценивается в обратном вызове диспетчера.

Обратный вызов диспетчера не является событием - это приоритетная рабочая очередь. Если у вас есть такие ситуации, ваш выбор:

  1. Измените привязки, чтобы их можно было оценить во время инициализации
  2. Используйте Dispather.BeginInvoke, чтобы запланировать собственный обратный вызов для выполнения после завершения привязки
  3. Пусть Binding позаботится об установке SelectedItem, а не об установке вручную в коде

Дополнительные примечания

Использование SelectedValueSource выглядит подозрительно. Ваша привязка к SelectedItem, кажется, указывает, что каждый элемент в ItemsSource является «Id», но ваше определение SelectedValueSource, кажется, указывает, что каждый элемент в ItemsSource содержит «Id». Редко можно найти структуру данных, в которой сама структура называется «Id» другой структурой, но сама она имеет поле «Id». Таким образом, я подозреваю некоторую путаницу здесь. Не видя ваших реальных структур данных, я не могу сказать больше.

При использовании OnSourceInitialized также создается впечатление, что у вас неправильное понимание. «Источник» в названии OnSourceInitialized относится к «источнику представления», такому как Win32 hWnd, а не к источнику данных. Цель OnSourceInitialized - на низком уровне взаимодействовать с операционной системой Windows или обновить приложение в зависимости от того, где оно представлено. Ваше использование кажется совершенно не связанным с этим. Я бы порекомендовал вам держаться подальше от OnSourceInitialized. Как правило, наилучшее время для инициализации ComboBox - это просто предоставить его в вашей модели представления и позволить привязке данных позаботиться об этом. Как только модель представления станет доступна, данные будут заполнены без кода.

0 голосов
/ 02 марта 2010

Я решил это!

Проблема заключалась в привязке свойства SelectedItem в EditPreferences.xaml:

         <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"/>
0 голосов
/ 01 марта 2010

У меня нет реального ответа на ваш вопрос, но OnSourceInitialized, похоже, слишком рано в процессе инициализации.

Опять же, я не пробовал ваш точный сценарий, но многие проблемы, подобные этой, решаются путем вызова FillControls (т.е. установка выбранного элемента) в событии Loaded вместо более раннего.

0 голосов
/ 01 марта 2010

Установите свойство SelectedIndex в конце вашего переопределения, кстати, я не могу найти OnSourceInitialised, только Initialized. Но он все равно должен работать, если вы установите его в конце кода.

    private void MyListBox_Initialized(object sender, EventArgs e)
    {
        // Run some code
        if (MyListBox.Items.Count > 0)
        {
            MyListBox.SelectedIndex = 0;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...