将UserControl绑定到自定义BusyIndicator控件
在加载新视图时,我需要关注特定的文本框。
解决方案是将这行代码添加到视图的OnLoaded事件中:
Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); });
所以这适用于一种观点,而不是另一种观点。 我花了一些时间来调试问题并意识到我正在处理的新视图有一个BusyIndicator,它将焦点从所有控件上移开,因为BusyIndicator被设置为true而在OnLoaded事件之后出现了false。
因此解决方案是在将NameTextBox
设置为false 后将焦点调用到NameTextBox
。 我的想法是创建一个可重用的BusyIndicator控件来处理这个额外的工作。 但是,我在MVVM中无法做到这一点。
我首先对工具包进行了简单的扩展:BusyIndicator:
public class EnhancedBusyIndicator : BusyIndicator { public UserControl ControlToFocusOn { get; set; } private bool _remoteFocusIsEnabled = false; public bool RemoteFocusIsEnabled { get { return _remoteFocusIsEnabled; } set { if (value == true) EnableRemoteFocus(); } } private void EnableRemoteFocus() { if (ControlToFocusOn.IsNotNull()) Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); }); else throw new InvalidOperationException("ControlToFocusOn has not been set."); }
我将控件添加到我的XAML文件中没有问题:
... ...
因此,我的想法是在我的ViewModel IsRemoteFocusEnabled
设置为true(我在ViewModel中将IsBusy
设置为false后执行此操作),焦点将设置为NameTextBox
。 如果它有效,其他人可以使用EnhancedBusyIndicator
并绑定到不同的控件,并在他们自己的ViewModel中适当地启用焦点,假设他们的视图有一个初始的BusyIndicator
活动。
但是,加载视图时出现此exception:
设置属性’foo.Controls.EnhancedBusyIndicator.ControlToFocusOn’抛出exception。 [线路:45位置:26]
这个解决方案我试图工作吗? 如果是这样,到目前为止我有什么问题(无法设置ControlToFocusOn
属性)?
更新1
我安装了适用于Silverlight 5的Visual Studio 10工具,并在导航到新视图时收到了更好的错误消息。 现在我发出此错误消息:
“System.ArgumentException:System.Windows.Data.Binding类型的对象无法转换为System.Windows.Controls.UserControl类型”
此外,我认为我需要更改此控件的DataContext。 在代码隐藏构造函数中,DataContext设置为我的ViewModel。 我尝试将一个DataContext属性添加到EnhancedBusyIndicator
,但这不起作用:
更新2
我需要将UserControl
更改为Control
因为我希望将焦点设置为TextBox
对象(实现Control
)。 但是,这并没有解决问题。
@Matt,不确定
DataContext="{Binding RelativeSource={RelativeSource Self}}"
将在Silverlight 5中工作,您是否尝试将其绑定为静态资源?
如果视图中没有BusyIndicator
,解决焦点问题的常用解决方案是添加代码
Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
到视图的Loaded
事件。 即使存在BusyIndicator
,这实际上也可以工作; 但是, BusyIndicator
立即将焦点从其余的Silverlight控件上移开。 解决方案是在BusyIndicator
不忙后调用控件的Focus()
方法。
我能够通过制作这样的控件来解决它:
public class EnhancedBusyIndicator : BusyIndicator { public EnhancedBusyIndicator() { Loaded += new RoutedEventHandler(EnhancedBusyIndicator_Loaded); } void EnhancedBusyIndicator_Loaded(object sender, RoutedEventArgs e) { AllowedToFocus = true; } private readonly DependencyProperty AllowedToFocusProperty = DependencyProperty.Register("AllowedToFocus", typeof(bool), typeof(EnhancedBusyIndicator), new PropertyMetadata(true)); public bool AllowedToFocus { get { return (bool)GetValue(AllowedToFocusProperty); } set { SetValue(AllowedToFocusProperty, value); } } public readonly DependencyProperty ControlToFocusOnProperty = DependencyProperty.Register("ControlToFocusOn", typeof(Control), typeof(EnhancedBusyIndicator), null); public Control ControlToFocusOn { get { return (Control)GetValue(ControlToFocusOnProperty); } set { SetValue(ControlToFocusOnProperty, value); } } protected override void OnIsBusyChanged(DependencyPropertyChangedEventArgs e) { base.OnIsBusyChanged(e); if (AllowedToFocus && !IsBusy) { Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); }); AllowedToFocus = false; } } }
要使用它,请使用新的EnhancedBusyIndicator
替换xaml中的BusyIndicator
标记,并添加相应的命名空间。
在元素内添加一个新属性ControlToFocusOn
,并将其绑定到视图中的现有元素,在EnhancedBusyIndicator
消失后,您希望焦点处于打开状态:
...
在这种情况下,我专注于一个名为NameTextBox
的文本框。
而已。 每次我们导航到页面时,此控件都会聚焦。 当我们在页面上时,如果EnhancedBusyIndicator
变得忙碌并且不忙于agiain,焦点将不会转到控件; 这只发生在初始加载时。
如果要允许EnhancedBusyIndicator
再次聚焦到ControlToFocusOn
,请添加另一个属性AllowedToFocus
:
...
当AllowedToFocus
设置为true时,下次EnhancedBusyIndicator
从忙碌切换到不忙时,焦点将转到ControlToFocusOn
。
加载视图时,AllowedToFocus也可以设置为false,以防止焦点转到控件。 如果将AllowedToFocus
绑定到ViewModel属性,则可能需要更改BindingMode
。 默认情况下,它是OneTime
。