2009-03-13 5 views
1

저는 WPF와 Databinding을 처음 접했지만 스스로 해결할 수없는 이상한 행동을 발견했습니다.WPF lost Databinding

대화 상자에는 사용자가있는 ListBox와 사용자 이름이있는 TextBox가 있습니다. 둘 다 UserLogonLogic에 바인딩되어 있습니다.이 UserLogonLogic은 CurrentUser 속성을 게시합니다.

ListBox에서 이름을 클릭하면 텍스트 상자의 텍스트가 업데이트되기를 원합니다. 또한 TextBox에 사용자 이름을 직접 입력 할 때 ListBox의 SelectedItem을 업데이트하려고합니다. TextBox의 부분 이름은 목록 상자에서 첫 번째로 일치하는 값으로 확인되거나없는 경우 null로 확인됩니다.

처음에는 ListBox를 클릭 할 때마다 TextBox가 업데이트됩니다. 디버그는 CurrentUser에 대한 PropertyChangeEvent이 실행될 때마다 txtName_TextChanged 메서드가 호출된다는 것을 보여줍니다. 텍스트 상자에 뭔가를 입력 한 후에야 TextBox DataBinding이 손실 된 것 같습니다. ListBox를 클릭하면 TextBox를 더 이상 업데이트하지 않습니다. 디버그 이제 PropertyChangeEvent이 해고 된 후에 txtName_TextChanged 메서드가 더 이상 호출되지 않음을 보여줍니다.

누구나 내가 잘못했을 수있는 생각이 있습니까?

덕분에 많은
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; 
    } 
} 

답변

7

좋은보기 모델을위한 작업입니다. 뷰 모델에 두 가지 속성을 정의

  • UserEntry : string
    • SelectedUser : User
    • 바인드 ListBoxSelectedItem SelectedUser 재산 및 TextBox 's를 UserEntry 재산 Text 속성입니다. 그런 다음 뷰 모델에서 당신은 동기화에 보관하는 작업을 수행 할 수 있습니다 을 - SelectedUser 변경하는 경우에 UserEntry을 설정 한 사용자의 Name - UserEntry 변경, 모든 사용자를 통해 지능적인 검색을 수행하고 설정하면 경우 중 하나 nullSelectedUser 없음 일치 항목을 찾았거나 첫 번째 일치 항목을 찾았습니다. 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)); 
          } 
      } 
      

      User.cs :

      public class User : ViewModel 
      { 
          private readonly string _name; 
      
          public User(string name) 
          { 
           _name = name; 
          } 
      
          public string Name 
          { 
           get { return _name; } 
          } 
      } 
      

      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)); 
          } 
      } 
      

      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> 
      

      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(); 
          } 
      } 
      
    1

    텍스트 상자에 twoway 바인딩이 있으면 안됩니까?

    +0

    사실 나는 적극적으로의 TextChanged의 ATTRIB를 설정하기 때문에, 그것은해야한다고 생각하지 말아. 그래서 나는 항상 바인딩을 사용하여 CurrentUser가 변경 될 때 항상 "수동적으로"값을 읽도록 보장합니다. TextBox의 모든 활성 변경 사항은 txtName_TextChanged에 의해 처리됩니다. – Savinien

    +0

    TwoWay로 시도해 보았습니다. 결과는 프로그램이 제대로 작동하지 않는다는 것입니다. 처음부터 SelectedItem을 사용한 변경 사항은 TextBox에 반영되지 않습니다. – Savinien