Проблема двустороннего связывания - PropertyChanged & CanExecuteChanged - PullRequest
2 голосов
/ 12 марта 2012

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

Я привязываю текстовое поле с помощью viewModel:

<TextBox Name="nameTbx" Text="{Binding Path=NewNode.Name, Mode=TwoWay}" />

Моя кнопка:

<Button Content="Add" Command="{Binding Path=AddNewNodeProperty}"/>

В коде XAML я установил DataContext для моей ViewModel.ViewModel выглядит следующим образом:

/* code*/

private Node _newNode = new Node();
public Node NewNode
{
    get
    {
        return _newNode;
    }
    set
    {
        _newNode = value;
        OnPropertyChanged("NewNode");
    }
}

private AddNode _addNewNodeProperty;
    public AddNode AddNewNodeProperty
    {
        get
        {
            return _addNewNodeProperty;
        }
    }

В конструкторе я инициализирую _addNewNodeProperty

this._addNewNodeProperty = new AddNode(this);

Вот мой класс AddNode:

public class AddNode : ICommand
{
    private ServiceMapViewModel viewModel;

    public AddNode(ServiceMapViewModel viewModel)
    {
        this.viewModel = viewModel;
        this.viewModel.PropertyChanged += (s, e) =>
        {
            if (CanExecuteChanged != null)
            {
                CanExecuteChanged(this, new EventArgs());
            }
        };
    }

    public bool CanExecute(object parameter)
    {
        bool b = !string.IsNullOrWhiteSpace(this.viewModel.NewNode.Name);

        return b;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        this.viewModel.AddNewNode();
    }
}

И, наконец, мой класс Node:

public class Node
{
    public string Name { get; set; }

    public bool? IsChecked { get; set; }

    public Group Group { get; set; }

    public Category Category { get; set; }

    public string Metadata { get; set; }

    public List<string> Children = new List<string>();

    public List<string> Parents = new List<string>();
}

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

Tnx дополнительно!

РЕДАКТИРОВАТЬ Позвольте мне добавить кое-что:

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

Выбранный элемент:

<DataGrid Name="nodeDataGrid" ItemsSource="{Binding Path=MyServiceMap.Nodes}"
        Background="Silver" Margin="0,34,10,10" IsReadOnly="True" SelectedItem="{Binding Path=SelectedNode}"  >

И ВМ:

private Node _selectedNode = new Node();
    public Node SelectedNode
    {
        get
        {
            return _selectedNode;
        }
        set
        {
            _selectedNode = value;
            OnPropertyChanged("SelectedNode");
        }
    }

Ответы [ 3 ]

1 голос
/ 12 марта 2012

Ваш TextBox привязан к свойству Name вашего Node объекта.Свойство Name не вызывает событие PropertyChanged при его изменении.Только все свойство NewNode будет вызывать событие при его изменении.

Изменить, чтобы предложить предложение

Один из подходов заключается в создании класса NodeViewModelкоторый, как предложил mtaboy , будет реализовывать интерфейс INotifyPropertyChanged.Вы должны создать любые свойства в ViewModel, которые вы хотели бы предоставить своему пользователю.Вы можете думать о ViewModel как о том, что он находится между вашей моделью (то есть Node классом) и вашим представлением (пользовательским интерфейсом, показанным пользователю).

Одно изменение, которое я бы порекомендовал, заключалось бы в инкапсуляции ICommandлогика в своем собственном классе.Я часто использую класс RelayCommand, который можно найти в статье Джоша Смита MVVM на MSDN.Если вы используете этот класс, вы можете определить либо свойство, либо приватный метод в вашем классе ViewModel, который будет возвращать, может ли пользователь добавить или нет.Например,

private bool CanAddNewNode()
{
  return !String.IsNullOrWhitespace(Name);
}

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

var saveCommand = new RelayCommand(param => SaveMethod(), param => CanAddNewNode());

Вы бы выставили ICommand свойство из вашей ViewModel, которое будет возвращать RelayCommand объект.

Надеюсь, это поможет вам начать.

1 голос
/ 12 марта 2012

Ваш класс узла должен иметь значение

INotifyPropertyChanged

public class Node:INotifyPropertyChanged
{

       #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
     string _name ;
      public string Name
      {
          get { return _name; }


          set
          {
              _name = value;
               OnPropertyChanged("Name");

          }
      }



.....

для получения дополнительной информации см. Шаблон MVVM

1 голос
/ 12 марта 2012

Проблема в том, что когда я изменяю текстовое поле, NewNode получает значение для меня, но оно должно установить.

Полагаю, вам следует также поддерживать INotifyPropertyChanged интерфейс для класса Node.

Также, если вы хотите, чтобы proeprty синхронизировался при каждом обновлении персонажа - попробуйте установить UpdateSourceTrigger, иначе свойство будет обновлено при событии LostFocus.

<TextBox Name="nameTbx" 
         Text="{Binding Path=NewNode.Name, 
                        Mode=TwoWay,
                        UpdateSourceTrigger=PropertyChanged}" />  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...