2017-07-03 12 views
0

独自のWPFユーザーコントロールを作成しました。このユーザーコントロールは、完全な自動提案のテキストボックスです。 XAMLは次のようになります。WPF:カスタムで作成されたユーザーコントロールのデータバインド

<UserControl x:Class="WpfApplication4.AutoCompleteTextBox" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:WpfApplication4" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<StackPanel Orientation="Vertical"> 
    <TextBox x:Name="textBox" /> 
    <ListBox x:Name="listBox" MaxHeight="100"/> 
</StackPanel> 

コードの後ろに次のようになります。

using System; 
using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 

namespace WpfApplication4 
{ 
/// <summary> 
/// A WPF component which has a textbox. If the user enters some text, a ListBox is displayed showing some suggestions which the user can follow or not. 
/// </summary> 
public partial class AutoCompleteTextBox : UserControl 
{ 

    /// <summary> 
    /// This event is invoked when the text of the textbox is changed. 
    /// </summary> 
    public event TextChangedEventHandler TextChanged; 

    /// <summary> 
    /// Needed for DataBinding of Suggestions 
    /// </summary> 
    public static DependencyProperty SuggestionsProperty; 

    /// <summary> 
    /// Needed for data binding of Text 
    /// </summary> 
    public static DependencyProperty TextProperty; 

    /// <summary> 
    /// A list of the suggestions which are displayed to the user. 
    /// </summary> 
    public List<string> Suggestions { get 
     { 
      return (List<string>) GetValue(SuggestionsProperty); 
     } 
     set 
     { 
      SetValue(SuggestionsProperty, value); 
     } 

    } 

    /// <summary> 
    /// True if showing of the suggestions is case-sensitive; false, if case-insensitive 
    /// </summary> 
    public bool CaseSensitive { get; set; } 

    /// <summary> 
    /// The text displayed inside the textbox 
    /// </summary> 
    public string Text 
    { 
     get 
     { 
      return textBox.Text; 
      //return (string)GetValue(TextProperty); 
     } 
     set 
     { 
      SetValue(TextProperty, value); 
      textBox.Text = value; 
     } 
    } 

    /// <summary> 
    /// Create a new AutoCompleteTextBox 
    /// </summary> 
    public AutoCompleteTextBox() 
    { 
     InitializeComponent(); 
     Suggestions = new List<string>(); 
     DataContext = this; 
     listBox.Visibility = Visibility.Collapsed; 
     CaseSensitive = true; 
     textBox.TextChanged += ExternalTextEvent; 
     textBox.TextChanged += textChanged; 
     textBox.PreviewKeyDown += keyDown; 
     textBox.LostFocus += lostFocus; 
     textBox.GotFocus += gotFocus; 
     listBox.SelectionChanged += selectionChanged; 
    } 

    private void ExternalTextEvent(object sender, TextChangedEventArgs e) 
    { 
     TextChanged?.Invoke(this, e); 
    } 

    static AutoCompleteTextBox() 
    { 
     SuggestionsProperty = DependencyProperty.Register(nameof(Suggestions), typeof(List<string>), typeof(AutoCompleteTextBox)); 
     TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(AutoCompleteTextBox)); 
    } 

    private void gotFocus(object sender, RoutedEventArgs e) 
    { 
     updateListBox(); 
    } 

    private void lostFocus(object sender, RoutedEventArgs e) 
    { 
     listBox.Visibility = Visibility.Collapsed; 
    } 

    private void keyDown(object sender, KeyEventArgs e) 
    { 
     if (e.Key == Key.Enter || e.Key == Key.Return) 
     { 
      if (listBox.Visibility == Visibility.Visible) 
      { 
       listBox.Visibility = Visibility.Collapsed; 
       textBox.TextChanged -= textChanged; 
       textBox.Text = (string)listBox.Items[0]; 
       textBox.TextChanged += textChanged; 
      } 
     } 
     if (e.Key == Key.Escape) 
     { 
      listBox.Visibility = Visibility.Collapsed; 
     } 
     //if(e.Key == Key.Down && listBox.Visibility == Visibility.Visible) 
     //{ 
     // textBox.LostFocus -= lostFocus; 
     // listBox.SelectedIndex = 0; 
     // textBox.LostFocus += lostFocus; 
     //} 
    } 

    private void selectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (listBox.ItemsSource != null) 
     { 
      listBox.Visibility = Visibility.Collapsed; 
      textBox.TextChanged -= textChanged; 
      if (listBox.SelectedIndex != -1) 
      { 
       textBox.Text = listBox.SelectedItem.ToString(); 
      } 
      textBox.TextChanged += textChanged; 
     } 
    } 

    private void textChanged(object sender, TextChangedEventArgs e) 
    { 
     SetValue(TextProperty, textBox.Text); 
     updateListBox(); 
    } 

    private void updateListBox() 
    { 
     if (String.IsNullOrEmpty(textBox.Text) || Suggestions == null || Suggestions.Count == 0) 
     { 
      return; 
     } 
     List<string> autoList = new List<string>(); 
     foreach (string item in Suggestions) 
     { 
      if (CaseSensitive && item.StartsWith(textBox.Text)) 
      { 
       autoList.Add(item); 
      } 
      if (!CaseSensitive && item.ToUpper().StartsWith(textBox.Text.ToUpper())) 
      { 
       autoList.Add(item); 
      } 
     } 
     if (autoList.Count > 0) 
     { 
      listBox.ItemsSource = autoList; 
      listBox.Visibility = Visibility.Visible; 
     } 
     else 
     { 
      listBox.Visibility = Visibility.Collapsed; 
      listBox.ItemsSource = null; 
     } 
    } 
} 
} 

私は、例えばこのユーザーコントロールを使用している場合

<local:AutoCompleteTextBox x:Name="autocomplete" Suggestions="{Binding SomeList}" Text="{Binding SomeText,UpdateSourceTrigger=PropertyChanged}"/> 

この場合、データバインディングは提案リストではうまく機能しますが、テキストボックスのテキストでは機能しません。私は何が悪いのか分からない。誰でも助けることができますか?私は非常に感謝されます。依存関係プロパティのCLRラッパーの

+0

は、あなたがテキストを置きたいと思うかもしれ= "{バインディングSomeText、モード=双方向、UpdateSourceTrigger =のPropertyChanged}" /> –

+0

をあなたはコントロールのかなり悪いアーキテクチャを持っています。 'Text'プロパティが' TextBox.Text'のプロキシであるのはなぜですか? 'GetValue' /' SetValue'を使用し、XAMLの 'TextBox.Text'のためにこのプロパティにバインドする必要があります。 WPFの基礎を学び、 'DataContext = this;'のようなコードを書くことはありません – Maxim

答えて

0

ゲッターとセッターはのみGetValueSetValueメソッドを呼び出す必要があります。プロパティが設定されているときに他に何かしたい場合は、コールバックを使用する必要があります。

あなたはおそらくも TwoWayTextプロパティのモードをデフォルトのバインディングを設定する必要があり

public string Text 
{ 
    get 
    { 
     return (string)GetValue(TextProperty); 
    } 
    set 
    { 
     SetValue(TextProperty, value); 
    } 
} 

static AutoCompleteTextBox() 
{ 
    SuggestionsProperty = DependencyProperty.Register(nameof(Suggestions), typeof(List<string>), typeof(AutoCompleteTextBox)); 
    TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(AutoCompleteTextBox), 
     new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextChanged)) { BindsTwoWayByDefault = true }); 
} 

private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
{ 
    AutoCompleteTextBox ctrl = d as AutoCompleteTextBox; 
    ctrl.textBox.Text = e.NewValue as string; 
} 
+0

これはうまくいきました。さらに、keyDownメソッドとselectionChangedメソッドで、 "textbox.Text = ..."を "Text = ..."に変更して、バインディングが常に最新であるようにしました。 – SomeBody

関連する問題