Как предотвратить вызов конструктора при изменении значения - PullRequest
0 голосов
/ 15 октября 2019

У меня есть пользовательский контроль, который я использую в DataTemplate. В этом пользовательском контроле у ​​меня есть текстовое поле. Раньше, когда я реализовывал этот пользовательский контроль, у меня не было проблем, но теперь, когда я поместил его в шаблон данных, кажется, что что-то пошло не так.

Каждый раз, когда я что-то печатаю в текстовое поле, код выполняется нормально, но в конце моих триггеров конструктор usercontrols снова вызывается и очищает мое текстовое поле. (Скрытые потоки называют это, так что я не знаю, где вообще начать искать, откуда исходит этот неинтуитивный вызов)

Я пытаюсь выяснить, что вызывает этот конструктор снова. Другие привязки, кажется, работают хорошо, и правильная информация заполняется и отображается. Это просто тот конструктор, который вызывается снова после того, как все разрешит и очистит внутренние переменные элемента управления пользовательского интерфейса.

Текущее исполнение:

Я печатаю в текстовое поле. Триггеры получают мое значение текстового поля, фильтруют списки соответственно, и затем вызывается конструктор, и текстовое поле сбрасывается к значению по умолчанию "".

Желаемое выполнение:

Я печатаю в текстовое поле. Триггеры получают мое значение текстового поля, фильтруют списки соответственно.

<UserControl x:Class="Analytics_Module.Views.TenantProfileFilterFieldsView"
             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:Analytics_Module.Views"
             xmlns:vm="clr-namespace:Analytics_Module.ViewModels"
             xmlns:uiComponents="clr-namespace:Analytics_Module.UI_Components"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <DockPanel>
        <DockPanel.DataContext>
            <vm:TenantProfileFilterFieldsViewModel  x:Name="test"/>
        </DockPanel.DataContext>

....

            <ScrollViewer HorizontalScrollBarVisibility="Auto">
                <ItemsControl ItemsSource="{Binding FiltersState.GroupedTenantNames, Mode=TwoWay}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                                <uiComponents:neoCombobox 
                                LabelText="Tenant Names"
                                ListBoxItems="{Binding StaticLists.TenantNames, ElementName=test}"
                                DisplayListBoxItems ="{Binding}"
                                />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

ПОЛЬЗОВАТЕЛЬСКИЙ КОНТРОЛЬ ПОЛЬЗОВАТЕЛЯ

<UserControl x:Class="Analytics_Module.UI_Components.neoCombobox"
             x:Name="parent"
             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:model="clr-namespace:Analytics_Module.Models"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel DataContext="{Binding ElementName=parent}" Width="200">

        <Label Name="ComboboxLabel"
                Content="{Binding Path=LabelText, FallbackValue='Error'}" Margin="5"/>
        <TextBox Name="InputField"
                     Text="{Binding Path=TextBoxValue, Mode=TwoWay, FallbackValue='Error', UpdateSourceTrigger='PropertyChanged'}"/>

        <!--TODO rename -->
        <ListBox Name="Something"
                ItemsSource="{Binding Path=DisplayListBoxItems, FallbackValue={}, Mode=TwoWay}"  >
            <ListBox.ItemTemplate >
                <DataTemplate >
                    <StackPanel>
                        <CheckBox Margin="-1"
                                  Content="{Binding Name, FallbackValue='Error'}" 
                                  IsChecked="{Binding Check_Status, Mode=TwoWay, FallbackValue=true}">
                        </CheckBox>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</UserControl>

Back-end пользовательского контроллера

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Analytics_Module.Models;
using Analytics_Module.Utillity;
using System.Timers;
using System.Collections.Specialized;

namespace Analytics_Module.UI_Components
{
    /// <summary>
/// Interaction logic for neoCombobox.xaml
/// </summary>
    public partial class neoCombobox : UserControl
    {
        #region LabelText DP
        public String LabelText
        {
            get { return (String)GetValue(LabelTextProperty); }
            set { SetValue(LabelTextProperty, value); }
        }
        public static readonly DependencyProperty LabelTextProperty =
            DependencyProperty.Register("LabelText",
                typeof(string),
                typeof(neoCombobox), new PropertyMetadata("")
            );

        #endregion


        #region TextBoxValue DP

        /// <summary>
        /// Gets or sets the Value which is being displayed
        /// </summary>
        public String TextBoxValue
        {
            get { return (String)GetValue(TextBoxValueProperty); }
            set { SetValue(TextBoxValueProperty, value); }
        }
        /// <summary>
        /// Identified the TextBoxValue dependency property
        /// </summary>
        public static readonly DependencyProperty TextBoxValueProperty =
            DependencyProperty.Register("TextBoxValue",
                typeof(String),
                typeof(neoCombobox),
                new PropertyMetadata("")
            );

        #endregion


        #region ListBoxItems DP
        public ItemsChangeObservableCollection<MultiSelectDropDownListEntry> ListBoxItems
        {
            get { return (ItemsChangeObservableCollection<MultiSelectDropDownListEntry>)GetValue(ListBoxItemsProperty); }
            set { SetValue(ListBoxItemsProperty, value); }
        }
        public static readonly DependencyProperty ListBoxItemsProperty =
            DependencyProperty.Register("ListBoxItems",
                typeof(ItemsChangeObservableCollection<MultiSelectDropDownListEntry>),
                typeof(neoCombobox),
                new PropertyMetadata(new ItemsChangeObservableCollection<MultiSelectDropDownListEntry>())
            );

        #endregion

        #region DisplayListBoxItems DP
        public ItemsChangeObservableCollection<MultiSelectDropDownListEntry> DisplayListBoxItems
        {
            get {
                if (GetValue(DisplayListBoxItemsProperty) == null)
                {
                    SetValue(DisplayListBoxItemsProperty, new ItemsChangeObservableCollection<MultiSelectDropDownListEntry>());
                }
                return (ItemsChangeObservableCollection<MultiSelectDropDownListEntry>)GetValue(DisplayListBoxItemsProperty);
            }
            set { SetValue(DisplayListBoxItemsProperty, value); }
        }
        public static readonly DependencyProperty DisplayListBoxItemsProperty =
            DependencyProperty.Register("DisplayListBoxItems",
                typeof(ItemsChangeObservableCollection<MultiSelectDropDownListEntry>),
                typeof(neoCombobox),
                new PropertyMetadata(new ItemsChangeObservableCollection<MultiSelectDropDownListEntry>())
            );

        #endregion

        /// <summary>
        /// _timer is used to determine if a user has stopped typing. 
        /// The timer is started when a user starts typing again or 
        /// types for the first time. 
        /// </summary>
        private readonly Timer _timerKeyPress;

        /// <summary>
        /// _timer is used to determine if a user has left the  typing. 
        /// The timer is started when a user starts typing again or 
        /// types for the first time. 
        /// </summary>
        private readonly Timer _timerMouseLeave;

        public neoCombobox()
        {
            if (TextBoxValue != "") return;

            InitializeComponent();
            _timerKeyPress = new Timer();
            _timerKeyPress.Interval = 750;
            _timerKeyPress.Elapsed += new ElapsedEventHandler(UserPausedTyping);
            _timerKeyPress.AutoReset = false;


            _timerMouseLeave = new Timer();
            _timerMouseLeave.Interval = 550;
            //_timerMouseLeave.Elapsed += new ElapsedEventHandler(UserLeft);
            _timerMouseLeave.AutoReset = false;



        }

        //TODO Add property to determine if user preferes Mouse Leave of focus leave. 
        protected override void OnPreviewGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            Console.WriteLine("@@@@@@@@@@@@@@@OnPreviewGotKeyboardFocus");
            _timerMouseLeave.Stop();
            base.OnPreviewGotKeyboardFocus(e);
        }

        protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            Console.WriteLine("------------OnPreviewLostKeyboardFocus");
            _timerMouseLeave.Stop();
            _timerMouseLeave.Start();
            base.OnPreviewLostKeyboardFocus(e);
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            _timerMouseLeave.Stop();
            base.OnMouseEnter(e);
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            _timerMouseLeave.Stop();
            _timerMouseLeave.Start();
            base.OnMouseLeave(e);

        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            _timerKeyPress.Stop();
            _timerKeyPress.Start();
        }

        private void UserPausedTyping(object source, ElapsedEventArgs e)
        {
            this.Dispatcher.Invoke(() =>
            {
                Console.WriteLine("@@@@@@@@@@@@@@@UserPausedTyping");
                this.RefreshDisplayList();
            });
        }

        private void UserLeft(object source, ElapsedEventArgs e)
        {
            this.Dispatcher.Invoke(() =>
            {
                Console.WriteLine("@@@@@@@@@@@@@@@User Left");
                this.TextBoxValue = "";
                this.RefreshDisplayList();
            });
        }

        protected void RefreshDisplayList()
        {
            int ItemsourceCount = 0;
            foreach (MultiSelectDropDownListEntry entry in this.DisplayListBoxItems.ToList())
            {
                if (!entry.Check_Status) this.DisplayListBoxItems.Remove(entry);
            }

            if (this.TextBoxValue == "") return;

            foreach (MultiSelectDropDownListEntry entry in this.ListBoxItems)
            {
                if (entry.Name.ToString().ToLower().Contains(this.TextBoxValue.ToLower()) && !this.DisplayListBoxItems.Contains(entry))
                {
                    this.DisplayListBoxItems.Add(entry);
                    if (ItemsourceCount++ > 15) break;
                }
            }
        }
    }
}

1 Ответ

0 голосов
/ 15 октября 2019

Вы не всегда можете избежать воссоздания контейнеров с помощью ItemsControl, но вы можете сделать ваши текстовые данные постоянными, привязав свойство Text вашего TextBox к свойству модели представления а не к свойству самого пользовательского элемента управления.

Вместо записи:

<TextBox Name="InputField" Text="{Binding Path=TextBoxValue, Mode=TwoWay, FallbackValue='Error', UpdateSourceTrigger='PropertyChanged'}"/>

возможно напишите

<TextBox Name="InputField" Text="{Binding MyTextProperty, Mode=TwoWay, FallbackValue='Error', UpdateSourceTrigger='PropertyChanged'}"/>

и укажите MyTextProperty, определенный в вашемрассмотрите модель следующим образом:

public class GroupedTenantNameViewModel {
    public string MyTextProperty { get; set; }
}

и сделайте свою коллекцию FiltersState.GroupedTenantNames коллекцией GroupedTenantNameViewModel предметов. Эта коллекция будет постоянной, даже если ItemsControl заново сгенерирует все элементы, а привязка позаботится о том, чтобы вернуть данные на место.

Если вы вообще не используете шаблон MVVM, тоЯ предлагаю вам немного изучить его, так как он предназначен для правильной работы с привязками!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...