Я пытаюсь использовать 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. Просто стрельба в темноте здесь.