在WPF应用程序开发中,经常会遇到需要动态设置控件焦点的场景。例如,当一个包含TextBox的UserControl出现时,如果有一个前景UserControl正在显示忙碌信息或者错误状态,那么常规的初始化或加载时设置焦点的方法就无法工作。为了解决这个问题,可以通过自定义控件和事件处理来实现动态焦点控制。
首先,定义了一个名为ActionableContentPresenter的自定义控件,它继承自ContentPresenter。通过重写ContentProperty的元数据,可以在Content发生变化时触发一个事件。这样,就可以在主窗口或UserControl中为这个事件绑定一个事件处理程序。
public class ActionableContentPresenter : ContentPresenter
{
static ActionableContentPresenter()
{
ContentProperty.OverrideMetadata(
typeof(ActionableContentPresenter),
new FrameworkPropertyMetadata(
new PropertyChangedCallback(OnContentChanged)));
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var mcc = d as ActionableContentPresenter;
mcc?.ContentChanged?.Invoke(mcc, new DependencyPropertyChangedEventArgs(ContentProperty, e.OldValue, e.NewValue));
}
public event DependencyPropertyChangedEventHandler ContentChanged;
}
在主窗口的构造函数中,为ActionableContentPresenter的ContentChanged事件绑定了一个事件处理程序。在这个事件处理程序中,检查ActionableContentPresenter的Content是否为null。如果是,并且DisableSetFocus选项没有被选中,那么就可以根据SetFocusTextBox2选项的值来决定焦点应该设置在第一个还是第二个TextBox上。
public MainWindow()
{
InitializeComponent();
ActionableContentPresenter.ContentChanged += ActionableContentPresenter_ContentChanged;
}
private void ActionableContentPresenter_ContentChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var mcc = sender as ActionableContentPresenter;
if (mcc.Content == null)
{
if (DisableSetFocus.IsChecked == false)
{
if (SetFocusTextBox2.IsChecked == false) TextBox1.Focus();
else TextBox2.Focus();
}
}
}
在示例中,有一个包含多个控件的主内容区域,包括两个CheckBox控件。一个CheckBox用于启用设置焦点,另一个用于改变焦点从第一个TextBox到第二个TextBox。当点击"Show Overlay"按钮时,会出现一个带有单个"Close"按钮的Panel,这个Panel会完全覆盖其他控件,使得只能点击"Close"按钮。当点击这个按钮时,覆盖的控件会消失。如果选中了"Disable Focus"CheckBox,那么表单上不会有任何控件获得焦点。如果没有选中,那么焦点会出现在第一个或第二个TextBox上,这取决于"Focus on TextBox 2 (otherwise TextBox 1)"CheckBox是否被选中。