2013-06-06 3 views
1

Есть ли способ создать только «экземпляр» ICommand для пользовательского контроля, БЕЗ статических классов «позади»?Возможна реализация нестатических команд WPF WPF?

Я пытаюсь обновить ранее созданный пользовательский элемент управления.

Одна из целей заключается в обеспечении многовекторности.

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

Я выяснил, как избавиться от большинства, но с проблемами с ICommand.

Элементы GUI в пользовательском контроле имеют команду, которая должна быть действительна только в экземпляре User Control - вместо этого теперь команда вмешивается во все экземпляры (например, CanExecute делает элементы GUI активными в экземплярах UserControl, где «локальные «условия не выполняются).

+1

Я думаю, что проблемы будут похожи на свойства зависимостей: http://stackoverflow.com/questions/3660696/non-static-dependency-properties –

+1

Я думаю, что вы неправильно понимаете команды. Нет ничего, что создавало бы команды, связанные со статическими классами. – Euphoric

+0

@Euphoric: документация MSDN, связанная с командами, действительно отстойная, потому что она заполнена такими образцами (когда команды находятся в статическом классе). В то же время подход MVVM, который является основным для WPF, предполагает, что эта команда является частью модели представления. Я понимаю, почему этот вопрос появился. – Dennis

ответ

2

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

В вашей ViewModel:

public ICommand MyCommand {get;set;} // construct your command and set it here 

вашим контролем:

<Button Command="{Binding MyCommand}"/> 

Если вы не используете шаблон MVVM, то вы должны создать то же поле в своем DataContext (возможно, в вашем контрольном коде)

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

В общем:

Для того чтобы знать ваши варианты при написании в WPF/C# Я рекомендую прочитать о MVVM модели, свойства зависимостей, DataContext и этикетирование - вы знаете, некоторые из этого уже.

+0

Я бы сказал «выставить его как свойство» вместо «сохранить его в поле», потому что привязка данных не работает с полями. Это может ввести в заблуждение. – Dennis

+0

Благодарим вас за понимание, английский язык не мой родной язык, и ваш комментарий очень ценится - я его изменил. –

1

Я думаю, вас может смутить тот факт, что методы CanExecute и Execute не имеют параметра, связывающего их с объектом, на котором они должны действовать.

Но помните, что интерфейс ICommand должен быть реализован классом, а объекты этого класса могут и должны иметь поля, обычно инициализированные в конструкторе.

Например, если вы следуете шаблону MVVM (как уже упоминалось в Ron.B.I.), команда обычно имеет ссылку на viewmodel. Или вы можете использовать что-то вроде RelayCommand и захватить viewmodel в делетете делегата или лямбда-закрытии.

0

Благодарим за ответы и разъяснения!

Ты дал мне решающий удар, поэтому я понял это. Я добавил свои примеры на purpouse.

Следуя вашим советам (Ron B I & Dennis) Я сначала хотел узнать больше о ViewModel.

Под http://msdn.microsoft.com/en-ca/magazine/dd419663.aspx есть примеры с нестационарным классом. Таким образом, решение было просто добавить новый класс в моем пользовательском элементе управления (точно, как показано на упомянутом сайте - Рисунок 3 - некоторые имя изменено - Copyright принадлежит Джошу Смиту joshsmithonwpf.wordpress.com):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Input; 

namespace WpfCommandControl 
{ 
class CommandImplementation : ICommand 
{ 
    #region Fields 

    readonly Action<object> _execute; 
    readonly Predicate<object> _canExecute; 

    #endregion // Fields 

    #region Constructors 

    public CommandImplementation(Action<object> execute) 
     : this(execute, null) 
    { 
    } 

    public CommandImplementation(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 

     _execute = execute; 
     _canExecute = canExecute; 
    } 
    #endregion // Constructors 

    #region ICommand Members 

    public bool CanExecute(object parameter) 
    { 
     return _canExecute == null ? true : _canExecute(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

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

    #endregion // ICommand Members 

} 
} 

Тогда в Контроль пользователя "окно"

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.ComponentModel; 

namespace WpfCommandControl 
{ 
    public partial class CommandControl : UserControl, INotifyPropertyChanged 
    { 
    #region [ Private Members ] 
    private bool _canActivated = false; 
    private int _counter = 0; 
    CommandImplementation _activateCommand; 

    #endregion 

    #region [ Properties ] 
    public int CommandCounter 
    { 
     get 
     { 
      return _counter; 
     } 

     set 
     { 
      _counter = value; 
      OnNotifyPropertyChanged("CommandCounter"); 
     } 

    } 

    public bool CanActivated 
    { 
     get 
     { 
      return _canActivated; 
     } 

     set 
     { 
      _canActivated = value; 
      OnNotifyPropertyChanged("CanActivated"); 
     }   
    } 
    #endregion 

    #region [ Property_Changed_Utilities ] 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnNotifyPropertyChanged(String info) 
    { 
     // Note: Do not forget to add interface "INotifyPropertyChanged" to your class. 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
    #endregion 

    # region [ Commands ] 
    public ICommand ActivateCommand 
    { 
     get 
     { 
      return _activateCommand; 
     } 
    } 
    #endregion 

    #region [ Constructor ] 
    public CommandControl() 
    { 
     InitializeComponent(); 
     _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated); 
    } 
    #endregion 

    #region [ Methods ] 
    void Activate() 
    { 
     CommandCounter++; 
    } 
    #endregion 


} 
} 

Наиболее важная часть:

Команда реализована в собственности:

public ICommand ActivateCommand 
    { 
     get 
     { 
      return _activateCommand; 
     } 
    } 

Так убеждается он возвращает фактический экземпляр, связанный команды, который был экземпляр с лямбда-выражения в конструкторе управления пользователя:

public CommandControl() 
    { 
     InitializeComponent(); 
     _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated); 
    } 

лямбда - выражение делает подключение к логической begind:

param => this.Activate() 

Для Activate() функции которым будет выполняться как команда обжигают

void Activate() 
    { 
     CommandCounter++; 
    } 

И

param => this.CanActivated 

Для передачи локальной логики для свойства ICommand CanExecute, что дает вам возможность контролировать, когда команда может быть выполнена.

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

public bool CanActivated 
    { 
     get 
     { 
      return _canActivated; 
     } 

     set 
     { 
      _canActivated = value; 
      OnNotifyPropertyChanged("CanActivated"); 
     }   
    } 

Опять же, как показано с Джошем Смитом joshsmithonwpf.wordpress.com - Я просто изменил его, чтобы создать экземпляр в конструкторе, а не проверять, является ли частный член нулевым и доставляет новый экземпляр, если это необходимо в части GET свойства Command.

Остальная часть кода - это просто реализация необходимых свойств и OnNotifyPropertyChanged, как показано на MSDN.

XAML прост - только для доказательства концепции.

<UserControl x:Class="WpfCommandControl.CommandControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:WpfCommandControl" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     d:DesignHeight="300" 
     d:DesignWidth="300" 
     mc:Ignorable="d"> 
<Grid> 
    <StackPanel> 
     <CheckBox Content="Activate" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" /> 
     <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                   AncestorType=UserControl}, 
            Path=ActivateCommand}" 
       Content="Click me" 
       IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                    AncestorType=UserControl}, 
            Path=CanActivated}" /> 
     <Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CommandCounter}" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" /> 
    </StackPanel> 
</Grid> 

Как вы можете видеть, что есть только CheckBox - Binding обеспечит включение/отключение кнопки. Нажмите кнопку «Кнопка», чтобы вызвать команду, которая просто увеличит счетчик - на ярлыке снова появится привязка.

Собираем все вместе:

Просто один простой XAML формы с четырьмя пользовательских элементов управления:

<Window x:Class="CommandsTest.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:CommandsTest" 
    xmlns:uctrl="clr-namespace:WpfCommandControl;assembly=WpfCommandControl" 
    Title="MainWindow" 
    Width="525" 
    Height="350"> 

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition /> 
     <RowDefinition /> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition /> 
     <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 

    <uctrl:CommandControl Grid.Row="0" Grid.Column="0" /> 

    <uctrl:CommandControl Grid.Row="0" Grid.Column="1" /> 

    <uctrl:CommandControl Grid.Row="1" Grid.Column="0" /> 

    <uctrl:CommandControl Grid.Row="1" Grid.Column="1" /> 

</Grid> 

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

Все решается в способе WPF - с использованием команд и привязок без прямого взаимодействия с элементами GUI, поэтому графический интерфейс можно обменивать без необходимости обновления кода.

Еще раз благодарим вас за то, что вы показали мне, что есть еще один (безопасный) способ реализовать пользовательские команды в WPF.