StackOverflowException в Silverlight MVVM после навигации по страницам - PullRequest
0 голосов
/ 09 августа 2011

Я пытаюсь использовать MVVM Light для создания простой страницы для добавления / редактирования / удаления контактов, а также для отправки пользователями факсов из других частей программного обеспечения. Главная страница имеет простой заголовок с некоторой корпоративной информацией и двумя ссылками, одна на страницу контактов, а другая на страницу истории факсов; Большая часть страницы MainPage представляет собой навигационную рамку, которая является целью ссылок, куда загружаются страницы. Я использую VS 2010 / .NET 4.0 / SL 4.0.

Страница «Контакты» имеет две сетки данных, одну для результатов поиска контактов («gridContacts») и другую для выбранных контактов (те контакты, которые получат факс - «gridSelected»). Для удаления контакта выбранной строки в сетке поиска есть кнопка «Удалить». Также есть кнопка «Отправить факс» для отправки факса этим контактам в сетке «Контакты».

Проблема в том, что если я когда-либо выберу строку в сетке поиска, перейду к истории факсов, а затем вернусь, я получу исключение StackOverflowException до того, как произойдет навигация. Если я сначала перейду на другую страницу, потом вернусь и выберу строку, я получу исключение на этом этапе. Я обнаружил, что кнопка «Отправить факс» также вызывает то же исключение при удалении кнопки «Удалить». На обеих кнопках я могу обойти проблему, просто кодируя все в коде позади; очевидно, это не идеально, поэтому я хочу научиться работать с MVVM.

Когда я перебираю код проблемы с кнопкой «Удалить», я вижу, что исключение возникает в DelegateCommand, с которым связана кнопка «Удалить». В методе CanExecute при первом использовании параметром объекта является правильно контакт (поскольку сетка связана со списком контактов). Он запускает CanExecuteChanged (this, new EventArgs ()) как часть логики кода - но затем возвращается BACK в функцию, но с переданным параметром null. Вот где становится очевидным бесконечный цикл: CanExecute запускается в цикле и чередуется между наличием объекта Contact и нулевого значения в качестве передаваемого параметра. Трассировка стека показывает внешний код между вызовами CanExecute, но это единственный присутствующий метод. В конечном итоге бесконечный цикл вызывает исключение StackOverflowException.

Вот код вопроса:

    public bool CanExecute(object parameter)    //parameter alternates between Contact object and null
    {
        bool temp = _funcCanExecute(parameter);  //_funcCanExecute is set to CanDelete in the DelegateCommand constructor.

        if (_bCanExecuteCache != temp)
        {
            _bCanExecuteCache = temp;
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, new EventArgs());   //this line somehow leads to another call into here --> infinite loop
            }
        }

        return _bCanExecuteCache;
    }

Сетка поиска ItemSource связана с виртуальной машиной через локатор следующим образом:

<sdk:DataGrid AutoGenerateColumns="False" x:Name="gridContacts" IsTabStop="False" Grid.Row="3" ItemsSource="{Binding Path=SearchContactsViewModel.Contacts, Mode=OneWay, Source={StaticResource Locator}}" SelectionMode="Single">

Вот кнопка «Удалить» Command и CommandParameter (общий контекст данных установлен как DataContext = "{Binding Source = {StaticResource Locator}, Path = SearchContactsViewModel}"):

                        <Button x:Name="btnDelete" Style="{StaticResource ButtonStyle}" Command="{Binding Path=SearchContactsViewModel.DeleteContactCommand, Source={StaticResource Locator}}" Grid.Row="1" Grid.Column="1" 
                    CommandParameter="{Binding Path=SelectedItem, ElementName=gridContacts}" HorizontalAlignment="Right">

Вот упрощенный код в SearchContactsViewModel:

    public DelegateCommand DeleteContactCommand
    {
        get;
        private set;
    }
    private bool CanDeleteContact(object param)
    {
        Contact c = param as Contact;
        if (c == null)
            return false;
        return true;
    }
    private void DeleteContact(object param)
    {

        int contactID = ((Contact)param).ID;
        _ServiceAgent.DeleteContact(contactID,
            (s, e) =>
            {
                if (!CheckErrorAndResult(e.Error, e.Result, e.errMsg))
                    return;
                SearchContacts();
                MessageBox.Show("Delete successful.");
            });
    }

Вот строка кода, которая связывает команду в ВМ:

DeleteContactCommand = new DelegateCommand(DeleteContact, CanDeleteContact);

НО, если я заменю CanDeleteContact на "(x) => true" для тестирования, я НЕ получу проблему.

Пожалуйста, дайте мне знать, если я смогу предоставить больше информации. Сначала я пытаюсь выяснить проблему с помощью кнопки «Удалить» и надеюсь применить то же решение к кнопке «Отправить факс».

Спасибо, J

*** ОБНОВЛЕНИЕ 9/6/2011: я заметил, что в навигации Silverlight каждый раз создается новый экземпляр страницы. Таким образом, это объясняет, почему эта проблема возникает только после навигации? И почему значения, обработанные в том, что я считал бесконечным циклом, чередуются между нулевыми и ненулевыми значениями? Как, может быть, старая страница теперь имеет значение NULL, но новая страница отображается и является правильной, но обе они связаны с одним и тем же статическим объектом VM. Просто стрельба в темноте здесь.

1 Ответ

0 голосов
/ 11 августа 2011

Я думаю, проблема в том, что вы смешиваете CanExecute и CanExecuteChanged. CanExecuteChanged определенно собирается заставить кого-то вызвать CanExecute (который вызывает CanExecuteChanged ... и так далее ...).

Таким образом, эти вызовы должны быть отдельными - CanExecute должен возвращать только некоторое значение (bCanExecuteCache). И CanExecuteChanged должен вызываться при определенных событиях / вызовах / случаях.

Вот несколько примеров на форуме Silverlight, которые могут вам помочь (показывает использование CanExecuteChanged): конкретное сообщение | целая нить

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...