如果我正确理解了这个问题,您有一个编辑字段(TextBox),您想用它来显示和编辑在ListBox 中选择的数据对象的Name 属性。有多个这样的ListBox 对象,用户应该能够编辑在最近使用的ListBox 中选择的数据对象。
事实证明,这并不是一件微不足道的事情。我同意 Ben Cohen 的回复,其中一种方法是在焦点更改时显示和隐藏 不同 TextBox 实例。一种相关但不同的方法是只有一个 TextBox 并根据焦点的变化更新其 Binding 属性。
但它比这更复杂,因为当用户在ListBox 中选择一个项目时,ListBox 本身并不是获得焦点的对象。这是ListBox 中的ListBoxItem。似乎没有一种方便的方法可以在 XAML 中访问所选的 ListBoxItem。但即使你可以,还有另一个问题……
此外,您不能简单地将每个TextBox 的可见性绑定到焦点状态(或合适的代理),因为一旦用户实际单击TextBox,ListBox/its代理失去焦点。
无论如何,在稍微解决了这个问题(坦率地说,可能比我应该做的要多,但是嘿……我在这里也学到了一些新东西:)),我想出了一个代码示例,我认为它确实可以你在这里要求(我希望这反过来是你想要的:)):
XAML:
<Window x:Class="TestSO30283586BindOnFocus.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:local="clr-namespace:TestSO30283586BindOnFocus"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="dataTemplate1" DataType="{x:Type local:A}">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<local:BoolToVisibilityConverter x:Key="boolToVisibilityConverter1"/>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource dataTemplate1}"/>
<Setter Property="Tag">
<Setter.Value>
<system:Boolean>False</system:Boolean>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ListBoxItem">
<EventSetter Event="GotFocus" Handler="listBoxItem_Focus"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="FontSize" Value="16"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox1"
Grid.Row="0">
<ListBox.Items>
<local:A Name="John"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox2"
Grid.Row="1">
<ListBox.Items>
<local:A Name="Jacob"/>
</ListBox.Items>
</ListBox>
<ListBox x:Name="listBox3"
Grid.Row="2">
<ListBox.Items>
<local:A Name="Jingleheimer"/>
</ListBox.Items>
</ListBox>
<TextBox Text="{Binding ElementName=listBox1, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox1, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox2, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox2, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
<TextBox Text="{Binding ElementName=listBox3, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ElementName=listBox3, Path=Tag, Converter={StaticResource boolToVisibilityConverter1}}"/>
</Grid>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void listBoxItem_Focus(object sender, RoutedEventArgs e)
{
ListBox[] listBoxes = { listBox1, listBox2, listBox3 };
ListBoxItem item = (ListBoxItem)sender;
ListBox listBoxParent = GetParent<ListBox>(item);
foreach (ListBox listBox in listBoxes)
{
listBox.Tag = object.ReferenceEquals(listBoxParent, listBox);
}
}
private static T GetParent<T>(DependencyObject o) where T : DependencyObject
{
while (o != null && !(o is T))
{
o = VisualTreeHelper.GetParent(o);
}
return (T)o;
}
}
class A : DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
"Name", typeof(string), typeof(A));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool))
{
return Binding.DoNothing;
}
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
有一些代码隐藏来处理我无法在标记中解决的问题。特别是,代码隐藏包括一个事件处理程序,只要ListBoxItem 获得焦点,就会触发该事件处理程序。发生这种情况时,事件处理程序会更新每个ListBox 的Tag 属性,从而将包含ListBoxItem 的属性设置为true,而将其他两个设置为false。
通过这种方式,我使用Tag 属性作为ListBoxItem 的IsFocused 属性的代理,允许我的TextBox 元素将其Visibility 属性绑定到该属性。请注意,我只在收到焦点时设置Tag。这样,即使在用户选择 TextBox 元素时失去焦点,可见性仍然保持设置。
当你运行这个程序时,每个ListBox 都被初始化为只有一个项目。当您选择一个项目时,它会显示在右侧的TextBox 中。随着焦点在三个ListBox 元素及其项目之间的变化,不同的TextBox 元素会显示和隐藏,但对用户来说,它看起来就像一个TextBox 正在更新以反映当前的选择。
如果您编辑TextBox 中的文本,则链接项目的Name 属性会与您一样更新。这种变化当然体现在ListBox的显示上。
我不能向你保证这是最好的方法。但它确实有效。我最大的抱怨是劫持了Tag 财产。但事实是,只要您不将该属性用于其他任何用途,就可以将其用于此目的。
我实际上希望一些真正的 WPF 专家会出现并告诉我我是一个假人,并且有更好的方法来实现这一点。但与此同时,我希望以上内容能够充分满足您的需求。 :)
我在上面提到过,另一种方法是为单个 TextBox 元素更新 Binding。我没有显示代码,但它不会有太大的不同。当然,您只有一个 TextBox,而 GotFocus 事件处理程序不会与 ListBox.Tag 属性混淆,而是会检索 ListBoxItem 发送事件的实际数据对象,并配置一个新的Binding 对象对应于单个 TextBox 对象。
这种方法将避免使用 Tag 属性,甚至可能会稍微更有效(或者可能不会……它可以采用任何一种方式)。我个人发现操作绑定的代码比上面只设置属性值的代码要复杂一些。但是由于需要沿着可视化树查找父级ListBox,因此代码变得复杂。所以也许是洗了。
除了这些差异之外,代码的结构仍然大致相同。