Waqar (Vicky) Ответов: 1

Как установить фокус на элемент управления WPF из модели представления, когда команду кнопку поиска асинхронного выполнен


В моем коде позади меня есть метод OnFocusRequested который вызывается с помощью интерфейса
из моей модели представления, когда команду кнопку поиска асинхронного выполнения.

проблема в том, что он отлично работает, когда я просто отлаживаю каждый шаг из кода. этот код не работает в режиме реального времени.
Я считаю, что это связано с асинхронным вызовом этой команды кнопки из модели представления
но не знаю, как это выяснить и исправить.
есть предложения ?

Что я уже пробовал:

private void OnFocusRequested(object sender, FocusRequestedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "StatementDate":
                    Application.Current.Dispatcher.BeginInvoke(
                        new Action(() =>
                        {
                            dateStatement.Focus();
                            Keyboard.Focus(dateStatement);
                        }),
                        DispatcherPriority.ContextIdle,
                        null
                    );
                    break;
            }
        }

это мой код за файловым методом, в котором я устанавливаю фокус, который я пробовал с диспетчером или без него, но результат тот же.

Nathan Minier

Контрольный фокус-это строго функция представления, поэтому я бы сделал это в коде представления, подключенном к событию завершения в ViewModel.

Graeme_Grant

слишком грязно ... ниже приведено простое и чистое решение ;)

1 Ответов

Рейтинг:
8

Graeme_Grant

Вы можете сделать это в коде позади, однако может быть грязно, если вам придется делать это для нескольких элементов управления. Ответ заключается в том, что там, где вам нужно использовать код более чем один раз, использовать поведение, а не код позади, чтобы инкапсулировать код, чтобы избежать одного и того же кода в нескольких местах, уменьшить количество ошибок и упростить обслуживание. Затем в XAML свяжите элемент управления со свойствами ViewModel. Вот что будет делать это решение...

Во-первых, нам нужно добавить основной механизм привязки данных для уведомления представления и команду для связи от представления к ViewModel:

public abstract class ObservableBase : INotifyPropertyChanged
{
    public void Set<TValue>(ref TValue field, TValue newValue, [CallerMemberName] string propertyName = "")
    {
        if (!EqualityComparer<TValue>.Default.Equals(field, default(TValue)) && field.Equals(newValue)) return;
        field = newValue;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public abstract class ViewModelBase : ObservableBase
{
    public bool IsInDesignMode
        => (bool) DesignerProperties.IsInDesignModeProperty
            .GetMetadata(typeof(DependencyObject))
            .DefaultValue;
}

public class RelayCommand<T> : ICommand
{
    #region Fields

    private readonly Action<T> execute;
    private readonly Predicate<T> canExecute;

    #endregion

    #region Constructors

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        this.execute = execute ?? throw new ArgumentNullException("execute");
        this.canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    public bool CanExecute(object parameter)
        => canExecute == null || canExecute((T)parameter);

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public void Execute(object parameter)
        => execute(parameter == null
            ? default(T)
            : (T)Convert.ChangeType(parameter, typeof(T)));

    #endregion
}

Теперь о поведении... Он присоединяется к элементу управления, на который нам нужно установить фокус, и привязывается к свойству в ViewModel, чтобы вызвать изменение фокуса...
public static class FocusHelper
{
    public static bool GetIsFocused(DependencyObject ctrl)
        => (bool)ctrl.GetValue(IsFocusedProperty);

    public static void SetIsFocused(DependencyObject ctrl, bool value)
        => ctrl.SetValue(IsFocusedProperty, value);

    public static readonly DependencyProperty IsFocusedProperty =
        DependencyProperty.RegisterAttached(
            "IsFocused", typeof(bool), typeof(FocusHelper),
            new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var ctrl = (UIElement)d;
        if ((bool)e.NewValue)
        {
            ctrl.Focus(); // Don't care about false values.
        }
    }
}

Способ, которым он будет работать, заключается в том, что при нажатии кнопки устанавливается свойство, уведомляющее о поведении для установки фокуса на присоединенный элемент управления.

Итак, вот ViewModel:
public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        if (IsInDesignMode)
        {
            // design time only...
        }
        else
        {
            // runtime only...
        }
    }

    #region Relay Command

    public RelayCommand<string> SelectCommand => new RelayCommand<string>(SetSelection);

    private void SetSelection(string value)
    {
        SelectThis = value == "this";
        SelectThat = value == "that";
    }

    #endregion

    #region Properties

    private bool selectThis;
    public bool SelectThis
    {
        get => selectThis;
        set => Set(ref selectThis, value);
    }

    private bool selectThat;
    public bool SelectThat
    {
        get => selectThat;
        set => Set(ref selectThat, value);
    }

    private string thisText = "ThisText";
    public string ThisText
    {
        get => thisText;
        set => Set(ref thisText, value);
    }

    private string thatText = "ThatText";
    public string ThatText
    {
        get => thatText;
        set => Set(ref thatText, value);
    }

    #endregion
}

Наконец, теперь мы можем подключить вид:
<Window x:Class="WpfSetFocus.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:l="clr-namespace:WpfSetFocus"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <l:MainViewModel/>
    </Window.DataContext>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="Padding" Value="10 5"/>
            </Style>
            <Style TargetType="TextBlock">
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="Grid.Row" Value="1"/>
            </Style>
            <Style TargetType="TextBox">
                <Setter Property="HorizontalAlignment" Value="Center"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="Width" Value="200"/>
                <Setter Property="Grid.Row" Value="2"/>
            </Style>
        </Grid.Resources>

        <Button Content="Select This"

                Command="{Binding SelectCommand}"

                CommandParameter="this"/>
        <TextBlock Text="{Binding SelectThis, StringFormat=Selected: {0}}"/>
        <TextBox l:FocusHelper.IsFocused="{Binding SelectThis}"

                 Text="{Binding ThisText}"/>

        <Button Content="Select That"

                Grid.Column="1"

                Command="{Binding SelectCommand}"

                CommandParameter="that"/>
        <TextBlock Text="{Binding SelectThat, StringFormat=Selected: {0}}"

                   Grid.Column="1"/>
        <TextBox Grid.Column="1"

                 l:FocusHelper.IsFocused="{Binding SelectThat}"

                 Text="{Binding ThatText}"/>

        <TextBlock Text="{Binding IsInDesignMode, StringFormat=Is in Design Mode: {0}}"

                   Grid.ColumnSpan="2"/>
    </Grid>
</Window>

Наслаждайтесь! :)