Вкладка WPF в элементы управления данными

В моем приложении WPF у меня есть привязка данных TextBox и привязка данных ItemsControl, Содержание ItemsControl определяется содержанием TextBox, Я хочу иметь возможность ввести значение в TextBoxнажмите вкладку и введите первый элемент в ItemsControl (создается из значения в TextBox). У меня проблема в том, что действие вкладки оценивается до того, как WPF создаст шаблонные элементы в ItemsControl, Следующий код демонстрирует эту проблему:

<Window x:Class="BindingExample.Window1" x:Name="SelfControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:loc="clr-namespace:BindingExample" Title="Window1" Height="300" Width="400">
    <Window.Resources>
        <DataTemplate DataType="{x:Type loc:ChildClass}">
            <TextBox Text="{Binding Value}" />
        </DataTemplate>
    </Window.Resources>
    <StackPanel DataContext="{Binding ElementName=SelfControl}" Focusable="False">
        <Label Content="Options: A, B, C" />
        <TextBox Text="{Binding Object.Value}" />
        <ItemsControl Margin="16,0,0,0" ItemsSource="{Binding Object.Children}" Focusable="False">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
        <TextBox Text="Box2" />
    </StackPanel>
</Window>

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace BindingExample
{
    public partial class Window1
    {
        public static readonly DependencyProperty ObjectProperty = DependencyProperty.Register("Object", typeof(ParentClass), typeof(Window1));
        public ParentClass Object
        {
            get { return GetValue(ObjectProperty) as ParentClass; }
            set { SetValue(ObjectProperty, value); }
        }

        public Window1()
        {
            InitializeComponent();
            Object = new ParentClass();
        }
    }

    public class ParentClass : INotifyPropertyChanged
    {
        private string value;
        public string Value 
        {
            get { return value; }
            set { this.value = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Children")); }
        }

        public IEnumerable<ChildClass> Children
        {
            get
            {
                switch (Value.ToUpper())
                {
                    case "A": return new ChildClass[] { "A-1", "A-2", "A-2" };
                    case "B": return new ChildClass[] { "B-1", "B-2", "B-3" };
                    case "C": return new ChildClass[] { "C-1", "C-2", "C-2" };
                    default: return new ChildClass[] { "Unknown" };
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class ChildClass
    {
        public string Value { get; set; }
        public static implicit operator ChildClass(string value) { return new ChildClass { Value = value }; }
    }
}

В этом коде я хотел бы ввести "А" в первый TextBoxнажмите вкладку и переключите фокус на ребенка TextBox с текстом "А-1". Вместо этого фокус переходит к TextBox с текстом "Box2". Как я могу добиться того поведения, которое ищу здесь?

Примечание. Как отметил Жюльен Пулин, эту работу можно выполнить, переключив UpdateSourceTrigger на TextBox в PropertyChanged, Это работает, однако, только если привязка "по мере ввода" является приемлемой. В моем случае я также хотел бы установить значение и вкладку одним нажатием клавиши. Есть ли способ заставить ItemsControl создавать свои шаблонные элементы по требованию?

2 ответа

Попробуйте установить UpdateMode из TextBox в PropertyChanged поэтому базовое значение устанавливается при вводе нового значения вместо того, когда TextBox теряет фокус:

<TextBox Text="{Binding Path=Object.Value, UpdateMode=PropertyChanged}" />

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

...
<TextBox Text="{Binding Object.Value}" KeyDown="TextBox_KeyDown" />
...

public partial class Window1
{
    private DispatcherTimer timer;

    ...

    private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Tab && e.KeyboardDevice.Modifiers != ModifierKeys.Shift)
        {
            e.Handled = true;
            var textbox = (TextBox)sender;
            textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            (timer = new DispatcherTimer(
                new TimeSpan(100000), // 10 ms
                DispatcherPriority.Normal,
                delegate
                {
                    textbox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                    timer.Stop();
                }, Dispatcher)).Start();
        }
    }
}

Единственная проблема, которую я вижу с этим кодом, состоит в том, что ItemsControl заполнение может занять более 10 мс, в этом случае Tab будет перепрыгивать через эти элементы. Есть ли способ определить, является ли создание ItemsControl предметы произошли?

Другие вопросы по тегам