2009-03-13 11 views
1

私はWPFとDatabindingの新機能ですが、自分で解決できない奇妙な動作に遭遇しました。WPF lost Databinding

私はダイアログボックスでユーザー名付きのリストボックスとユーザー名用のテキストボックスを持っています。両方ともUserLogonLogicにバインドされています。これは、とりわけCurrentUserプロパティを公開します。

ListBoxで名前をクリックすると、TextBoxのテキストが更新されます。また、TextBoxにユーザー名を直接入力すると、リストボックス内のSelectedItemが更新されます。 TextBox内の部分的な名前は、リストボックス内の最初に一致する値に解決されます。存在しない場合はnullになります。

まず、ListBoxをクリックするたびにTextBoxが更新されます。デバッグは、CurrentUserPropertyChangeEventが呼び出されるたびにメソッドtxtName_TextChangedメソッドが呼び出されることを示しています。テキストボックスに何かを入力した後で、TextBoxのDataBindingが失われているようです。 ListBoxをクリックすると、TextBoxの更なる更新はありません。 Debugは、CurrentUserPropertyChangeEventが起動された後で、メソッド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

これは、良好なビューモデルの仕事です。あなたのビューモデルに2つのプロパティを定義します。

  • UserEntry : string
    • SelectedUser : User
    • バインド ListBoxSelectedItem SelectedUserプロパティに、そして TextBoxUserEntryプロパティに Textプロパティ。次に、あなたのビューモデルにあなたは同期でそれらを保つために作業を行うことができます。 - SelectedUser変更は、そのユーザの NameUserEntryを設定している場合 - UserEntry変更は、すべてのユーザーによるインテリジェントな検索を行う場合と無い場合 nullのいずれかに SelectedUserを設定一致が見つかりました、または最初の一致 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