使用WPF我正在尝试生成一个TextBlock,双击后,“转换”成一个准备编辑的TextBox。之后,Enter键,Esc键或失去焦点会导致编辑结束,TextBox将恢复为TextBlock。
我找到的解决方案主要是工作。我的问题是我无法将TextBox聚焦在“转换”上,因此迫使用户在元素上再次显式单击以聚焦它并开始编辑。
我选择实现此行为的方式是使用模板和样式的DataTriggers来更改元素的模板。在我展示的示例中,元素是一个简单的ContentControl,虽然我正在尝试这样做的实际用例略微复杂(我有一个ListBox,其中每个元素都可以通过此行为进行编辑,一次一个)。
这个想法如下:
虽然不是最优的(视图和模型行为有某种混合 - 特别是在InEditing属性中 - 我不喜欢通过各种处理程序实质上重新实现对TextBox模型的更改的提交逻辑KeyDown和LostFocus),系统实际上没有问题。
首先,我想到了连接到TextBox的IsVisibleChanged事件,并将焦点设置在那里。不能这样做,因为前面提到的无法在Style中的Target标签上指定事件。请改用EventSetter。
错误建议的解决方案无法使用,因为此类事件不是路由事件,因此无法在EventSetter中使用。
代码分为四个文件。
using System.Windows;
namespace LeFocus
{
public class Model: DependencyObject
{
public bool InEditing
{
get { return (bool)GetValue(InEditingProperty); }
set { SetValue(InEditingProperty, value); }
}
// Using a DependencyProperty as the backing store for InEditing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty InEditingProperty =
DependencyProperty.Register("InEditing", typeof(bool), typeof(Model), new UIPropertyMetadata(false));
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
// Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Model), new UIPropertyMetadata("Hello!"));
}
}
<Application x:Class="LeFocus.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lefocus="clr-namespace:LeFocus"
StartupUri="MainWindow.xaml">
<Application.Resources>
<lefocus:Model x:Key="Model"/>
</Application.Resources>
</Application>
<Window x:Class="LeFocus.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lefocus="clr-namespace:LeFocus"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Source={StaticResource Model}}"
Name="mainWindow">
<Window.Resources>
<Style x:Key="SwitchingStyle"
TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type lefocus:Model}">
<TextBlock Text="{Binding Path=Name}">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="lefocus:MainWindow.EditName"
CommandParameter="{Binding}"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<EventSetter Event="TextBox.KeyDown" Handler="TextBox_KeyDown"/>
<EventSetter Event="TextBox.LostFocus" Handler="TextBox_LostFocus"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=InEditing}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type lefocus:Model}">
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=lefocus:MainWindow, AncestorLevel=1}, Path=NameInEditing, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged" KeyDown="TextBox_KeyDown_1" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="lefocus:MainWindow.EditName" Executed="setInEditing"/>
</Window.CommandBindings>
<Grid>
<ContentControl Style="{StaticResource SwitchingStyle}" Content="{Binding}"/>
</Grid>
</Window>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace LeFocus
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string NameInEditing
{
get { return (string)GetValue(NameInEditingProperty); }
set { SetValue(NameInEditingProperty, value); }
}
// Using a DependencyProperty as the backing store for NameInEditing. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NameInEditingProperty =
DependencyProperty.Register("NameInEditing", typeof(string), typeof(MainWindow), new UIPropertyMetadata(null));
public static readonly RoutedUICommand EditName =
new RoutedUICommand("EditName", "EditName", typeof(MainWindow));
private void setInEditing(object sender, ExecutedRoutedEventArgs e)
{
var model = ((Model)e.Parameter);
NameInEditing = model.Name;
model.InEditing = true;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var model = getModelFromSender(sender);
model.Name = NameInEditing;
NameInEditing = null;
model.InEditing = false;
}
else if (e.Key == Key.Escape)
{
var model = getModelFromSender(sender);
model.InEditing = false;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var model = getModelFromSender(sender);
model.Name = NameInEditing;
NameInEditing = null;
model.InEditing = false;
}
private static Model getModelFromSender(object sender)
{
var contentControl = (ContentControl)sender;
var model = (Model)contentControl.DataContext;
return model;
}
}
}
答案 0 :(得分:0)
可以使用此设置的一种方法是在Loaded
上处理TextBox
,然后在Keyboard.Focus
上处理sender
。
答案 1 :(得分:0)
我认为Change listbox template on lost focus event in WPF中的代码已经做了你想要的。这是一个隐藏列表框选择矩形的修改,以便行为更明显。选择listboxitem(单击一下)后,文本框边框变为可见,但只有当鼠标悬停在它上面时,它才可编辑(尝试输入)。当您再次单击它,或者双击以在第一个位置选择项目时,可以对其进行编辑。
<Page.Resources>
<ResourceDictionary>
<Style x:Key="NullSelectionStyle" TargetType="ListBoxItem">
<Style.Resources>
<!-- SelectedItem with focus -->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<!-- SelectedItem without focus -->
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
<!-- SelectedItem text foreground -->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="{DynamicResource {x:Static SystemColors.ControlTextColorKey}}" />
</Style.Resources>
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
</Style>
<Style x:Key="ListBoxSelectableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="IsHitTestVisible" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}" Value="True">
<Setter Property="IsHitTestVisible" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Page.Resources>
<Grid>
<ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Margin="5" Style="{StaticResource ListBoxSelectableTextBox}" Text="{Binding Name}" BorderBrush="{x:Null}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>