Привязка UserControl к пользовательскому элементу управления BusyIndicator - PullRequest
1 голос
/ 13 января 2012

У меня есть требование сосредоточиться на конкретном текстовом поле при загрузке нового представления.

Решением было добавить эту строку кода в событие OnLoaded для представления:

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

Так что это работало для одного представления, но не для другого. Я потратил некоторое время на отладку проблемы и понял, что в новом представлении, над которым я работал, был BusyIndicator, который отвлекает внимание от всех элементов управления, так как для BusyIndicator было установлено значение true, а false происходило после события OnLoaded.

Таким образом, решение состоит в том, чтобы вызвать фокус на NameTextBox после того, как мой BusyIndicator был установлен в false. Моя идея состояла в том, чтобы создать повторно используемый элемент управления 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>

Таким образом, идея заключается в том, что когда IsRemoteFocusEnabled установлен в true в моей ViewModel (что я делаю после того, как я установил IsBusy в false в ViewModel), фокус будет установлен на NameTextBox. И если это сработает, другие могут использовать EnhancedBusyIndicator и просто привязать к другому элементу управления и соответствующим образом включить фокус в своих собственных моделях представления, при условии, что их представления имеют начальную BusyIndicator активную.

Однако я получаю это исключение при загрузке представления:

Установка свойства 'foo.Controls.EnhancedBusyIndicator.ControlToFocusOn' вызвала исключение. [Линия: 45 Позиция: 26]

Будет ли это решение работать? Если так, что не так с тем, что у меня есть (не могу установить свойство ControlToFocusOn)?


Обновление 1

Я установил Visual Studio 10 Tools для Silverlight 5 и получил лучшее сообщение об ошибке при переходе к новому представлению. Теперь я получаю это сообщение об ошибке:

«System.ArgumentException: объект типа System.Windows.Data.Binding не может быть преобразован в тип System.Windows.Controls.UserControl»

Кроме того, я думаю, что мне нужно изменить DataContext для этого элемента управления. В конструкторе с выделенным кодом для DataContext установлено значение моего ViewModel. Я попытался добавить свойство DataContext к EnhancedBusyIndicator, но это не сработало:

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

Обновление 2

Мне нужно изменить UserControl на Control, так как я хочу установить фокус на TextBox объекты (которые реализуют Control). Однако это не решает проблему.

Ответы [ 2 ]

0 голосов
/ 19 января 2012

Без 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 становится занятым и не занятым, фокус не переходит к элементу управления;это происходит только при начальной загрузке.

Если вы хотите, чтобы EnhancedBusyIndicator фокусировался на ControlToFocusOn в другой раз, добавьте другое свойство, AllowedToFocus:

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

КогдаAllowedToFocus установлен в значение true, в следующий раз EnhancedBusyIndicator переключится с занятого на не занятый, фокус перейдет к ControlToFocusOn.

При загрузке вида AllowedToFocus также может быть установлен в значение false, чтобы предотвратить фокусировкуот перехода к контролю.Если вы связываете AllowedToFocus со свойством ViewModel, вам может потребоваться изменить BindingMode.По умолчанию это OneTime.

0 голосов
/ 14 января 2012

@ Мэтт, не уверен,

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

будет работать в Silverlight 5, вы пытались связать его как статический ресурс?

...