2017-07-03 5 views
0

Ich erstellte mein eigenes WPF-Benutzersteuerelement, das ein Textfeld mit Auto-Vervollständigen-Vorschlägen ist. Die XAML sieht wie folgt aus:WPF: Datenbindung in einem benutzerdefinierten Benutzersteuerelement

<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> 

Der Kodex hinter sieht wie folgt aus:

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

Wenn ich diesen Benutzersteuerung mit z.B.

dann funktioniert die Datenbindung ziemlich gut für die Liste der Vorschläge, aber es funktioniert nicht mit dem Text der Textbox. Ich weiß nicht, was ich falsch gemacht habe. Kann jemand helfen? Ich wäre sehr dankbar.

+0

Möglicherweise möchten Sie Text setzen = "{Binding EINTEXT, Mode = TwoWay, Update = Property}" /> –

+0

Sie haben eine ziemlich schlechte Architektur Ihrer Kontrolle. Warum ist die 'Text' -Eigenschaft ein Proxy für' TextBox.Text'? Sie müssen 'GetValue' /' SetValue' verwenden und eine Bindung zu dieser Eigenschaft in XAML für 'TextBox.Text' verwenden. Bitte lerne die Grundlagen des WPF und du wirst niemals Code schreiben wie 'DataContext = this;' – Maxim

Antwort

0

Der Getter und Setter einer CLR-Wrapper für eine Abhängigkeitseigenschaft sollte nur nennen sich die GetValue und SetValue Methoden. Sie sollten einen Rückruf verwenden, wenn Sie beim Festlegen der Eigenschaft noch etwas anderes möchten.

Sie sollten wahrscheinlich auch die Standardbindungsmodus des Text Eigenschaft auf TwoWay gesetzt:

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

Das hat funktioniert, vielen Dank. Außerdem habe ich in der Methode keyDown und in der Methode selectionChanged "textbox.Text = ..." in "Text = ..." geändert, damit die Bindung immer auf dem neuesten Stand ist. – SomeBody

Verwandte Themen