TreeView автоматически выбирает родителя после того, как пользователь выбирает дочерний
В моем окне у меня есть TreeView и TextBox. Представьте, что TextBox используется для написания собственного скрипта, а TreeView - это способ выбора функции для вставки; думаю, редактор сценариев Crystal Report.
Моя цель - чтобы пользователь щелкнул по одному из дочерних элементов TreeView, и этот дочерний элемент вставляется в TextBox. Дочерний объект является сигнатурой функции и находится в родительском узле. Затем пользователь может перейти к TextBox, выбрать один из параметров функции и заменить его другой сигнатурой функции. Для этого я обрабатываю событие SelectedItemChanged TreeView, устанавливаю SelectedText TextBox, а затем пытаюсь выделить текст после его изменения.
SelectedText TextBox правильно поменяется местами. Однако текст не выделяется, а полоса прокрутки не прокручивается до выделенного текста.
Вот мой XAML из тестового проекта, который я написал для воспроизведения поведения:
<Window x:Class="SelectedTextWeirdness.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:SelectedTextWeirdness="clr-namespace:SelectedTextWeirdness" Title="MainWindow" Width="600" Height="600"
x:Name="Me">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TreeView Grid.Row="0" x:Name="treeView" ItemsSource="{Binding ElementName=Me, Path=TreeViewItems, Mode=TwoWay}"
SelectedItemChanged="treeView_SelectedItemChanged" Margin="10">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type SelectedTextWeirdness:Parent}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type SelectedTextWeirdness:Child}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
<TextBox Grid.Row="1" x:Name="scriptTextBox" Margin="10" Height="200" Width="Auto" FontFamily="Consolas, Courier New"
HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Auto"
MaxLines="9999" AcceptsReturn="True" AcceptsTab="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Text="{Binding Path=Script, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
</Grid>
</Window>
А вот код позади:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SelectedTextWeirdness
{
public class Child
{
public string Name
{
get;
set;
}
}
public class Parent
{
public string Name
{
get;
set;
}
public List<Child> Children
{
get;
set;
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public List<Parent> TreeViewItems
{
get;
set;
}
public MainWindow()
{
BuildTreeViewItems();
InitializeComponent();
}
private void BuildTreeViewItems()
{
TreeViewItems = new List<Parent>()
{
new Parent()
{
Name = "Parent1",
Children =
new List<Child>()
{
new Child() {Name = "ReallyLongFunctionNameNumber1(ReallyLongLeft1, ReallyLongRight1)"},
new Child() {Name = "ReallyLongFunctionNameNumber2(ReallyLongLeft2, ReallyLongRight2)"},
new Child() {Name = "ReallyLongFunctionNameNumber3(ReallyLongLeft3, ReallyLongRight3)"},
new Child() {Name = "ReallyLongFunctionNameNumber4(ReallyLongLeft4, ReallyLongRight4)"},
new Child() {Name = "ReallyLongFunctionNameNumber5(ReallyLongLeft5, ReallyLongRight5)"}
}
},
new Parent()
{
Name = "Parent2",
Children =
new List<Child>()
{
new Child() {Name = "ReallyLongFunctionNameNumber1(ReallyLongLeft1, ReallyLongRight1)"},
new Child() {Name = "ReallyLongFunctionNameNumber2(ReallyLongLeft2, ReallyLongRight2)"},
new Child() {Name = "ReallyLongFunctionNameNumber3(ReallyLongLeft3, ReallyLongRight3)"},
new Child() {Name = "ReallyLongFunctionNameNumber4(ReallyLongLeft4, ReallyLongRight4)"},
new Child() {Name = "ReallyLongFunctionNameNumber5(ReallyLongLeft5, ReallyLongRight5)"}
}
}
};
}
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var tree = (TreeView)sender;
var selectedItem = tree.SelectedItem as Child;
if (selectedItem != null)
{
int selectionStart = scriptTextBox.SelectionStart;
string selectedText = selectedItem.Name;
scriptTextBox.SelectedText = selectedText;
scriptTextBox.Focus();
scriptTextBox.Select(selectionStart, selectedText.Length);
}
}
}
}
Я попытался установить SelectedItemChanged e.Handled = true. Это не сработало. Я попытался обработать LostFocus в TextBox и установить e.Handled = true, но это не сработало. Это только кажется, когда я использую HierarchicalDateTemplate. Если я изменю данные только на один уровень, эта настройка будет работать нормально.
Есть идеи?
1 ответ
Основная проблема заключается в том, чтобы иметь Focus()
изменить в обработчике событий. Отложите Фокус, позвонив в BeginInvoke
,
Что-то вроде:
delegate void voidDelegate();
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var tree = (TreeView)sender;
var selectedItem = tree.SelectedItem as Child;
if (selectedItem != null)
{
int selectionStart = scriptTextBox.SelectionStart;
string selectedText = selectedItem.Name;
voidDelegate giveFocusDelegate = new voidDelegate(giveFocus);
Dispatcher.BeginInvoke(giveFocusDelegate, new object[] { });
scriptTextBox.SelectedText = selectedText;
}
}
private void giveFocus()
{
scriptTextBox.Focus();
}
Должен приблизить вас к цели.
Изменить: Как мы знаем, что это будет работать?
В качестве документации дляDispatcher.BeginInvoke
говорит:
Операция добавляется в очередь событий Dispatcher по указанному DispatcherPriority.
Поэтому независимо от приоритета задачи, в которой вы вызываете beginInvoke, ближайший момент, когда вызов может произойти, наступает сразу после завершения текущей операции: операция beginInvoked "помещается" где-то в очередь диспетчера, которая работает на одном нить.