Когда пользовательский интерфейс отключается от команд? - PullRequest
6 голосов
/ 04 ноября 2010

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

Пример:

В 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 (I ')При использовании метода Prism DelegateCommand со слабой подпиской на события) в модели представления, я замечаю, что это срабатывает, хотя диалог был закрыт.С какой стати связь между кнопкой в ​​диалоговом окне и командой ViewModel все еще жива?

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

Ответы [ 4 ]

1 голос
/ 05 декабря 2011

Вы можете использовать шаблон WeakEvent, чтобы решить эту проблему.Пожалуйста, обратитесь к следующему вопросу Stackoverflow: Является ли реализация Джоша Смита RelayCommand ошибочной?

0 голосов
/ 22 июня 2011

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

0 голосов
/ 25 ноября 2011

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

public ICommand CommandClickMe
{
   get
   {
       return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
   }
}
0 голосов
/ 10 января 2011

Я видел этот улов много раз в разных проектах, я не уверен, что эта жуткая ошибка также скрывается в вашем приложении, но стоит проверить.проблема в WPF 3.5 (включая SP1), в основном вы можете столкнуться с ней, если привязываетесь к чему-то, что не является DependencyProperty или не реализует INotifyPropertyChangedИ это именно то, о чем ваш код.

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

...