我是WPF及其数据绑定的新手,但我偶然发现了一个我无法解决的奇怪行为.
在Dialog中,我有一个包含用户的Listbox和一个用户名的TextBox.两者都绑定到UserLogonLogic,后者发布CurrentUser
属性.
我希望TextBox在单击ListBox中的名称时更新其文本.SelectedItem
当我直接在TextBox中输入用户名时,我也希望更新ListBox中的内容.TextBox中的部分名称将解析为列表框中的第一个匹配值,如果没有,则为null.
每次点击ListBox时,TextBox都会更新.Debug告诉我每次激活PropertyChangeEvent
for时都会调用CurrentUser
方法txtName_TextChanged
方法.只有在我在文本框中键入内容后DataBinding
,TextBox似乎才会丢失.当我点击ListBox时,TextBox将不再进一步更新.Debug现在告诉我,在触发txtName_TextChanged
后不再调用该方法CurrentUser
PropertyChangeEvent
.
有没有人知道我哪里可能出错?
非常感谢
Rü
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}" />
Run Code Online (Sandbox Code Playgroud)
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--;
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个很好的视图模型的工作.在视图模型上定义两个属性:
SelectedUser : User
UserEntry : string
绑定ListBox
的SelectedItem
的SelectedUser
属性和TextBox
的Text
属性的UserEntry
属性.然后,在您的视图模型中,您可以完成工作以使它们保持同步: - 如果SelectedUser
更改,则设置UserEntry
为该用户Name
- 如果UserEntry
更改,则对所有用户进行智能搜索SelectedUser
,null
如果未找到匹配则设置为或者第一个匹配User
这是一个完整且有效的样本.我希望我现在可以轻松附上一个zip文件.
首先,ViewModel.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));
}
}
Run Code Online (Sandbox Code Playgroud)
User.cs:
public class User : ViewModel
{
private readonly string _name;
public User(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
}
}
Run Code Online (Sandbox Code Playgroud)
LogonViewModel.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));
}
}
Run Code Online (Sandbox Code Playgroud)
UserLogon.xaml:
<UserControl x:Class="WpfApplication1.UserLogon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<StackPanel>
<ListBox ItemsSource="{Binding Users}" SelectedItem="{Binding SelectedUser}" DisplayMemberPath="Name"/>
<TextBox Text="{Binding UserEntry, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</UserControl>
Run Code Online (Sandbox Code Playgroud)
UserLogon.xaml.cs:
public partial class UserLogon : UserControl
{
public UserLogon()
{
InitializeComponent();
//would normally map view model to view with a DataTemplate, not manually like this
DataContext = new LogonViewModel();
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2382 次 |
最近记录: |