1

在我的 WPF 应用程序中,我有一个 databoundTextBox和一个 databound ItemsControl。的内容ItemsControl由 的内容决定TextBox。我希望能够在 中输入一个值TextBox,按 Tab 并输入第一项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 }; }
    }
}

在这段代码中,我想在第一个中键入“A” TextBox,按 Tab,然后将焦点转移到TextBox带有文本“A-1”的孩子身上。相反,焦点会跳到带有文本“Box2”的文本框。我怎样才能实现我在这里寻找的行为?

注意:正如 Julien Poulin 指出的那样,可以通过将 切换到 来完成UpdateSourceTrigger这项TextBox工作PropertyChanged。但是,这仅在“as you type”绑定是可接受的情况下才有效。就我而言,我还想一键设置值和选项卡。有什么方法可以强制 ItemsControl 按需创建其模板项目?

4

2 回答 2

1

尝试将 的 设置为UpdateModeTextBox以便PropertyChanged在您键入新值时设置基础值,而不是在TextBox失去焦点时设置:

<TextBox Text="{Binding Path=Object.Value, UpdateMode=PropertyChanged}" />
于 2009-07-20T22:41:31.073 回答
0

这是一个替代解决方案。这有点骇人听闻,但似乎可以工作。由于在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项目的创建是否已经发生?

于 2009-07-23T16:26:11.973 回答