我是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;
}
}
答案 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)
你的文本框不应该有双重绑定吗?