文本框值未在源对象上更新属性已更改

时间:2016-09-04 16:27:53

标签: c# wpf mvvm

我正在使用视图模型属性 SelectedUser 绑定TextBox文本,但在更新时,与此属性关联的控件不会反映更改。

我的ViewModel&模型

public class UserViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged.members
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion



    private ObservableCollection<UserModel> _userList;
    public ObservableCollection<UserModel> UserList
    {
        get
        {
            return _userList;
        }
        set
        {
            _userList = value;
        }
    }

    private UserModel _selectedUser;
    public UserModel SelectedUser
    {
        get
        {
            return _selectedUser;
        }
        set
        {
            _selectedUser = value;
            OnPropertyChanged("SelectedUser");
        }
    }


    public UserViewModel()
    {
        UserManager manager = new UserManager();

        var FilterCombo = new List<ComboItem> { 
        new ComboItem{Text = "Name", Value = "Name"},
        new ComboItem{Text = "Owner", Value = "Owner"},
        new ComboItem{Text = "Email", Value = "Email"},
        new ComboItem{Text = "Contact Number", Value = "Contact"},
        new ComboItem{Text = "Address", Value = "Address"},
        };
        var filter = new FilterModel
        {
            FilterItems = FilterCombo,
            FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
            FilterValue = ""
        };
        _userList = new ObservableCollection<UserModel>(manager.GetList(filter));
    }



}

public class UserModel :INotifyPropertyChanged
{
    public Database.User user { get; set; }

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

SelectedUser 属性正在更新DataGrid选择,如下所示

   <DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False" 
                          HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
                          SelectedValue="{Binding SelectedUser, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

查看内容

<Label Content="Login ID" Grid.Row="1"/>
                    <TextBox Grid.Row="2" Text="{Binding SelectedUser.Login, Mode=TwoWay}" x:Name="txtLogin" />
                    <Label Content="Password" Grid.Row="3"/>
                    <TextBox Grid.Row="4" Text="{Binding SelectedUser.Password, Mode=TwoWay}" x:Name="txtPassword"/>
                    <Label Content="Full Name" Grid.Row="5"/>
                    <TextBox Grid.Row="6" Text="{Binding SelectedUser.Name, Mode=TwoWay}" x:Name="txtName"/>
                    <Label Content="Email" Grid.Row="7"/>
                    <TextBox Grid.Row="8" Text="{Binding SelectedUser.Email, Mode=TwoWay}" x:Name="txtEmail"/>
                    <Label Content="Contact" Grid.Row="9"/>
                    <TextBox Grid.Row="10" Text="{Binding SelectedUser.Contact, Mode=TwoWay}" x:Name="txtContact" />

2 个答案:

答案 0 :(得分:1)

您的代码有多个问题。您的UserModel类没有绑定xaml的属性(行名称,电子邮件,联系人,...)。并且您不会为要绑定的所有属性触发OnPropertyChanged INotifyPropertyChange方法。我建议进行以下更改。

ViewModel基类

我会使用BindableBasePRISM作为视图模型的基类,它们具有更改属性,因此您不需要在每个视图模型中实现INotifyPropertyChanged接口

/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
public abstract class BindableBase : INotifyPropertyChanged
{
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Checks if a property already matches a desired value. Sets the property and
    /// notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">Name of the property used to notify listeners. This
    /// value is optional and can be provided automatically when invoked from compilers that
    /// support CallerMemberName.</param>
    /// <returns>True if the value was changed, false if the existing value matched the
    /// desired value.</returns>
    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        this.OnPropertyChanged(propertyName);

        return true;
    }

    /// <summary>
    /// Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">Name of the property used to notify listeners. This
    /// value is optional and can be provided automatically when invoked from compilers
    /// that support <see cref="CallerMemberNameAttribute"/>.</param>
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <typeparam name="T">The type of the property that has a new value</typeparam>
    /// <param name="propertyExpression">A Lambda expression representing the property that has a new value.</param>
    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
        this.OnPropertyChanged(propertyName);
    }
}

<强>模型

另外,我并不建议使用绑定的属性,因为你的模型类会因为需要实现INotifyPropertyChanged而变得丑陋。因此,如果您的Database.User课程不包含任何逻辑或不需要的字段,请将其用作模型或创建您自己的字段:

public class User 
{
    public string Name { get; set; }
    public string Password { get; set; }
    public string Login { get; set; }
    public string Email { get; set; }
    public string Contact { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

<强>扩展

要将Database.User转换为模型,您可以创建扩展方法

public static class DatabaseUserExtensions
{
    public static User ToModel(this Database.User x)
    {
        //convert the object of type Database.User to your User model clas here
    }

    public static IEnumerable<User> ToModels(this IEnumerable<Database.User> xs)
    {
        return xs.Select(x => x.ToModel());
    }
}

<强>视图模型

因为您的模型没有实现INotifyPropertyChanged,所以您需要为您的属性创建包装器。因此,将viewModel更改为以下内容:

//use the BindableBase class as base class
public class UserViewModel : BindableBase
{
    //use the correct Model class (the newly created or Database.User if it
    //is sufficient). 
    //also IEnumerable is sufficient
    //there is no need for an private field
    public IEnumerable<User> UserList { get; private set; } 

    //wrapper for SelectedUser.Name
    public string SelectedUserName
    {
        get { return SelectedUser.Name; }
        set
        {
            SelectedUser.Name = value;
            OnPropertyChanged(nameof(SelectedUserName));
        }
    }

    //add wrappers for the other properties like 
    //SelectedUserPassword, SelectedUserContact, ...

    //use the correct Model class 
    //(the newly created or Database.User if it is sufficient)
    private User _selectedUser;
    public User SelectedUser
    {
        get
        {
            return _selectedUser;
        }
        set
        {
            _selectedUser = value;
            // call the OnPropertyChanged of BindableBase 
            // (I would also recommend using nameof)
            OnPropertyChanged(nameof(SelectedUser));
            // call OnPropertyChanged for the other wrapper properties like
            // SelectedUserPassword, SelectedUserContact, ...
            OnPropertyChanged(nameof(SelectedUserName));
            // ...
        }
    }

    public UserViewModel()
    {
        UserManager manager = new UserManager();

        var FilterCombo = new List<ComboItem> { 
            new ComboItem{Text = "Name", Value = "Name"},
            new ComboItem{Text = "Owner", Value = "Owner"},
            new ComboItem{Text = "Email", Value = "Email"},
            new ComboItem{Text = "Contact Number", Value = "Contact"},
            new ComboItem{Text = "Address", Value = "Address"},
        };
        var filter = new FilterModel
        {
            FilterItems = FilterCombo,
            FilterSelected = FilterCombo.Where(t => t.Text == "Name").FirstOrDefault(),
            FilterValue = ""
        };

        //this uses now the extension method 
        //(your manager should be doing the converting and return the model
        //instead of the database user if the database user class is 
        //not sufficient)
        _userList = manager.GetList(filter).ToModels();
    }
}

查看

您在xaml中设置的所有属性都不是必需的(默认情况下应该为true)。

 <DataGrid Grid.Row="1" ItemsSource="{Binding Path=UserList}" AutoGenerateColumns="False" 
                      HorizontalAlignment="Stretch" IsReadOnly="True" SelectionMode="Single"
                      SelectedValue="{Binding Path=SelectedUser}">

并根据viewmodel中的更改

更改xaml的其余部分
<Label Content="Login ID" Grid.Row="1"/>
                <TextBox Grid.Row="2" Text="{Binding Path=SelectedUserLogin}" x:Name="txtLogin" />
                <Label Content="Password" Grid.Row="3"/>
                <TextBox Grid.Row="4" Text="{Binding Path=SelectedUserPassword}" x:Name="txtPassword"/>
                <Label Content="Full Name" Grid.Row="5"/>
                <TextBox Grid.Row="6" Text="{Binding Path=SelectedUserName}" x:Name="txtName"/>
                <Label Content="Email" Grid.Row="7"/>
                <TextBox Grid.Row="8" Text="{Binding Path=SelectedUserEmail}" x:Name="txtEmail"/>
                <Label Content="Contact" Grid.Row="9"/>
                <TextBox Grid.Row="10" Text="{Binding Path=SelectedUserContact}" x:Name="txtContact" />

答案 1 :(得分:0)

首先,SelectedUser类型为UserModel的{​​{1}}并不具备所有这些属性(登录名,密码,名称...),我想你的Database.User拥有它们。

我的简单解决方案是,您将所有View内容DataContext绑定到SelectedUser,并将textboxe的绑定更改为 - user.Login,user.Password ...