WPF MVVM: comment fermer une fenêtre

j'ai un Button qui ferme ma fenêtre quand il est cliqué:

<Button x:Name="buttonOk"  IsCancel="True">Ok</Button>

c'est très bien jusqu'à ce que j'ajoute un Command au Button i.e.

<Button x:Name="buttonOk" 
        Command="{Binding SaveCommand}" 
        IsCancel="True">Ok</Button>

maintenant il ne ferme pas probablement parce que je m'occupe du Command . Je peux corriger ceci en mettant un EventHandler Dans et appelant this.Close() i.e.

<Button x:Name="buttonOk" 
        Click="closeWindow" 
        Command="{Binding SaveCommand}" 
        IsCancel="True">Ok</Button>

mais maintenant j'ai du code dans mon code derrière la méthode SaveCommand . J'utilise le modèle MVVM et SaveCommand est le seul code dans mon code derrière.

Comment faire autrement pour ne pas utiliser de code derrière?

61
demandé sur ΩmegaMan 2010-12-07 15:18:31

18 réponses

je viens de compléter un blog post sur ce même sujet. En un mot, ajoutez une propriété Action à votre Visimodel avec des accesseurs get et set . Puis définissez le Action de votre constructeur View . Enfin, invoquez votre action dans la commande liée qui devrait fermer la fenêtre.

dans le modèle de vue:

public Action CloseAction  { get; set;}

et dans le View constructeur:

private View()
{
    InitializeComponent();
    ViewModel vm = new ViewModel();
    this.DataContext = vm;
    if ( vm.CloseAction == null )
        vm.CloseAction = new Action(this.Close);
}

enfin, dans n'importe quelle commande liée qui devrait fermer la fenêtre, nous pouvons simplement invoquer

CloseAction(); // Calls Close() method of the View

Cela a fonctionné pour moi, semblait assez élégante solution, et m'a sauvé un tas de codage.

48
répondu Jonathan Shay 2015-12-08 19:11:48

malheureusement, l'affichage de fenêtres est une véritable douleur dans MVVM donc vous devez faire un peu de travail d'infrastructure ou utiliser un cadre MVVM comme Cinch . Si vous voulez investir le temps de le faire vous-même voici un lien de la façon dont Cinch le fait.

c'est bien que vous essayiez de garder toute logique hors de la vue mais ce n'est vraiment pas la fin du monde si vous le faites. Dans ce cas, il ne semble pas que cela causerait trop problème.

21
répondu Jon Mitchell 2010-12-07 12:50:51

comme quelqu'un l'a commenté, le code que j'ai posté n'est pas amical MVVM, qu'en est-il de la deuxième solution?

le 1er, pas MVVM solution (je ne vais pas supprimer ce comme référence)

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>

ViewModel:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    // Your Code
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

2ème, probablement meilleure solution: À l'aide de joint comportements

XAML

<Button Content="Ok and Close" Command="{Binding OkCommand}" b:CloseOnClickBehaviour.IsEnabled="True" />

View Modèle

public ICommand OkCommand
{
    get { return _okCommand; }
}

Classe De Comportement Quelque chose de semblable à ceci:

public static class CloseOnClickBehaviour
{
    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached(
            "IsEnabled",
            typeof(bool),
            typeof(CloseOnClickBehaviour),
            new PropertyMetadata(false, OnIsEnabledPropertyChanged)
        );

    public static bool GetIsEnabled(DependencyObject obj)
    {
        var val = obj.GetValue(IsEnabledProperty);
        return (bool)val;
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }

    static void OnIsEnabledPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
    {
        var button = dpo as Button;
        if (button == null)
            return;

        var oldValue = (bool)args.OldValue;
        var newValue = (bool)args.NewValue;

        if (!oldValue && newValue)
        {
            button.Click += OnClick;
        }
        else if (oldValue && !newValue)
        {
            button.PreviewMouseLeftButtonDown -= OnClick;
        }
    }

    static void OnClick(object sender, RoutedEventArgs e)
    {
        var button = sender as Button;
        if (button == null)
            return;

        var win = Window.GetWindow(button);
        if (win == null)
            return;

        win.Close();
    }

}
13
répondu Simone 2017-12-22 22:04:12

j'utiliserais personnellement un comportement pour faire ce genre de chose:

public class WindowCloseBehaviour : Behavior<Window>
{
    public static readonly DependencyProperty CommandProperty =
      DependencyProperty.Register(
        "Command",
        typeof(ICommand),
        typeof(WindowCloseBehaviour));

    public static readonly DependencyProperty CommandParameterProperty =
      DependencyProperty.Register(
        "CommandParameter",
        typeof(object),
        typeof(WindowCloseBehaviour));

    public static readonly DependencyProperty CloseButtonProperty =
      DependencyProperty.Register(
        "CloseButton",
        typeof(Button),
        typeof(WindowCloseBehaviour),
        new FrameworkPropertyMetadata(null, OnButtonChanged));

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public Button CloseButton
    {
        get { return (Button)GetValue(CloseButtonProperty); }
        set { SetValue(CloseButtonProperty, value); }
    }

    private static void OnButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var window = (Window)((WindowCloseBehaviour)d).AssociatedObject;
        ((Button) e.NewValue).Click +=
            (s, e1) =>
            {
                var command = ((WindowCloseBehaviour)d).Command;
                var commandParameter = ((WindowCloseBehaviour)d).CommandParameter;
                if (command != null)
                {
                    command.Execute(commandParameter);                                                      
                }
                window.Close();
            };
        }
    }

vous pouvez alors joindre ceci à votre Window et Button pour faire le travail:

<Window x:Class="WpfApplication6.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:local="clr-namespace:WpfApplication6"
        Title="Window1" Height="300" Width="300">
    <i:Interaction.Behaviors>
        <local:WindowCloseBehaviour CloseButton="{Binding ElementName=closeButton}"/>
    </i:Interaction.Behaviors>
    <Grid>
        <Button Name="closeButton">Close</Button>
    </Grid>
</Window>

j'ai ajouté Command et CommandParameter ici pour que vous puissiez exécuter une commande avant la fermeture du Window .

11
répondu The Angry Programmer 2016-10-05 17:28:53

pour les petites applications, j'utilise mon propre contrôleur D'Application pour afficher, fermer et éliminer les fenêtres et les textes de données. C'est un point central dans L'UI d'une demande.

c'est quelque chose comme ça:

//It is singleton, I will just post 2 methods and their invocations
public void ShowNewWindow(Window window, object dataContext = null, bool dialog = true)
{
    window.DataContext = dataContext;
    addToWindowRegistry(dataContext, window);

    if (dialog)
        window.ShowDialog();
    else
        window.Show();

}

public void CloseWindow(object dataContextSender)
{
    var correspondingWindows = windowRegistry.Where(c => c.DataContext.Equals(dataContextSender)).ToList();
    foreach (var pair in correspondingWindows)
    {
        pair.Window.Close();              
    }
}

et leurs invocations de ViewModels :

// Show new Window with DataContext
ApplicationController.Instance.ShowNewWindow(
                new ClientCardsWindow(),
                new ClientCardsVM(),
                false);

// Close Current Window from viewModel
ApplicationController.Instance.CloseWindow(this);

bien sûr, vous pouvez trouver quelques restrictions dans ma solution. Encore une fois: je l'utilise pour de petits projets, et c'est suffisant. Si vous êtes intéressé, je peux poster le code complet ici ou ailleurs/

7
répondu Ilya Smagin 2013-05-02 11:53:15

j'utilise le publier le patron D'Abonnement pour les dépendances de classe compliquées:

ViewModel:

    public class ViewModel : ViewModelBase
    {
        public ViewModel()
        {
            CloseComand = new DelegateCommand((obj) =>
                {
                    MessageBus.Instance.Publish(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, null);
                });
        }
}

fenêtre:

public partial class SomeWindow : Window
{
    Subscription _subscription = new Subscription();

    public SomeWindow()
    {
        InitializeComponent();

        _subscription.Subscribe(Messages.REQUEST_DEPLOYMENT_SETTINGS_CLOSED, obj =>
            {
                this.Close();
            });
    }
}

Vous pouvez tirer parti de la Bizmonger.Motifs pour obtenir le MessageBus.

MessageBus

public class MessageBus
{
    #region Singleton
    static MessageBus _messageBus = null;
    private MessageBus() { }

    public static MessageBus Instance
    {
        get
        {
            if (_messageBus == null)
            {
                _messageBus = new MessageBus();
            }

            return _messageBus;
        }
    }
    #endregion

    #region Members
    List<Observer> _observers = new List<Observer>();
    List<Observer> _oneTimeObservers = new List<Observer>();
    List<Observer> _waitingSubscribers = new List<Observer>();
    List<Observer> _waitingUnsubscribers = new List<Observer>();

    int _publishingCount = 0;
    #endregion

    public void Subscribe(string message, Action<object> response)
    {
        Subscribe(message, response, _observers);
    }

    public void SubscribeFirstPublication(string message, Action<object> response)
    {
        Subscribe(message, response, _oneTimeObservers);
    }

    public int Unsubscribe(string message, Action<object> response)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Respond == response).ToList());
        observers.AddRange(_waitingSubscribers.Where(o => o.Respond == response));
        observers.AddRange(_oneTimeObservers.Where(o => o.Respond == response));

        if (_publishingCount == 0)
        {
            observers.ForEach(o => _observers.Remove(o));
        }

        else
        {
            _waitingUnsubscribers.AddRange(observers);
        }

        return observers.Count;
    }

    public int Unsubscribe(string subscription)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription).ToList());
        observers.AddRange(_waitingSubscribers.Where(o => o.Subscription == subscription));
        observers.AddRange(_oneTimeObservers.Where(o => o.Subscription == subscription));

        if (_publishingCount == 0)
        {
            observers.ForEach(o => _observers.Remove(o));
        }

        else
        {
            _waitingUnsubscribers.AddRange(observers);
        }

        return observers.Count;
    }

    public void Publish(string message, object payload)
    {
        _publishingCount++;

        Publish(_observers, message, payload);
        Publish(_oneTimeObservers, message, payload);
        Publish(_waitingSubscribers, message, payload);

        _oneTimeObservers.RemoveAll(o => o.Subscription == message);
        _waitingUnsubscribers.Clear();

        _publishingCount--;
    }

    private void Publish(List<Observer> observers, string message, object payload)
    {
        Debug.Assert(_publishingCount >= 0);

        var subscribers = observers.Where(o => o.Subscription.ToLower() == message.ToLower());

        foreach (var subscriber in subscribers)
        {
            subscriber.Respond(payload);
        }
    }

    public IEnumerable<Observer> GetObservers(string subscription)
    {
        var observers = new List<Observer>(_observers.Where(o => o.Subscription == subscription));
        return observers;
    }

    public void Clear()
    {
        _observers.Clear();
        _oneTimeObservers.Clear();
    }

    #region Helpers
    private void Subscribe(string message, Action<object> response, List<Observer> observers)
    {
        Debug.Assert(_publishingCount >= 0);

        var observer = new Observer() { Subscription = message, Respond = response };

        if (_publishingCount == 0)
        {
            observers.Add(observer);
        }
        else
        {
            _waitingSubscribers.Add(observer);
        }
    }
    #endregion
}

}

Abonnement

public class Subscription
{
    #region Members
    List<Observer> _observerList = new List<Observer>();
    #endregion

    public void Unsubscribe(string subscription)
    {
        var observers = _observerList.Where(o => o.Subscription == subscription);

        foreach (var observer in observers)
        {
            MessageBus.Instance.Unsubscribe(observer.Subscription, observer.Respond);
        }

        _observerList.Where(o => o.Subscription == subscription).ToList().ForEach(o => _observerList.Remove(o));
    }

    public void Subscribe(string subscription, Action<object> response)
    {
        MessageBus.Instance.Subscribe(subscription, response);
        _observerList.Add(new Observer() { Subscription = subscription, Respond = response });
    }

    public void SubscribeFirstPublication(string subscription, Action<object> response)
    {
        MessageBus.Instance.SubscribeFirstPublication(subscription, response);
    }
}
5
répondu Scott Nimrod 2015-03-13 13:31:03

j'ai essayé de résoudre ce problème d'une manière générique, MVVM, mais je trouve toujours que je finis par une logique complexe inutile. Pour parvenir à un comportement proche, j'ai fait une exception à la règle du "Pas de code derrière" et j'ai utilisé simplement les bons vieux événements dans le code derrière:

XAML:

<Button Content="Close" Click="OnCloseClicked" />

Code derrière:

private void OnCloseClicked(object sender, EventArgs e)
{
    Visibility = Visibility.Collapsed;
}

bien que je souhaite que ce soit mieux supporté en utilisant commandes / MVVM, je pense simplement que il n'y a pas de solution plus simple et plus claire que d'utiliser des événements.

4
répondu larsmoa 2013-05-02 12:02:09

Très propre et MVVM façon est d'utiliser des InteractionTrigger et CallMethodAction au sens de Microsoft.Interactivity.Core

vous devrez ajouter deux espaces de noms comme ci-dessous

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"

Et Ensembles Système.Windows.Interactivité et Microsoft.Expression.Les Interactions et ensuite en dessous du code xaml fonctionneront.

<Button Content="Save" Command="{Binding SaveCommand}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <ei:CallMethodAction MethodName="Close"
                           TargetObject="{Binding RelativeSource={RelativeSource
                                                  Mode=FindAncestor,
                                                  AncestorType=Window}}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</Button>

vous n'avez pas besoin de code ou autre chose et pouvez également appelez toute autre méthode de Window .

4
répondu RAJ 2016-10-03 18:05:18

il y a un comportement utile pour cette tâche qui ne casse pas MVVM, un comportement, introduit avec Expression Blend 3, pour permettre à la vue de se raccorder à des commandes définies complètement dans le ViewModel.

ce comportement démontre une technique simple pour permettre à la ViewModel pour gérer les événements de fermeture de la vue dans un Modèle-Vue-Vuemodèle application.

cela vous permet de raccorder un comportement dans votre vue (UserControl) qui fournira le contrôle sur la fenêtre de la commande, permettant le modèle de vue pour contrôler si la fenêtre peut être fermée via ICommands standard.

utiliser des comportements pour permettre au modèle de vue de gérer la durée de vie de la vue en M-V-VM

http://gallery.expression.microsoft.com/WindowCloseBehavior /

le lien ci-dessus a été archivé http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content

3
répondu akjoshi 2013-03-14 13:57:24

j'ai lutté avec ce sujet pendant un certain temps, et finalement est allé avec l'approche la plus simple qui est encore compatible avec MVVM: avoir le bouton Exécuter la commande qui fait tout le levage lourd et avoir le Gestionnaire de clic du bouton fermer la fenêtre.

XAML

<Button x:Name="buttonOk" 
        Click="closeWindow" 
        Command="{Binding SaveCommand}" />

XAML.cs

public void closeWindow() 
{
    this.DialogResult = true;
}

SaveCommand.cs

 // I'm in my own file, not the code-behind!

il est vrai qu'il y a encore du code derrière, mais il n'y a rien de mal à cela. Et c'est le plus logique pour moi, d'un point de vue OO, de juste dire à la fenêtre de se fermer.

1
répondu DPH 2015-10-19 21:18:51

Nous avons le nom de la propriété dans le .XAML définition:

x:Name="WindowsForm"

puis nous avons le bouton:

<Button Command="{Binding CloseCommand}" 
CommandParameter="{Binding ElementName=WindowsForm}" />

puis dans le modèle de vue:

public DelegateCommand <Object>  CloseCommand { get; private set; }

Constructor for that view model:
this.CloseCommand = new DelegateCommand<object>(this.CloseAction);

puis enfin, la méthode d'action:

private void CloseAction (object obj)
{
  Window Win = obj as Window;
  Win.Close();

}

j'ai utilisé ce code pour fermer une fenêtre pop-up d'une application..

1
répondu Prabhuprasad NG 2016-03-21 17:53:10

j'ai été à la recherche d'une solution au même problème et j'ai trouvé que faire des travaux suivants très bien. La solution est similaire à ce que OP a mentionné dans sa question avec quelques différences:

  1. Pas besoin de "151930920 de la propriété".

  2. le Code derrière ne doit pas fermer la fenêtre. Juste mettre DialogResult

dans mon cas, il exécute d'abord le code derrière et ensuite voir la commande model liée au bouton.

XAML

<Button x:Name="buttonOk" Click="Save_Click" Command="{Binding SaveCommand}">OK</Button>

Code Derrière

private void Apply_OnClick(object sender, RoutedEventArgs e)
{
    this.DialogResult = true;
}

Modèle De Vue

private void Save()
{
 // Save data.
}

Espérons que cette aide.

0
répondu AksharRoop 2013-05-29 06:17:34

vous pourriez reformuler la question, et en le faisant - trouver une autre solution. Comment puis-je activer la communication entre vues, modèles de vues et autres dans un environnement MVVM? Vous pouvez utiliser le modèle de médiateur. Il s'agit essentiellement d'un système de notification. Pour la mise en œuvre effective de Mediator, googlez-le ou demandez-le moi et je peux l'envoyer par e-mail.

Faire une Commande dont le but est de fermer la vue.

public void Execute( object parameter )
{
    this.viewModel.DisposeMyStuff();
    Mediator.NotifyColleagues(Mediator.Token.ConfigWindowShouldClose);
}

le Médiateur soulèvera un notification (un jeton)

écouter cette notification (token) comme ceci dans la vue codebehind constructor:

public ClientConfigView()
{
    InitializeComponent();
    Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}
0
répondu imbageek 2013-10-29 15:00:11

j'ai la solution suivante à Silverlight. Serait également dans WPF.

ChildWindowExt.cs:

namespace System.Windows.Controls
{
    public class ChildWindowExt : ChildWindow
    {
        public static readonly DependencyProperty IsOpenedProperty =
          DependencyProperty.Register(
          "IsOpened",
          typeof(bool),
          typeof(ChildWindowExt),
          new PropertyMetadata(false, IsOpenedChanged));

        private static void IsOpenedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue == false)
            {
                ChildWindowExt window = d as ChildWindowExt;
                window.Close();
            }
            else if ((bool)e.NewValue == true)
            {
                ChildWindowExt window = d as ChildWindowExt;
                window.Show();
            }
        }

        public bool IsOpened
        {
            get { return (bool)GetValue(IsOpenedProperty); }
            set { SetValue(IsOpenedProperty, value); }
        }

        protected override void OnClosing(ComponentModel.CancelEventArgs e)
        {
            this.IsOpened = false;
            base.OnClosing(e);
        }

        protected override void OnOpened()
        {
            this.IsOpened = true;
            base.OnOpened();
        }
    }
}

ItemWindow.xaml:

<extControls:ChildWindowExt  
    x:Class="MyProject.ItemWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:extControls="clr-namespace:System.Windows.Controls"
    Title="{Binding Title}" IsOpened="{Binding IsOpened, Mode=TwoWay}" Width="640" Height="480">

    <Grid x:Name="LayoutRoot">
        <Button Command="{Binding UpdateCommand}" Content="OK" Width="70" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>

</extControls:ChildWindowExt>

ItemViewModel.cs:

private bool _IsOpened;
public bool IsOpened
{
    get
    {
        return _IsOpened;
    }
    set
    {
        if (!Equals(_IsOpened, value))
        {
            _IsOpened = value;
            RaisePropertyChanged("IsOpened");
        }
    }
}

private RelayCommand _UpdateCommand;
/// <summary>
/// Insert / Update data entity
/// </summary>
public RelayCommand UpdateCommand
{
    get
    {
        if (_UpdateCommand == null)
        {
            _UpdateCommand = new RelayCommand(
                () =>
                {
                    // Insert / Update data entity
                    ...

                    IsOpened = false;
                },
                () =>
                {
                    return true;
                });
        }
        return _UpdateCommand;
    }
}

ItemsViewModel.cs:

    private RelayCommand _InsertItemCommand;
    /// <summary>
    /// 
    /// </summary>
    public RelayCommand InsertItemCommand
    {
        get
        {
            if (_InsertItemCommand == null)
            {
                _InsertItemCommand = new RelayCommand(
                    () =>
                    {
                        ItemWindow itemWin = new ItemWindow();
                        itemWin.DataContext = new ItemViewModel();
                        itemWin.Show();

                        // OR

                        // ItemWindow itemWin = new ItemWindow();
                        // ItemViewModel newItem = new ItemViewModel();
                        // itemWin.DataContext = newItem;
                        // newItem.IsOpened = true;

                    },
                    () =>
                    {
                        return true;
                    });
            }
            return _InsertItemCommand;
        }
    }

de la page d'accueil.xaml:

<Grid x:Name="LayoutRoot">
    <Button Command="{Binding InsertItemCommand}" Content="Add New" Width="70" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>

je vous souhaite à tous de bonnes idées et de projets ;-)

0
répondu ADM-IT 2013-12-06 16:44:56

cela pourrait vous aider, fermer une fenêtre wpf en utilisant mvvm avec un code minimal derrière: http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/

0
répondu EvilInside 2015-01-28 20:38:02

je pense que le moyen le plus simple n'a pas été inclus déjà (presque). Au lieu d'utiliser des comportements qui ajoutent de nouvelles dépendances, il suffit d'utiliser les propriétés attachées:

    using System;
    using System.Windows;
    using System.Windows.Controls;

    public class DialogButtonManager
    {
        public static readonly DependencyProperty IsAcceptButtonProperty = DependencyProperty.RegisterAttached("IsAcceptButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsAcceptButtonPropertyChanged));
        public static readonly DependencyProperty IsCancelButtonProperty = DependencyProperty.RegisterAttached("IsCancelButton", typeof(bool), typeof(DialogButtonManager), new FrameworkPropertyMetadata(OnIsCancelButtonPropertyChanged));

        public static void SetIsAcceptButton(UIElement element, bool value)
        {
            element.SetValue(IsAcceptButtonProperty, value);
        }

        public static bool GetIsAcceptButton(UIElement element)
        {
            return (bool)element.GetValue(IsAcceptButtonProperty);
        }

        public static void SetIsCancelButton(UIElement element, bool value)
        {
            element.SetValue(IsCancelButtonProperty, value);
        }

        public static bool GetIsCancelButton(UIElement element)
        {
            return (bool)element.GetValue(IsCancelButtonProperty);
        }

        private static void OnIsAcceptButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Button button = sender as Button;

            if (button != null)
            {
                if ((bool)e.NewValue)
                {
                    SetAcceptButton(button);
                }
                else
                {
                    ResetAcceptButton(button);
                }
            }
        }

        private static void OnIsCancelButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            Button button = sender as Button;

            if (button != null)
            {
                if ((bool)e.NewValue)
                {
                    SetCancelButton(button);
                }
                else
                {
                    ResetCancelButton(button);
                }
            }
        }

        private static void SetAcceptButton(Button button)
        {
            Window window = Window.GetWindow(button);
            button.Command = new RelayCommand(new Action<object>(ExecuteAccept));
            button.CommandParameter = window;
        }

        private static void ResetAcceptButton(Button button)
        {
            button.Command = null;
            button.CommandParameter = null;
        }

        private static void ExecuteAccept(object buttonWindow)
        {
            Window window = (Window)buttonWindow;

            window.DialogResult = true;
        }

        private static void SetCancelButton(Button button)
        {
            Window window = Window.GetWindow(button);
            button.Command = new RelayCommand(new Action<object>(ExecuteCancel));
            button.CommandParameter = window;
        }

        private static void ResetCancelButton(Button button)
        {
            button.Command = null;
            button.CommandParameter = null;
        }

        private static void ExecuteCancel(object buttonWindow)
        {
            Window window = (Window)buttonWindow;

            window.DialogResult = false;
        }
    }

ensuite, il suffit de le régler sur vos boutons de dialogue:

<UniformGrid Grid.Row="2" Grid.Column="1" Rows="1" Columns="2" Margin="3" >
    <Button Content="Accept" IsDefault="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsAcceptButton="True" />
    <Button Content="Cancel" IsCancel="True" Padding="3" Margin="3,0,3,0" DialogButtonManager.IsCancelButton="True" />
</UniformGrid>
0
répondu Ignacio Soler Garcia 2015-02-23 12:46:18

Vous pouvez le faire sans code derrière. Créer une commande, dans la méthode exécuter, appeler la méthode" Save "sur viewmodel et, après cet appel, fermer la méthode sur edit window, que vous pouvez passer à la commande par paramètre:

public void Execute(object parameter)
{
    _mainViewModel.SaveSomething();
    var editWindow = parameter as MyEditWindow;
    editWindow?.Close();
}

Enregistrer&Fermer bouton XAML:

<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"  IsDefault="True" />
0
répondu Kamil Svoboda 2015-11-10 12:53:35

j'ai aussi eu à faire face à ce problème, donc voici ma solution. Il fonctionne très bien pour moi.

1. Créer la classe DelegateCommand

    public class DelegateCommand<T> : ICommand
{
    private Predicate<T> _canExecuteMethod;
    private readonly Action<T> _executeMethod;
    public event EventHandler CanExecuteChanged;

    public DelegateCommand(Action<T> executeMethod) : this(executeMethod, null)
    {
    }
    public DelegateCommand(Action<T> executeMethod, Predicate<T> canExecuteMethod)
    {
        this._canExecuteMethod = canExecuteMethod;
        this._executeMethod = executeMethod ?? throw new ArgumentNullException(nameof(executeMethod), "Command is not specified."); 
    }


    public void RaiseCanExecuteChanged()
    {
        if (this.CanExecuteChanged != null)
            CanExecuteChanged(this, null);
    }
    public bool CanExecute(object parameter)
    {
        return _canExecuteMethod == null || _canExecuteMethod((T)parameter) == true;
    }

    public void Execute(object parameter)
    {
        _executeMethod((T)parameter);
    }
}

2. Définissez votre commande

        public DelegateCommand<Window> CloseWindowCommand { get; private set; }


    public MyViewModel()//ctor of your viewmodel
    {
        //do something

        CloseWindowCommand = new DelegateCommand<Window>(CloseWindow);


    }
        public void CloseWindow(Window win) // this method is also in your viewmodel
    {
        //do something
        win?.Close();
    }

3. Lier votre commande dans le point de vue

public MyView(Window win) //ctor of your view, window as parameter
    {
        InitializeComponent();
        MyButton.CommandParameter = win;
        MyButton.Command = ((MyViewModel)this.DataContext).CloseWindowCommand;
    }

4. Et maintenant la fenêtre

  Window win = new Window()
        {
            Title = "My Window",
            Height = 800,
            Width = 800,
            WindowStartupLocation = WindowStartupLocation.CenterScreen,

        };
        win.Content = new MyView(win);
        win.ShowDialog();

alors voilà, vous pouvez aussi lier la commande dans le fichier xaml et trouver la fenêtre avec FindAncestor et la lier au paramètre commande.

0
répondu Hajvaz 2017-09-22 22:43:25