Как ViewModel должен закрыть форму?

Я создал пример stackblitz , где работает тип ввода = файл, единственное, чего не хватает в моем примере - это запрос на загрузку. Попробуйте построить объект запроса таким образом .

Если ваш сервер ожидает multipart/form-data, вы должны использовать его как Content-Type вместо application/json. Не забудьте добавить свой токен Authorization в заголовок запроса:

let fileList: FileList = event.target.files;
if(fileList.length > 0) {
    let file: File = fileList[0];
    let formData:FormData = new FormData();
    formData.append('uploadFile', file, file.name);
    let headers = new Headers();
    /** In Angular 5, including the header Content-Type can invalidate your request */

    headers.append('Content-Type', 'multipart/form-data');
    headers.append('Accept', 'application/json');
    headers.append('Authorization', 'Bearer ' + accessToken);

    let options = new RequestOptions({ headers: headers });
    this.http.post(`${this.apiEndPoint}`, formData, options)
        .map(res => res.json())
        .catch(error => Observable.throw(error))
        .subscribe(
            data => console.log('success'),
            error => console.log(error)
        )
}

239
задан Community 23 May 2017 в 02:34
поделиться

9 ответов

Вот то, что я первоначально сделал, который действительно работает, однако это кажется довольно многоречивым и ужасным (глобальные помехи, что-либо никогда не хорошо)

1: App.xaml.cs

public partial class App : Application
{
    // create a new global custom WPF Command
    public static readonly RoutedUICommand LoggedIn = new RoutedUICommand();
}

2: LoginForm.xaml

// bind the global command to a local eventhandler
<CommandBinding Command="client:App.LoggedIn" Executed="OnLoggedIn" />

3: LoginForm.xaml.cs

// implement the local eventhandler in codebehind
private void OnLoggedIn( object sender, ExecutedRoutedEventArgs e )
{
    DialogResult = true;
    Close();
}

4: <час> LoginFormViewModel.cs

// fire the global command from the viewmodel
private void OnRemoteServerReturnedSuccess()
{
    App.LoggedIn.Execute(this, null);
}

я позже затем удалил весь этот код и просто имел эти LoginFormViewModel, обращаются к Близкому методу, это - представление. Это закончило тем, что было намного более хорошим и легче следовать. По моему скромному мнению, точка шаблонов должна дать людям более легкий способ понять то, что Ваше приложение делает, и в этом случае, MVVM делал его намного тяжелее, чтобы понять, не использовал ли я его и был теперь анти- - шаблон.

4
ответ дан Orion Edwards 23 November 2019 в 03:21
поделиться

Существует много комментариев, обсуждая за и против MVVM здесь. Для меня я соглашаюсь с Nir; это - вопрос использования шаблона соответственно, и MVVM не всегда соответствует. Люди, кажется, стали склонны жертвовать всеми самыми важными принципами разработки программного обеспечения ТОЛЬКО, чтобы заставить это соответствовать MVVM.

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

В большинстве случаев я столкнулся, WPF позволяет Вам продвинуться БЕЗ нескольких Windows. Возможно, Вы могли попытаться использовать Frames и Pages вместо Windows с DialogResults.

В Вашем случае мое предложение было бы, имеют LoginFormViewModel обработайте LoginCommand и если вход в систему недопустим, установите свойство на LoginFormViewModel к соответствующему значению (false или некоторое перечисление значений как UserAuthenticationStates.FailedAuthentication). Вы сделали бы то же для успешного входа в систему (true или некоторое другое перечисление значений). Вы затем использовали бы a DataTrigger который отвечает на различные состояния аутентификации пользователя и мог использовать простое Setter измениться Source свойство Frame.

Наличие Вашего Окна входа в систему возвращает a DialogResult я думаю, то, где Вы запутываетесь; это DialogResult действительно свойство Вашего ViewModel. В моем, по общему признанию ограниченном опыте с WPF, когда что-то обычно не чувствует себя хорошо он, потому что я думаю с точки зрения того, как я сделал бы то же самое в WinForms.

Надежда, которая помогает.

15
ответ дан Stimul8d 23 November 2019 в 03:21
поделиться

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

6
ответ дан 23 November 2019 в 03:21
поделиться

FYI, I ran into this same problem and I think I figured out a work around that doesn't require globals or statics, although it may not be the best answer. I let the you guys decide that for yourself.

In my case, the ViewModel that instantiates the Window to be displayed (lets call it ViewModelMain) also knows about the LoginFormViewModel (using the situation above as an example).

So what I did was to create a property on the LoginFormViewModel that was of type ICommand (Lets call it CloseWindowCommand). Then, before I call .ShowDialog() on the Window, I set the CloseWindowCommand property on the LoginFormViewModel to the window.Close() method of the Window I instantiated. Then inside the LoginFormViewModel all I have to do is call CloseWindowCommand.Execute() to close the window.

It is a bit of a workaround/hack I suppose, but it works well without really breaking the MVVM pattern.

Feel free to critique this process as much as you like, I can take it! :)

3
ответ дан 23 November 2019 в 03:21
поделиться

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

Я не могу понять, как создать приложение без диалогов (возможно, это просто блок ума) . Итак, я был в тупике с MVVM и показывал диалог. Итак, я наткнулся на эту статью CodeProject:

http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

Это UserControl, который позволяет окну находиться в визуальном дереве другого окна ( не допускается в xaml). Он также предоставляет логическое свойство DependencyProperty, называемое IsShowing.

Вы можете установить стиль, как обычно в словаре ресурсов, который в основном отображает диалог всякий раз, когда свойство Content элемента управления! = Null с помощью триггеров:

<Style TargetType="{x:Type d:Dialog}">
    <Style.Triggers>
        <Trigger Property="HasContent"  Value="True">
            <Setter Property="Showing" Value="True" />
        </Trigger>
    </Style.Triggers>
</Style>

В представлении, где вы хотите отобразить диалог, просто сделайте следующее:

<d:Dialog Content="{Binding Path=DialogViewModel}"/>

И в вашей ViewModel все, что вам нужно сделать, это установить для свойства значение (Примечание: класс ViewModel должен поддерживать INotifyPropertyChanged, чтобы представление знало, что что-то произошло).

вот так:

DialogViewModel = new DisplayViewModel();

Чтобы соответствовать ViewModel с View у вас должно быть что-то вроде этого в словаре ресурсов:

<DataTemplate DataType="{x:Type vm:DisplayViewModel}">
    <vw:DisplayView/>
</DataTemplate>

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

        var vm = new DisplayViewModel();
        vm.RequestClose += new RequestCloseHandler(DisplayViewModel_RequestClose);
        DialogViewModel = vm;

Затем вы можете обработать результат диалога с помощью обратного вызова.

Это может показаться немного сложно, но как только будет заложен фундамент, это довольно просто. Опять же, это моя реализация, я уверен, что есть и другие :)

Надеюсь, это поможет,

3
ответ дан 23 November 2019 в 03:21
поделиться

Я использовал прикрепленные поведения, чтобы закрыть окно. Свяжите свойство "signal" в вашей ViewModel с прикрепленным поведением (на самом деле я использую триггер) Если установлено значение true, поведение закрывает окно.

http://adammills.wordpress.com/2009/07/01/window-close-from-xaml/

18
ответ дан 23 November 2019 в 03:21
поделиться

Если предположить, что диалог входа в систему является первым созданным окном, попробуйте это в классе LoginViewModel:

    void OnLoginResponse(bool loginSucceded)
    {
        if (loginSucceded)
        {
            Window1 window = new Window1() { DataContext = new MainWindowViewModel() };
            window.Show();

            App.Current.MainWindow.Close();
            App.Current.MainWindow = window;
        }
        else
        {
            LoginError = true;
        }
    }
9
ответ дан 23 November 2019 в 03:21
поделиться

С моей точки зрения, вопрос довольно неплохой, так как такой же подход использовался бы не только для окна "Логин", но и для любого типа окна. Я просмотрел много предложений, и ни одно из них не подходит мне. Пожалуйста, ознакомьтесь с моим предложением, которое было взято из статьи MVVM design pattern article.

Каждый класс ViewModel должен наследовать от WorkspaceViewModel, который имеет событие RequestClose и свойство CloseCommand типа ICommand. Реализация свойства CloseCommand по умолчанию вызовет событие RequestClose.

Для того чтобы закрыть окно, необходимо переопределить метод OnLoaded вашего окна:

void CustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
    CustomerViewModel customer = CustomerViewModel.GetYourCustomer();
    DataContext = customer;
    customer.RequestClose += () => { Close(); };
}

или OnStartup вашего приложения:

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        MainWindow window = new MainWindow();
        var viewModel = new MainWindowViewModel();
        viewModel.RequestClose += window.Close;
        window.DataContext = viewModel;

        window.Show();
    }

Я думаю, что событие RequestClose и реализация свойства CloseCommand в WorkspaceViewModel довольно понятны, но я покажу их последовательность:

public abstract class WorkspaceViewModel : ViewModelBase
// There's nothing interesting in ViewModelBase as it only implements the INotifyPropertyChanged interface
{
    RelayCommand _closeCommand;
    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
            {
                _closeCommand = new RelayCommand(
                   param => Close(),
                   param => CanClose()
                   );
            }
            return _closeCommand;
        }
    }

    public event Action RequestClose;

    public virtual void Close()
    {
        if ( RequestClose != null )
        {
            RequestClose();
        }
    }

    public virtual bool CanClose()
    {
        return true;
    }
}

И исходный код RelayCommand:

public class RelayCommand : ICommand
{
    #region Constructors

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

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

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members

    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields
}

P. S. Не обращайтесь со мной плохо из-за этих источников! Если бы они были у меня вчера, это сэкономило бы мне несколько часов...

P.P.S. Любые комментарии или предложения приветствуются.

64
ответ дан 23 November 2019 в 03:21
поделиться

Меня вдохновил ответ Thejuan на написание более простого вложенного свойства. Никаких стилей, никаких триггеров; вместо этого вы можете просто сделать вот что:

<Window ...
        xmlns:xc="clr-namespace:ExCastle.Wpf"
        xc:DialogCloser.DialogResult="{Binding DialogResult}">

Это почти так же чисто, как если бы команда WPF сделала все правильно и сделала DialogResult зависимым свойством с самого начала. Просто поместите bool? свойство DialogResult на вашей ViewModel и реализуйте INotifyPropertyChanged, и вуаля, ваша ViewModel может закрыть окно (и установить его DialogResult), просто установив свойство. MVVM, каким он должен быть.

Вот код для DialogCloser:

using System.Windows;

namespace ExCastle.Wpf
{
    public static class DialogCloser
    {
        public static readonly DependencyProperty DialogResultProperty =
            DependencyProperty.RegisterAttached(
                "DialogResult",
                typeof(bool?),
                typeof(DialogCloser),
                new PropertyMetadata(DialogResultChanged));

        private static void DialogResultChanged(
            DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            var window = d as Window;
            if (window != null)
                window.DialogResult = e.NewValue as bool?;
        }
        public static void SetDialogResult(Window target, bool? value)
        {
            target.SetValue(DialogResultProperty, value);
        }
    }
}

Я также опубликовал это в своем блоге.

314
ответ дан 23 November 2019 в 03:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: