WPF丢失了数据绑定

1 .net c# data-binding wpf

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

在Dialog中,我有一个包含用户的Listbox和一个用户名的TextBox.两者都绑定到UserLogonLogic,后者发布CurrentUser属性.

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

每次点击ListBox时,TextBox都会更新.Debug告诉我每次激活PropertyChangeEventfor时都会调用CurrentUser方法txtName_TextChanged方法.只有在我在文本框中键入内容后DataBinding,TextBox似乎才会丢失.当我点击ListBox时,TextBox将不再进一步更新.Debug现在告诉我,在触发txtName_TextChanged后不再调用该方法CurrentUser PropertyChangeEvent.

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

非常感谢

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)

Ken*_*art 7

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

  • SelectedUser : User
  • UserEntry : string

绑定ListBoxSelectedItemSelectedUser属性和TextBoxText属性的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)