在TextChanged上激发方法,但不是一直这样

本文关键字:一直 TextChanged 方法 | 更新日期: 2023-09-27 17:58:31

我正在创建一个简单的自动完成文本框,并有一个值列表,如果用户开始在其中输入任何字符字符串,就会出现相应的字符串。

现在,我已经创建了我的文本框,并将Binding属性绑定到我的ViewModel:

<TextBox Text="{Binding ServerURL, UpdateSourceTrigger=PropertyChanged}" />

因此,当用户输入一个新字符时,它将触发我的属性被激发,从而激发一个方法,该方法将检索与其相关的值。

private string _serverURL;
public string ServerURL {
    get { return _serverURL; }
    set
    {
        _serverURL = value;
        ServerURL_TextChanged();
        OnPropertyChanged("ServerURL");
    }
}

然后,该方法将使用字符串引用的结果填充ListBox

当我从ListBox中选择一个值时,我想将Full字符串值设置为TextBox文本属性,但当我这样做时,它会触发ServerURL_TextChanged()方法。

有没有一种方法可以设置ServerURL属性,但不触发其中的方法?

在TextChanged上激发方法,但不是一直这样

对于解决方案,需要分离设置ServerURL属性的方法。

public string ServerURL {
    get { return _serverURL; }
    set
    {
        setServerURL(value, isSetByUser = true);        
    }
} 
private function void setServerURL(string value, bool isSetByUser){
        _serverURL = value;
        ServerURL_TextChanged(isSetByUser);
        OnPropertyChanged("ServerURL");
}

当列表更改时,您可以从代码setServerURL(someValue, isSetByUser = false);调用然后在ServerURL_TextChanged实现中决定如何处理它。

实现此功能的最简单方法是从代码后面处理TextChanged事件,在代码后面可以完全控制这种决策的UI。从代码背后管理UI操作并不违反MVVM原则。

下面是这样一个代码隐藏实现的示例。你可能会发现它很有用。

public partial class AutoCompleteComboBox : UserControl
    {
        private Window w;
        public AutoCompleteComboBox()
        {
            InitializeComponent();
        }
        ~AutoCompleteComboBox()
        {
            if(w == null)
            {
                return;
            }
            else
            {
                w.MouseLeftButtonDown -= Window_MouseLeftDown;
            }
        }

        #region Behaviours
        public void FocusTextBox()
        {
            txt.Focus();
            txt.CaretIndex = txt.Text.Length;
        }
        #endregion  
        #region DependencyProperties
        public static readonly DependencyProperty InputPaddingProperty =
            DependencyProperty.Register(
                "InputPadding",
                typeof(Thickness),
                typeof(AutoCompleteComboBox)
                );
        public Thickness InputPadding
        {
            get
            {
                return (Thickness)GetValue(InputPaddingProperty);
            }
            set
            {
                SetValue(InputPaddingProperty, value);
            }
        }
        public static readonly DependencyProperty TextBoxHeightProperty =
            DependencyProperty.Register(
                "TextBoxHeight",
                typeof(double),
                typeof(AutoCompleteComboBox)
                );
        public double TextBoxHeight
        {
            get
            {
                return (double)GetValue(TextBoxHeightProperty);
            }
            set
            {
                SetValue(TextBoxHeightProperty, value);
            }
        }
        public static readonly DependencyProperty ItemPanelMaxHeightProperty =
            DependencyProperty.Register(
                "ItemPanelMaxHeight",
                typeof(double),
                typeof(AutoCompleteComboBox)
                );
        public double ItemPanelMaxHeight
        {
            get
            {
                return (double)GetValue(ItemPanelMaxHeightProperty);
            }
            set
            {
                SetValue(ItemPanelMaxHeightProperty, value);
            }
        }
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register(
                "ItemsSource",
                typeof(IEnumerable),
                typeof(AutoCompleteComboBox)
                );
        public IEnumerable ItemsSource
        {
            get
            {
                return (IEnumerable)ItemsSource;
            }
            set
            {
                SetValue(ItemsSourceProperty, value);
            }
        }
        public static readonly DependencyProperty DisplayMemberPathProperty =
            DependencyProperty.Register(
                "DisplayMemberPath",
                typeof(string),
                typeof(AutoCompleteComboBox)
                );
        public string DisplayMemberPath
        {
            get
            {
                return GetValue(DisplayMemberPathProperty).ToString();
            }
            set
            {
                SetValue(DisplayMemberPathProperty, value);
            }
        }
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register(
                "Text",
                typeof(string),
                typeof(AutoCompleteComboBox),
                new FrameworkPropertyMetadata(
                    "",
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
                    )
                );
        public string Text
        {
            get
            {
                return GetValue(TextProperty).ToString();
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }
        public string TargetValue { get; set; } = "";
        public static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register(
                "IsDropDownOpen",
                typeof(bool),
                typeof(AutoCompleteComboBox),
                new FrameworkPropertyMetadata(
                    false,
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
                    )
                );
        public bool IsDropDownOpen
        {
            get
            {
                return (bool)GetValue(IsDropDownOpenProperty);
            }
            set
            {
                SetValue(IsDropDownOpenProperty, value);
            }
        }
        #endregion
        #region Events
        private void me_Loaded(object sender, RoutedEventArgs e)
        {
            w = VisualTreeHelpers.FindAncestor<Window>(this);
            w.MouseLeftButtonDown += Window_MouseLeftDown;
            FocusTextBox();
        }
        private void Window_MouseLeftDown(object sender, MouseButtonEventArgs e)
        {
            IsDropDownOpen = false;
        }
        private void lst_KeyDown(object sender, KeyEventArgs e)
        {
        }
        private void lst_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (TargetValue != null && TargetValue.Trim().Length > 0)
            {
                txt.Text = TargetValue;
                IsDropDownOpen = false;
            }
            FocusTextBox();
        }
        private void lst_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
        }
        private void lst_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lst.SelectedItem != null)
            {
                TargetValue = lst.SelectedItem.ToString();
            }
        }
        private void txt_LostFocus(object sender, RoutedEventArgs e)
        {
            if (lst.IsFocused == false)
            {
                IsDropDownOpen = false;
                FocusTextBox();
            }
        }
        private void lst_LostFocus(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("text changed");
            if (txt.IsFocused == false)
            {
                IsDropDownOpen = false;
            }
        }
        private void txt_TextChanged(object sender, TextChangedEventArgs e)
        {
            IsDropDownOpen = true;
        }
        private void txt_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (IsDropDownOpen && lst.Items.Count > 0)
            {
                if (lst.SelectedIndex < 0)
                {
                    lst.SelectedIndex = 0;
                }
                if (e.Key == Key.Up && lst.SelectedIndex > 0)
                {
                    lst.SelectedIndex--;
                }
                else if (e.Key == Key.Down && lst.SelectedIndex < lst.Items.Count - 1)
                {
                    lst.SelectedIndex++;
                }
                else if(e.Key == Key.Enter || e.Key == Key.Tab)
                {
                    if(lst.SelectedIndex > -1)
                    {
                        txt.Text = TargetValue;
                        IsDropDownOpen = false;
                        FocusTextBox();
                    }
                }
            }
        }
        #endregion
    }

这是XAML

<UserControl x:Class="SHARED_COMPONENTS.AutoCompleteComboBox"
             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" 
             x:Name="me"
             Loaded="me_Loaded"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBox 
                x:Name="txt" 
                Background="CornflowerBlue"
                Foreground="White"
                Grid.Row="0" 
                Text="{Binding ElementName=me, Path=Text,UpdateSourceTrigger=PropertyChanged}" 
                TextChanged="txt_TextChanged" PreviewKeyDown="txt_PreviewKeyDown" 
                Height="{Binding ElementName=me, Path=ActualHeight}"
                Padding="{Binding ElementName=me,Path=InputPadding}"
                />
            <Popup IsOpen="{Binding ElementName=me, Path=IsDropDownOpen}" ClipToBounds="False">
                <Border Grid.Row="1">
                    <Border.Effect>
                        <DropShadowEffect Color="Black" />
                    </Border.Effect>
                    <ListBox 
                            x:Name="lst" 
                            Grid.Row="1" 
                            ItemsSource="{Binding ElementName=me, Path=ItemsSource}" 
                            PreviewKeyDown="lst_KeyDown" 
                            SelectionChanged="lst_SelectionChanged" 
                            PreviewMouseLeftButtonDown="lst_MouseLeftButtonDown" 
                            PreviewMouseLeftButtonUp="lst_PreviewMouseLeftButtonUp" 
                            DisplayMemberPath="{Binding ElementName=me, Path=DisplayMemberPath }"
                            ClipToBounds="False"
                        >
                        <ListBox.Style>
                            <Style TargetType="ListBox">
                                <Setter Property="Background" Value="#f0f0f0" />
                                <Setter Property="Visibility" Value="Collapsed" />
                                <Style.Triggers>
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding ElementName=lst, Path=HasItems}" Value="True" />
                                            <Condition Binding="{Binding ElementName=me, Path=IsDropDownOpen}" Value="True" />
                                        </MultiDataTrigger.Conditions>
                                        <Setter Property="Visibility" Value="Visible" />
                                    </MultiDataTrigger>
                                    <DataTrigger Binding="{Binding ElementName=me, Path=IsDropDownOpen}" Value="False">
                                        <Setter Property="Visibility" Value="Collapsed" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </ListBox.Style>
                        <ListBox.Resources>
                            <Style TargetType="ListBoxItem">
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True">
                                        <Setter Property="IsSelected" Value="True" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}" Value="True">
                                        <Setter Property="Foreground" Value="CornflowerBlue" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </ListBox.Resources>
                    </ListBox>
                </Border>
            </Popup>
        </Grid>
    </Grid>
</UserControl>