将UserControl绑定到自定义BusyIndi​​cator控件

时间:2012-01-13 18:51:51

标签: c# silverlight-4.0 mvvm busyindicator

我需要在加载新视图时专注于特定的文本框。

解决方案是将这行代码添加到视图的OnLoaded事件中:

Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); });

所以这适用于一种观点,但不适用于另一种观点。我花了一些时间调试问题,并意识到我正在处理的新视图有一个BusyIndi​​cator,它将焦点从所有控件上移开,因为BusyIndi​​cator被设置为true而在OnLoaded事件之后出现了错误。

所以解决方法是在我的BusyIndi​​cator设置为false之后将焦点调用到NameTextBox 。我的想法是创建一个可重用的BusyIndi​​cator控件来处理这个额外的工作。但是,我在MVVM中无法做到这一点。

我首先对工具包进行了简单的扩展:BusyIndi​​cator:

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文件中没有问题:

<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
    IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>    
...
    <my:myTextBox (this extends TextBox)
        x:Name="NameTextBox"
    ...
    />
...
</my:EnhancedBusyIndicator>

所以我的想法是在我的ViewModel中将IsRemoteFocusEnabled设置为true(我在ViewModel中将IsBusy设置为false后执行此操作),焦点将设置为{{1} }。如果它有效,其他人可以使用NameTextBox并绑定到不同的控件并在他们自己的ViewModel中适当地启用焦点,假设他们的视图具有初始EnhancedBusyIndicator活动。

但是,加载视图时出现此异常:

设置属性'foo.Controls.EnhancedBusyIndi​​cator.ControlToFocusOn'抛出异常。 [行:45位置:26]

这个解决方案我试图工作吗?如果是这样,到目前为止我有什么问题(不能设置BusyIndicator属性)?


更新1

我为Silverlight 5安装了Visual Studio 10工具,并在导航到新视图时收到了更好的错误消息。现在我发出此错误消息:

“System.ArgumentException:System.Windows.Data.Binding类型的对象无法转换为System.Windows.Controls.UserControl类型”

另外,我认为我需要更改此控件的DataContext。在代码隐藏构造函数中,DataContext设置为我的ViewModel。我尝试将一个DataContext属性添加到ControlToFocusOn,但这不起作用:

EnhancedBusyIndicator

更新2

我需要将<my:EnhancedBusyIndicator DataContext="{Binding RelativeSource={RelativeSource Self}}" ControlToFocusOn="{Binding ElementName=NameTextBox}" RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}" IsBusy="{Binding IsDetailsBusyIndicatorActive}" ... > 更改为UserControl,因为我希望将焦点设置为Control个对象(实现TextBox)。但是,这并没有解决问题。

2 个答案:

答案 0 :(得分:0)

@Matt,不确定

DataContext="{Binding RelativeSource={RelativeSource Self}}"

将在Silverlight 5中运行,您是否尝试将其绑定为静态资源?

答案 1 :(得分:0)

如果视图中没有BusyIndicator,解决焦点问题的常用解决方案是添加代码

Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });

到视图的Loaded事件。这实际上即使在BusyIndicator存在的情况下也能正常工作;但是,BusyIndicator立即将焦点从其余的Silverlight控件上移开。解决方案是在Focus() 忙后调用控件的BusyIndicator方法。

我能够通过制作这样的控件来解决它:

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;
        }
    }
}

要使用它,请使用新的BusyIndicator替换xaml中的EnhancedBusyIndicator标记,然后添加相应的命名空间。

在元素中添加一个新属性ControlToFocusOn,并将其绑定到视图中的现有元素,在EnhancedBusyIndicator消失后,您希望焦点处于打开状态:

<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    ...
>
    ...
</my:EnhancedBusyIndicator>

在这种情况下,我专注于一个名为NameTextBox的文本框。

就是这样。每次我们导航到页面时,此控件都会聚焦。当我们在页面上时,如果EnhancedBusyIndicator变得忙碌并且不忙于agiain,焦点将不会转向控件;这只发生在初始加载时。

如果您想让EnhancedBusyIndicator再次关注ControlToFocusOn,请添加其他媒体资源AllowedToFocus

<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    AllowedToFocus="{Binding IsAllowedToFocus}"
    ...
>
    ...
</my:EnhancedBusyIndicator>

AllowedToFocus设置为true时,下次EnhancedBusyIndicator从忙碌切换到不忙时,焦点将转到ControlToFocusOn

加载视图时,AllowedToFocus也可以设置为false,以防止焦点转到控件。如果将AllowedToFocus绑定到ViewModel属性,则可能需要更改BindingMode。默认情况下,它是OneTime