2010-11-04 2 views
6

Я действительно почесываю голову этим. У меня есть mainwindow, который открывает диалог. После закрытия диалога метод CanExecute в командах, связанных в диалоговом окне, все еще выполняется. Это вызывает некоторые серьезные проблемы в моем приложении.Когда ui отсоединяется от команд?

Пример:

MainWindow имеет кнопку с обработчиком щелчка. Это обработчик события нажатия:

private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     DialogWindow window = new DialogWindow(); 
     window.ShowDialog(); 
    } 

В диалоговом окне я связать элементы управления для статического ресурса в диалоговом окне, и каждый элемент в списке имеет команду:

<Window.Resources> 

    <Collections:ArrayList x:Key="itemsSource"> 
     <local:ItemViewModel Description="A"></local:ItemViewModel> 
     <local:ItemViewModel Description="B"></local:ItemViewModel> 
     <local:ItemViewModel Description="C"></local:ItemViewModel> 
    </Collections:ArrayList> 

    <DataTemplate DataType="{x:Type local:ItemViewModel}"> 
      <Button Grid.Column="1" Command="{Binding Path=CommandClickMe}" Content="{Binding Path=Description}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"> 
      </Button> 
    </DataTemplate> 

</Window.Resources> 

<Grid> 
    <ToolBar ItemsSource="{StaticResource itemsSource}"></ToolBar> 
</Grid> 

Это модели представления:

public class ItemViewModel 
{ 
    private RelayWpfCommand<object> _commandClickMe; 

    public RelayWpfCommand<object> CommandClickMe 
    { 
     get 
     { 
      if (_commandClickMe == null) 
       _commandClickMe = new RelayWpfCommand<object>(obj => System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe()); 

      return _commandClickMe; 
     } 
    } 

    private bool CanClickMe() 
    { 
     return true; 
    } 

    public string Description { get; set; } 

И это реализация DelegateCommand:

public class RelayWpfCommand<T> : ICommand 
{ 
    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    private readonly Predicate<T> _canExecute; 
    private readonly Action<T> _execute; 

    public RelayWpfCommand(Action<T> execute, Predicate<T> canExecute) 
    { 
     _execute = execute; 
     _canExecute = canExecute; 
    } 

    /// <summary> 
    /// Forces a notification that the CanExecute state has changed 
    /// </summary> 
    public void RaiseCanExecuteChanged() 
    { 
     CommandManager.InvalidateRequerySuggested(); 
    } 

    public bool CanExecute(T parameter) 
    { 
     return _canExecute(parameter); 
    } 

    public void Execute(T parameter) 
    { 
     _execute(parameter); 
    } 

    bool ICommand.CanExecute(object parameter) 
    { 
     if (!IsParameterValidType(parameter)) 
      return false; 

     return CanExecute((T)parameter); 
    } 

    void ICommand.Execute(object parameter) 
    { 
     if (!IsParameterValidType(parameter)) 
      throw new ArgumentException(string.Format("Parameter must be of type {0}", typeof(T))); 

     Execute((T)parameter); 
    } 

    private static bool IsParameterValidType(object parameter) 
    { 
     if (parameter != null && !typeof(T).IsAssignableFrom(parameter.GetType())) 
      return false; 

     return true; 
    } 
} 

Теперь, если я закрою диалоговое окно и задаю точку останова в методе CanExecute (я использую Prism DelegateCommand со слабой подпиской на события) на модели viewmodel, я заметил, что он вызывает, хотя диалог был закрыт. Почему на самом деле связь между кнопкой в ​​диалоговом окне и командой на ViewModel все еще жива?

И я проверяю, выполняется ли его, закрывая окно и позднее устанавливая точку останова в методе CanClickMe в модели viewmodel. Он будет выполняться некоторое время, а затем внезапно остановится (вероятно, из-за GC). Это не-детерминированное поведение вызывает проблемы, поскольку в реальном приложении модель просмотра может быть уже удалена.

+0

В какой момент вы это видите? Экземпляр окна будет по-прежнему находиться в области действия после его закрытия, пока вы не покинете событие Click. Это позволит вызывающему пользователю получить доступ к свойствам в окне (подумайте, например, о окне «Параметры»). Кроме того, что в CanExecute вызывает проблему? Может ли проблема на самом деле заключаться в том, что вы создаете побочные эффекты в CanExecute? –

+0

Мои комментарии – Marius

ответ

0

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

Существует в WPF 3.5 (включая SP1) а known memory leak issue, в основном вы можете столкнуться, если вы привязки к чему-то, что не является DependencyProperty или не реализует INotifyPropertyChanged. И это именно то, о чем ваш код.

Только внесите INotifyPropertyChanged на номер ItemViewModel и посмотрите, как оно идет. Надеюсь это поможет.

+1

Я реализовал INotifyPropertyChanged на ItemViewModel, но это не помогло. – Marius

0

Вы можете очистить коллекцию CommandBindings своего окна, когда она закрывается.

0

вместо того, чтобы ваша команда как свойство, вы можете попробовать следующее:

public ICommand CommandClickMe 
{ 
    get 
    { 
     return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe()); 
    } 
} 
1

Вы можете использовать WeakEvent шаблон для смягчения этой проблемы. Пожалуйста, обратитесь к следующему вопросу Stackoverflow: Is Josh Smith's implementation of the RelayCommand flawed?

+0

Нет ничего ошибочного в реализации, CommandManager правильно внедряет шаблон слабых событий (который также является принятым ответом в вопросе, который вы ссылаетесь на состояния) – Marius

Смежные вопросы