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

发布于 2024-12-27 09:44:55 字数 2932 浏览 5 评论 0原文

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

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

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

因此这适用于一个视图,但不适用于另一个视图。我花了一些时间调试这个问题,并意识到我正在处理的新视图有一个 BusyIndi​​cator,它将焦点从所有控件上移开,因为 BusyIndi​​cator 被设置为 true 和 false 是在 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),焦点将设置为 NameTextBox。如果它有效,其他人可以使用 EnhancedBusyIndi​​cator 并绑定到不同的控件并在自己的 ViewModel 中适当地启用焦点,假设他们的视图具有初始的 BusyIndi​​cator 活动状态。

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

Set property 'foo.Controls.EnhancedBusyIndi​​cator.ControlToFocusOn' 引发异常。 [线路:45 位置:26]

我尝试的这个解决方案有效吗?如果是这样,到目前为止我所拥有的有什么问题(无法设置 ControlToFocusOn 属性)?


更新 1

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

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

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

<my:EnhancedBusyIndicator
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
    IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>

更新 2

我需要将 UserControl 更改为 Control< /code> 因为我希望将焦点设置到 TextBox 对象(实现 Control)。然而,这并不能解决问题。

I have a requirement to focus on a specific textbox when a new view is loaded.

The solution was to add this line of code to the OnLoaded event for the view:

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

So this worked for one view, but not another. I spent some time debugging the problem and realized that the new view I was working on had a BusyIndicator that takes focus away from all controls since the BusyIndicator being set to true and false was occuring after the OnLoaded event.

So the solution is to call focus to the NameTextBox after my BusyIndicator has been set to false. My idea was to create a reusable BusyIndicator control that handles this extra work. However, I am having trouble doing this in MVVM.

I started by making a simple extension of the toolkit: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.");
    }

I added the control to my XAML file with no problem:

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

So the idea is when IsRemoteFocusEnabled is set to true in my ViewModel (which I do after I've set IsBusy to false in the ViewModel), focus will be set to NameTextBox. And if it works, others could use the EnhancedBusyIndicator and just bind to a different control and enable the focus appropriately in their own ViewModels, assuming their views have an intial BusyIndicator active.

However, I get this exception when the view is loaded:

Set property 'foo.Controls.EnhancedBusyIndicator.ControlToFocusOn' threw an exception. [Line: 45 Position: 26]

Will this solution I am attempting work? If so, what is wrong with what I have thus far (cannot set the ControlToFocusOn property)?


Update 1

I installed Visual Studio 10 Tools for Silverlight 5 and got a better error message when navigating to the new view. Now I gete this error message:

"System.ArgumentException: Object of type System.Windows.Data.Binding cannot be converted to type System.Windows.Controls.UserControl"

Also, I think I need to change the DataContext for this control. In the code-behind constructor, DataContext is set to my ViewModel. I tried adding a DataContext property to the EnhancedBusyIndicator, but that did not work:

<my:EnhancedBusyIndicator
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    ControlToFocusOn="{Binding ElementName=NameTextBox}"
    RemoteFocusIsEnabled="{Binding IsRemoteFocusEnabled}"
    IsBusy="{Binding IsDetailsBusyIndicatorActive}"
...
>

Update 2

I need to change UserControl to Control since I will be wanting to set focus to TextBox objects (which implement Control). However, this does not solve the issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

混吃等死 2025-01-03 09:44:55

@Matt,不确定

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

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

@Matt, not sure

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

will work in Silverlight 5, have you tried binding it as a static resource?

隐诗 2025-01-03 09:44:55

如果视图中没有 BusyIndi​​cator ,解决焦点问题的常见解决方案是将代码添加

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

到视图的 Loaded 事件中。即使存在 BusyIndi​​cator,这实际上也有效;但是,BusyIndi​​cator 会立即将焦点从 Silverlight 控件的其余部分移开。解决方案是在BusyIndi​​cator忙后调用控件的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 中的 BusyIndi​​cator 标记替换为新的 EnhancedBusyIndi​​cator 并添加适当的命名空间。

在元素内添加一个新属性 ControlToFocusOn,并将其绑定到视图中您希望在 EnhancedBusyIndi​​cator 消失后焦点所在的现有元素:

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

在本例中,我关注一个名为 NameTextBox 的文本框。

就是这样。每次我们导航到该页面时,该控件都会获得焦点。当我们在页面上时,如果 EnhancedBusyIndi​​cator 再次变得忙碌并且不再忙碌,焦点将不会转到控件;这仅发生在初始加载时。

如果您希望允许 EnhancedBusyIndi​​cator 再次聚焦于 ControlToFocusOn,请添加另一个属性 AllowedToFocus

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

AllowedToFocus 时> 设置为 true,下次 EnhancedBusyIndi​​cator 从忙切换到不忙时,焦点将转到 ControlToFocusOn

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

Without a BusyIndicator present in the view, the common solution to solve the focus problem is to add the code

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

to the Loaded event of the view. This actually works even with the BusyIndicator present; however, the BusyIndicator immediately takes focus away from the rest of the Silverlight controls. The solution is to invoke the Focus() method of the control after the BusyIndicator is not busy.

I was able to solve it by making a control like this:

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

To use it, replace the BusyIndicator tags in your xaml with the new EnhancedBusyIndicator and add the appropriate namespace.

Add a new property, ControlToFocusOn inside the element, and bind it to an existing element in the view that you want focus to be on after the EnhancedBusyIndicator disappears:

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

In this case, I focused to a textbox called NameTextBox.

That's it. This control will get focus every time we navigate to the page. While we are on the page, if the EnhancedBusyIndicator becomes busy and not busy agiain, focus will not go to the control; this only happens on initial load.

If you want to allow the EnhancedBusyIndicator to focus to the ControlToFocusOn another time, add another property, AllowedToFocus:

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

When AllowedToFocus is set to true, the next time EnhancedBusyIndicator switches from busy to not busy, focus will go to ControlToFocusOn.

AllowedToFocus can also be set to false when loading the view, to prevent focus from going to a control. If you bind AllowedToFocus to a ViewModel property, you may need to change the BindingMode. By default, it is OneTime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文