WPF丢失了数据绑定

时间:2009-03-13 09:59:23

标签: c# .net wpf data-binding

我是WPF及其数据绑定的新手,但我偶然发现了一个我无法为自己解决的奇怪行为。

在对话框中,我有一个包含用户的列表框和一个用于用户名的TextBox。两者都绑定到UserLogonLogic,后者发布CurrentUser属性。

我希望TextBox在单击ListBox中的名称时更新其文本。当我直接在TextBox中输入用户名时,我还希望更新ListBox中的SelectedItem。 TextBox中的部分名称将被解析为列表框中的第一个匹配值,如果没有,则为null。

首先,每次点击ListBox时,TextBox都会更新。调试向我显示,每次PropertyChangeEvent CurrentUser被触发时,都会调用方法txtName_TextChanged方法。只有在我在文本框中输入内容后,TextBox的DataBinding才会丢失。当我点击ListBox时,TextBox将不再进一步更新。调试现在告诉我,在txtName_TextChanged CurrentUser被触发后,方法PropertyChangeEvent不再被调用。

有没有人知道我哪里可能出错?

非常感谢 RU

UserLogon.xaml:

    <ListBox Grid.Column="0" Grid.Row="1" Grid.RowSpan="4" MinWidth="100" Margin="5" Name="lstUser" MouseUp="lstUser_MouseUp"
             ItemsSource="{Binding Path=Users}" SelectedItem="{Binding Path=CurrentUser, Mode=TwoWay}"/>
    <TextBox Grid.Column="1" Grid.Row="1" Margin="3" Name="txtName" TextChanged="txtName_TextChanged"
             Text="{Binding Path=CurrentUser, Mode=OneWay}" />

UserLogon.xaml.cs:

    public UserLogon()
    {
        InitializeComponent();

        _logic = new UserLogonLogic();
        TopLevelContainer.DataContext = _logic;
    }

    private int _internalChange = 0;
    private void txtName_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (_internalChange > 0)
        {
            return;
        }

        _internalChange++;
        string oldName = txtName.Text;
        User user = _logic.SelectByPartialUserName(oldName);
        string newName = (user == null) ? "" : user.Name;

        if (oldName != newName)
        {
            txtName.Text = (newName == "") ? oldName : newName;
            txtName.Select(oldName.Length, newName.Length);
        }
        _internalChange--;
    }

UserLogon.Logic.cs:

public class UserLogonLogic : INotifyPropertyChanged
{
    private User _currentUser;
    public User CurrentUser
    {
        get { return _currentUser; }
        set
        {
            if (value != CurrentUser)
            {
                _currentUser = value;
                OnPropertyChanged("CurrentUser");
            }
        }

    private IEnumerable<User> _users;
    public IEnumerable<User> Users
    {
        get
        {
            if (_users == null)
            {
                List<User> _users = Database.GetAllUsers();
            }
            return _users;
        }
    }

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

    public User SelectByPartialUserName(string value)
    {
        if (value != "")
        {
            IEnumerable<User> allUser = GetAllUserByName(value);
            if (allUser.Count() > 0)
            {
                CurrentUser = allUser.First();
            }
            else
            {
                CurrentUser = null;
            }
        }
        else
        {
            CurrentUser = null;
        }

        return CurrentUser;
    }

    private IEnumerable<User> GetAllUserByName(string name)
    {
        return from user in Users
               where user.Name.ToLower().StartsWith(name.ToLower())
               select user;
    }
}

2 个答案:

答案 0 :(得分:7)

这是一个很好的视图模型的工作。在视图模型上定义两个属性:

  • SelectedUser : User
  • UserEntry : string

ListBox的{​​{1}}绑定到SelectedItem属性,将SelectedUser的{​​{1}}属性绑定到TextBox属性。然后,在您的视图模型中,您可以完成工作以使它们保持同步:   - 如果Text发生变化,请将UserEntry设置为该用户的SelectedUser   - 如果UserEntry发生变化,请对所有用户进行智能搜索,如果未找到匹配项,则将Name设置为UserEntry,或者首先匹配SelectedUser

这是一个完整且有效的样本。我希望我现在可以轻松附上一个zip文件。

首先, ViewModel.cs

null

User.cs

User

LogonViewModel.cs

public abstract class ViewModel : INotifyPropertyChanged
{
    private readonly Dispatcher _dispatcher;

    protected ViewModel()
    {
        if (Application.Current != null)
        {
            _dispatcher = Application.Current.Dispatcher;
        }
        else
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected Dispatcher Dispatcher
    {
        get { return _dispatcher; }
    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        var handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, e);
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
}

UserLogon.xaml

public class User : ViewModel
{
    private readonly string _name;

    public User(string name)
    {
        _name = name;
    }

    public string Name
    {
        get { return _name; }
    }
}

UserLogon.xaml.cs

public class LogonViewModel : ViewModel
{
    private readonly ICollection<User> _users;
    private User _selectedUser;
    private string _userEntry;

    public LogonViewModel()
    {
        _users = new List<User>();
        //fake data
        _users.Add(new User("Kent"));
        _users.Add(new User("Tempany"));
    }

    public ICollection<User> Users
    {
        get { return _users; }
    }

    public User SelectedUser
    {
        get { return _selectedUser; }
        set
        {
            if (_selectedUser != value)
            {
                _selectedUser = value;
                OnPropertyChanged("SelectedUser");
                UserEntry = value == null ? null : value.Name;
            }
        }
    }

    public string UserEntry
    {
        get { return _userEntry; }
        set
        {
            if (_userEntry != value)
            {
                _userEntry = value;
                OnPropertyChanged("UserEntry");
                DoSearch();
            }
        }
    }

    private void DoSearch()
    {
        //do whatever fuzzy logic you want here - I'm just doing a simple match
        SelectedUser = Users.FirstOrDefault(user => user.Name.StartsWith(UserEntry, StringComparison.OrdinalIgnoreCase));
    }
}

答案 1 :(得分:1)

你的文本框不应该有双重绑定吗?