简体   繁体   English

WPF:在自定义创建的用户控件中的数据绑定

[英]WPF: Databinding in a customly created usercontrol

I created my own WPF user control, which is a text box with auto complete suggestions. 我创建了自己的WPF用户控件,这是一个带有自动完成建议的文本框。 The XAML looks like this: XAML看起来像这样:

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

The Code behind looks like this: 后面的代码如下所示:

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

If I use this user control with eg 如果我将此用户控件与例如

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

then the databinding works pretty well for the suggestions list, but it does not work with the text of the Textbox. 那么数据绑定对于建议列表效果很好,但不适用于文本框的文本。 I don't know what I did wrong. 我不知道我做错了什么。 Can anybody help? 有人可以帮忙吗? I would be very grateful. 我会很感激。

The getter and setter of a CLR wrapper for a dependency property should only call the GetValue and SetValue methods. 依赖项属性的CLR包装器的getter和setter 应调用GetValueSetValue方法。 You should use a callback if you want to anything else when the property is being set. 如果要在设置属性时进行其他操作,则应使用回调。

You should probably also set the default binding mode of the Text property to TwoWay : 您可能还应该将Text属性的默认绑定模式设置为TwoWay

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM