MVVM绑定到属性的奇怪行为

时间:2018-02-08 12:03:52

标签: c# wpf mvvm

我是MVVM的初学者。我正在编写名为Members的简单应用程序。这是我的成员类(模型):

class Member: INotifyPropertyChanged
{
    public Member(string name)
    {
        Name = name;
        _infoCommand = new InfoCommand(this);
    }

    string _name;

    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name= value;
            notify("Name");
            notify("CanShowInfo");
        }
    }

    public override string ToString()
    {
        return Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void notify(string property_name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property_name));
        }
    }

    private ICommand _infoCommand;
    public ICommand InfoCommand
    {
        get
        {
            return _infoCommand;
        }
        set
        {
            _infoCommand = value;
        }
    }

    public bool CanShowInfo
    {
        get
        {
            return _infoCommand.CanExecute(null);
        }
    }
}

这是我的InfoCommand类:

class InfoCommand : ICommand
{
    Member _member;

    public InfoCommand(Member member)
    {
        _member = member;
    }

    public bool CanExecute(object parameter)
    {
        if (_member.Jmeno.Length > 0)
            return true;
        else
            return false;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        MessageBox.Show("I am " + _member.Name);
    }
}

这是我的MemberViewModel类:

class MembersViewModel : INotifyPropertyChanged
{
    ObservableCollection<Member> _members = new ObservableCollection<Member>();

    public MembersViewModel()
    {
        Members.Add(new Member("Member1"));
        Members.Add(new Member("Member2"));
        Members.Add(new Member("Member3"));
        Members.Add(new Member("Member4"));
        Members.Add(new Member("Member5"));
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void notify(string property_name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property_name));
    }

    Member _selectedMember;

    public Member SelectedMember
    {
        get
        {
            return _selectedMember;
        }

        set
        {
            _selectedMember= value;
            notify("SelectedMember");
        }
    }

    public ObservableCollection<Member> Members
    {
        get
        {
            return _members;
        }
        set
        {
            _members = value;
        }
    }

    AddCommand _addCommand;

    public AddCommand AddCommand
    {
        get
        {
            if (_addCommand == null)
                _addCommand = new AddCommand(this);
            return _addCommand;
        }
    }
}

这是我的AddCommand:

类AddCommand:ICommand     {         MembersViewModel _vm;

    public AddCommand(MembersViewModel vm)
    {
        _vm = vm;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _vm.Members.Add(new Member("New Member")); //<-------------------------
    }
}

最后我的观点:

<Window x:Class="mvvm_gabriel.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:mvvm_gabriel.ViewModel"
    Title="MainWindow" Height="482" Width="525">
<Window.Resources>
</Window.Resources>
<Window.DataContext>
    <ViewModels:MembersViewModel />
</Window.DataContext>
<Grid>
    <ListView ItemsSource="{Binding Members}" 
              SelectedItem="{Binding SelectedMember, Mode=TwoWay}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" />
                    <Button Grid.Column="1" Content="Info" Width="50" HorizontalAlignment="Left" Command="{Binding InfoCommand}" IsEnabled="{Binding Path=CanShowInfo, Mode=OneWay}" />

                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    <TextBox Text="{Binding SelectedMember.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <Button Content="Add" Command="{Binding AddCommand}" />
</Grid>

当我点击ListView中的某个成员时,他的名字显示在TextBox中。现在我可以编辑此名称,并自动更新我的Member对象的属性。当我完全删除某个成员的名称(string.Length == 0)时,我的成员模板中的信息按钮被禁用。

我也可以点击添加按钮添加新成员。成员被添加到我的可观察集合中并自动显示在ListView中。

就这里而言,一切都很完美。

但现在:在我的AddCommand.Execute方法中查看标记为<----------------------的行。当我向我的收藏中添加新会员时,我会自动给他起名字#34;新会员&#34;一切正常。然后我可以调整我的会员姓名,我的按钮会自动禁用,如上所述。但是当我在标记行的构造函数中给出空字符串作为新成员的名称时,启用我的信息按钮退出工作。我可以给我的新成员任何名字,我的信息按钮仍然被禁用。

有人可以解释一下并建议一些解决方案吗?

1 个答案:

答案 0 :(得分:0)

主窗口中的按钮将按钮的IsEnabled绑定到模型中的属性,但命令绑定也会使按钮询问命令的CanExecute()。

<Button Grid.Column="1" Content="Info" Width="50" HorizontalAlignment="Left" Command="{Binding InfoCommand}" IsEnabled="{Binding Path=CanShowInfo, Mode=OneWay}" />

这会导致混乱的行为,如您的情况所示。

您基本上可以删除按钮的IsEnabled绑定,并将属性更改处理程序添加到InfoCommand。

public class InfoCommand : ICommand
{
    Member _member;

    public InfoCommand(Member member)
    {
        _member = member;
        _member.PropertyChanged += _member_PropertyChanged;
    }

    private void _member_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Name")
            RaiseCanExecuteChanged();
    }

    private void RaiseCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }

    public bool CanExecute(object parameter)
    {
        if (_member.Name.Length > 0)
            return true;
        else
            return false;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        MessageBox.Show("I am " + _member.Name);
    }
}