Используя интерфейс сервиса, чтобы получить пользовательский ввод из окна


Я не работал с WPF/MVVM отвечает за какое-то время, но теперь я снова решает некоторые вопросы с одним из моих старых приложений и вспоминая схваток я всегда, когда имеешь дело с запрос пользовательского ввода (например. отображение диалогового окна) из модели представления.

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

public interface IUserInputService
{
    UserRegistration PromptUserRegistration();
}

Тогда я написал универсальный метод, который создает новый экземпляр представления и модели представления, связывает их воедино, показывает диалоговое окно и возвращает диалоговое окно вид модели и результат (например. приняли, отменили) обратно. Наконец, абонент может извлечь информацию из возвращаемой модели представления и возвращать нужный результат. Модели вид есть события, которые вызываются, когда в модели представления "готово", например: принят и отменен. Они подключены с помощью универсального метода.

public class UserInputService : IUserInputService
{
    // Implementation of the service interface using the generic method below
    public UserRegistration PromptUserRegistration()
    {
        var result = ShowDialog<UserRegistrationDialog, UserRegistrationViewModel>((v, vm) =>
        {
            vm.Accepted += (s, e) => v.DialogResult = true;
            vm.Cancelled += (s, e) => v.DialogResult = false;
        });

        if (result.State != DialogState.Accepted)
        {
            return null;
        }

        return new UserRegistration
        {
            FirstName = result.ViewModel.FirstName,
            LastName = result.ViewModel.LastName
        };
    }

    // The generic method
    private DialogResult<TViewModel> ShowDialog<TView, TViewModel>(
        Action<TView, TViewModel> viewModelConnected
    )
        where TView : Window, new()
        where TViewModel : ViewModelBase, new()
    {
        TViewModel viewModel = new TViewModel();
        TView view = new TView();
        view.DataContext = viewModel;

        viewModelConnected(view, viewModel);

        view.ShowDialog();

        DialogState state;
        switch (view.DialogResult)
        {
            case true:
                state = DialogState.Accepted;
                break;
            case false:
                state = DialogState.Declined;
                break;
            default:
                state = DialogState.Cancelled;
                break;
        }

        return new DialogResult<TViewModel>(state, viewModel);
    }
}

Теперь я могу обойти интерфейс сервиса с помощью di/МОК и посмотреть модели можно задать для пользовательского ввода, не заботясь о том, как (например. через диалог или насмешливо).

Другие типы (dialogresult и DialogState) таковы:

public enum DialogState
{
    Accepted,
    Declined,
    Cancelled
}

public class DialogResult<TViewModel>
{
    public DialogResult(DialogState state, TViewModel viewModel)
    {
        State = state;
        ViewModel = viewModel;
    }

    public DialogState State { get; }
    public TViewModel ViewModel { get; }
}

Поэтому мои вопросы...

  1. Это хороший подход?
  2. Что бы вы сделали иначе и почему?
  3. Любые другие предложения, касающиеся качества кода?

Я хочу придерживаться принципов в MVVM как можно больше (но в пределах разумного), сохраняя при этом все тестируемые... позвольте мне знать, что вы думаете.



396
5
задан 7 марта 2018 в 01:03 Источник Поделиться
Комментарии
2 ответа

Я работал в проекте, где такой IUserInputService был использован. После нескольких лет реализации был большой класс, содержащий вызов dialog логика для всех модулей.

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


Комментарий Код


  • Ваш код выглядит чистым и опрятным

  • Связь между моделью представления и представлением событий может быть нормально в вашем случае, потому что вид модели-это всего лишь внутренняя структура не известна за пределами метода. Однако, в оферта я предпочел бы путь, где точка зрения не ссылки на модели представления (который является косвенным, поскольку делегаты содержать указание на вид). Обычно, я использую модели представления собственность "боол закрыть" и привязать его к просмотру, так что мнение может реагировать на изменения и закрыть себя.

  • Ваш подход не позволяет создание модели представления через контейнер. Если виртуальная машина имеет зависимости, они должны быть переданы методу PromptUserRegistration.

  • Настройка виртуальной машины по умолчанию требует параметризации PromptUserRegistration Как хорошо.

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


Обратите внимание, что есть также рамки, адресс этой проблемы. Е. Г. https://github.com/FantasticFiasco/mvvm-dialogs

2
ответ дан 10 марта 2018 в 08:03 Источник Поделиться

Мне очень нравится, как ReactiveUI обрабатывает эти случаи. Существует класс называется взаимодействие. Вы выражаетесь, как общественная собственность на виртуальной машине и обработчики зарегистрироваться на ваш взгляд.

public class ViewModel : ReactiveObject
{
private readonly Interaction<string, bool> confirm;

public ViewModel()
{
this.confirm = new Interaction<string, bool>();
}

public Interaction<string, bool> Confirm => this.confirm;

public async Task DeleteFileAsync()
{
var fileName = ...;

// this will throw an exception if nothing handles the interaction
var delete = await this.confirm.Handle(fileName);

if (delete)
{
// delete the file
}
}
}

public class View
{
public View()
{
this.WhenActivated(
d =>
{
d(this
.ViewModel
.Confirm
.RegisterHandler(
async interaction =>
{
var deleteIt = await this.DisplayAlert(
"Confirm Delete",
$"Are you sure you want to delete '{interaction.Input}'?",
"YES",
"NO");

interaction.SetOutput(deleteIt);
}));
});
}
}

В слой " вид " вы, конечно, можете воспользоваться сервисом. На плюс сторона, вы можете легко UnitTest различных сценариях (по регистрации различных обработчиков) без необходимости фактически привязка ViewModel в управления интерфейса.

0
ответ дан 11 марта 2018 в 03:03 Источник Поделиться