c# - MVVM 绑定(bind)到属性的奇怪行为

标签 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 : ICommand
{
成员 View 模型_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 方法中。当我将新成员添加到我的收藏中时,我会自动给他起名为“新成员”并且一切正常。然后我可以编辑我的成员(member)姓名,并且我的按钮会自动禁用,如上所述。但是,当我在标记行的构造函数中将空字符串作为新成员的名称时,启用我的信息按钮将停止工作。我可以给我的新成员起任何名字,但我的信息按钮仍然被禁用。

谁能解释一下并提出一些解决方案,好吗?

最佳答案

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

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

如您的案例所示,这可能会导致令人困惑的行为。

您基本上可以删除按钮的 IsEnabled 绑定(bind),并将属性更改处理程序添加到 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);
    }
}

关于c# - MVVM 绑定(bind)到属性的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48685058/

相关文章:

c# - 从工作线程访问 UI 线程集合

c# - 正则表达式匹配除

c# - WPF MetroTheme = 图标资源

wpf - 弹出窗口永不关闭

wpf - ViewModel 构造函数包含 WPF 中的参数

c# - 将 INotifyPropertyChanged 添加到模型?

c# - Winforms 文本框掩码

c# - 可以跟踪由 .NET Web 服务客户端发送的 SOAP 信封吗?

c# - SQL Server 加密和 nhibernate

wpf - 当文本太长时自动垂直扩展文本框