简体   繁体   English

MVVM绑定到属性的奇怪行为

[英]Strange behavior of MVVM binding to property

I am beginner in MVVM. 我是MVVM的初学者。 I am writing simple app called Members. 我正在写一个叫做会员的简单应用。 This is my member class (model): 这是我的成员类(模型):

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);
        }
    }
}

This is my InfoCommand class: 这是我的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);
    }
}

This is my MemberViewModel class: 这是我的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;
        }
    }
}

This is my AddCommand: 这是我的AddCommand:

class AddCommand : ICommand { MembersViewModel _vm; class 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")); //<-------------------------
    }
}

And finally my View: 最后是我的观点:

<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>

When I click some member in my ListView, his name is shown in TextBox. 当我单击ListView中的某个成员时,其名称将显示在TextBox中。 Now I can edit this name and property of my Member object is updated automatically. 现在,我可以编辑此名称,并且Member对象的属性会自动更新。 When I delete name of some member completely (string.Length == 0), Info button in my member template is disabled. 当我完全删除某个成员的名称(string.Length == 0)时,成员模板中的“信息”按钮将被禁用。

I can also add new members by clicking Add button. 我也可以通过单击添加按钮来添加新成员。 Member is added to my observable collection and automatically shown in ListView. 成员已添加到我的可观察集合中,并自动显示在ListView中。

Everything works perfectly as far as here. 一切都在这里进行得很好。

But now: look at line marked like this <---------------------- in my AddCommand.Execute method. 但是现在:在我的AddCommand.Execute方法中查看标记为<----------------------的行。 When I add new member to my collection, I automatically give him name "New Member" and everything works fine. 当我向集合中添加新成员时,我会自动给他起“新成员”的名字,并且一切正常。 I can then adit my member's name and my button is disabled automatically as described above. 然后,我可以输入会员的姓名,并且如上所述,我的按钮被自动禁用。 But when I give empty string as the name for new member in constructor on marked line, enabling of my Info button quits working. 但是,当我在标记行上将空字符串作为构造函数中新成员的名称时,启用“信息”按钮将退出工作。 I can give my new member any name and my Info button is still disabled. 我可以给新成员起任何名字,而我的“信息”按钮仍处于禁用状态。

Can anyone explain it and suggest some solution, please? 任何人都可以解释一下并提出一些解决方案吗?

Your button in the mainwindow is binding the IsEnabled of the button to a property in the model, but the command binding will also cause the button to interrogate the CanExecute() of the command. 您在主窗口中的按钮会将按钮的IsEnabled绑定到模型中的属性,但是命令绑定也会导致按钮查询命令的CanExecute()。

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

This can lead to confusing behavior, as seen in your case. 如您所见,这可能导致混乱的行为。

You can basically remove the IsEnabled binding of the button, and add the property changed handler to the InfoCommand. 您基本上可以删除按钮的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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM