双向绑定问题 - PropertyChanged& CanExecuteChanged

时间:2012-03-12 12:53:22

标签: c# wpf xaml binding mvvm

我想实现在更改文本框值时,我的“添加”按钮变为可用。

我用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 in advanced!

修改 让我补充一点:

我在屏幕上也有一个数据网格,当更改了选定项目时,我的添加按钮变为可用。

选择项目:

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

和VM:

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

3 个答案:

答案 0 :(得分:1)

  

问题在于,当我更改文本框文本时,NewNode正在获取   我的价值,但它应该设定。

我相信您也应该支持INotifyPropertyChanged类的Node界面。

此外,如果你想在每个角色更新时保持同步 - 尝试设置UpdateSourceTrigger,否则属性将在LostFocus事件上更新。

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

答案 1 :(得分:1)

您的TextBox绑定到Name对象的Node属性。 Name属性在更改时不会引发PropertyChanged事件。只有整个NewNode属性会在事件发生变化时引发事件。

修改以提供建议

一种方法是创建一个NodeViewModel类,正如 mtaboy 建议的那样,它将实现INotifyPropertyChanged接口。您将在ViewModel上创建要向用户公开的任何属性。您可以将ViewModel视为模型(即Node类)和View(显示给用户的UI)之间的对象。

我建议的一个改变是将ICommand逻辑封装到自己的类中。我经常使用的是RelayCommand类,可以在MSDN上的Josh Smith的MVVM article中找到。如果使用该类,则可以在ViewModel类中定义属性或私有方法,该方法将返回用户是否可以添加。例如,

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

在实例化RelayCommand时,您可以为引用上述方法的第二个参数传递lambda。

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

您将从ViewModel公开ICommand属性,该属性将返回RelayCommand对象。

希望这能让你开始。

答案 2 :(得分:1)

您的节点类必须恭维

  

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 Pattern