C# 将UserControl绑定到自定义BusyIndicator控件

C# 将UserControl绑定到自定义BusyIndicator控件,c#,silverlight-4.0,mvvm,busyindicator,C#,Silverlight 4.0,Mvvm,Busyindicator,我需要在加载新视图时关注特定的文本框 解决方案是将这行代码添加到视图的onload事件中: Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); }); 因此,这对一种观点有效,但对另一种观点无效。我花了一些时间调试这个问题,并意识到我正在处理的新视图有一个BusyIndicator,它将焦点从所有控件上移开,因为BusyIndicator设置为true和false是在onload事件之后发生的 因此,解决方案是在将my BusyIndi

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

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

Dispatcher.BeginInvoke(() => { NameTextBox.Focus(); });
因此,这对一种观点有效,但对另一种观点无效。我花了一些时间调试这个问题,并意识到我正在处理的新视图有一个BusyIndicator,它将焦点从所有控件上移开,因为BusyIndicator设置为true和false是在onload事件之后发生的

因此,解决方案是在将my BusyIndicator设置为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文件中,没有问题:

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

更新2

我需要将
UserControl
更改为
Control
,因为我需要将焦点设置为
TextBox
对象(实现
Control
)。但是,这并不能解决问题。

@Matt,不确定

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

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

如果视图中没有
总线指示器,解决焦点问题的常见解决方案是添加代码

Dispatcher.BeginInvoke(() => { ControlToFocusOn.Focus(); });
到视图的
已加载事件。即使存在
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;
        }
    }
}
要使用它,请将xaml中的
BusyIndicator
标记替换为新的
EnhancedBusyIndicator
,并添加适当的命名空间

在元素内部添加一个新属性,
ControlToFocusOn
,并将其绑定到视图中的一个现有元素,在
EnhancedBusyIndicator
消失后,您希望焦点集中在该元素上:

<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    ...
>
    ...
</my:EnhancedBusyIndicator>
AllowedToFocus
设置为true时,下次
EnhancedBusyIndicator
从忙切换到不忙时,焦点将转到
ControlToFocusOn


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

我实际上使用的是Silverlight 4;我只是能够通过使用VisualStudio10的Silverlight 5工具来获得我在“Update 1”中发布的更好的错误消息,这也恰好适用于Silverlight 4。我没有尝试将其绑定为静态资源。我试试看。
<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    ...
>
    ...
</my:EnhancedBusyIndicator>
<my:EnhancedBusyIndicator
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    AllowedToFocus="{Binding IsAllowedToFocus}"
    ...
>
    ...
</my:EnhancedBusyIndicator>