Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
144 views
in Technique[技术] by (71.8m points)

c# - Bind ViewModel method to View's Alt+F4 and Close button

I have created a custom method to close a form in my view model and bound it to a button. How do I get the default close button in the title bar and Alt+F4 to run the same command?

public void Close(object parameter)
{
    // Check if any field has been edited
    if (IsDirty())
    {
        string message = "You have unsaved changes.

Are you sure you want to close this form?";
        string title = "Close Window";
        MessageBoxResult result = MessageBox.Show(message, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);

        if (result == MessageBoxResult.Cancel)
            return;
    }

    employeeWindow.Close();
}

I'm still new to WPF and MVVM, and I'm just not understanding any of the solutions already out there or how I can customise them to run the above code.

question from:https://stackoverflow.com/questions/66054510/bind-viewmodel-method-to-views-altf4-and-close-button

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

What you are looking for is the Closing event on Window. Its CancelEventArgs carry a Cancel property of type bool that can be set to true, which will cancel closing the window. The closing event will both be fired for closing a window using the close button and pressing Alt+F4.

Code-Behind

In a code-behind scenario, you would add an event handler in XAML like this.

<Window Closing="OnClosing" ...>

Then, you would create this event handler in code-behind and set the Cancel property accordingly.

private void OnClosing(object sender, CancelEventArgs e)
{
   // Check if any field has been edited
   if (IsDirty())
   {
      string message = "You have unsaved changes.

Are you sure you want to close this form?";
      string title = "Close Window";
      MessageBoxResult result = MessageBox.Show(message, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);

      if (result == MessageBoxResult.Cancel)
         e.Cancel = true;
   }
}

Event Trigger (Non MVVM)

In MVVM you could install the Microsoft.Xaml.Behaviors.Wpf NuGet package, which is a replacement of the legacy Blend behaviors (System.Windows.Interactivity). You can use an EventTrigger to bind the event to a command in your view model. In this example, you pass the CancelEventArgs directly to the command.

<Window ...>
   <b:Interaction.Triggers>
      <b:EventTrigger EventName="Closing">
         <b:InvokeCommandAction Command="{Binding ClosingCommand}"
                                PassEventArgsToCommand="True"/>
      </b:EventTrigger>
   </b:Interaction.Triggers>
   <!-- ...other markup. -->
</Window>

This solution allows you to define a command in your view model.

public class MyViewModel
{
   public MyViewModel()
   {
      ClosingCommand = new RelayCommand<CancelEventArgs>(ExecuteClosing);
   }

   public ICommand ClosingCommand { get; }

private void ExecuteClosing(CancelEventArgs e)
   {
      string message = "You have unsaved changes.

Are you sure you want to close this form?";
      string title = "Close Window";
      MessageBoxResult result = MessageBox.Show(message, title, MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
   
      if (result == MessageBoxResult.Cancel)
         e.Cancel = true;
   }
}

I do not provide an ICommand implementation here. For more information on the basics refer to:

Although this solution uses a command on the view model, it is not MVVM compliant, since the message box is a view component that must not reside in a view model. The same applies to the cancel event args.

MVVM Behavior

An MVVM compliant way could be to create a behavior to move the confirmation code out. For that create an interface for your view model that contains the IsDirty method and implement it in your view model.

public interface IStatefulViewModel
{
   bool IsDirty();
}
public class MyViewModel : IStatefulViewModel
{
   // ...your code.

   public bool IsDirty()
   {
      // ...your checks.
   }
}

Then, create a behavior using the Microsoft.Xaml.Behaviors.Wpf NuGet package. This behavior is reusable and encapsulates the closing logic decoupled from your view model. The Caption and Message dependency properties allow binding the message box contents.

public class WindowClosingBehavior : Behavior<Window>
{
   public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register(
      nameof(Caption), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
      nameof(Message), typeof(string), typeof(WindowClosingBehavior), new PropertyMetadata(string.Empty));

   public string Caption
   {
      get => (string)GetValue(CaptionProperty);
      set => SetValue(CaptionProperty, value);
   }

   public string Message
   {
      get => (string)GetValue(MessageProperty);
      set => SetValue(MessageProperty, value);
   }

   protected override void OnAttached()
   {
      base.OnAttached();
      AssociatedObject.Closing += OnClosing;
   }

   protected override void OnDetaching()
   {
      base.OnDetaching();
      AssociatedObject.Closing -= OnClosing;
   }

   private void OnClosing(object sender, CancelEventArgs e)
   {
      if (!(AssociatedObject.DataContext is IStatefulViewModel statefulViewModel))
         return;

      if (!statefulViewModel.IsDirty())
         return;

      e.Cancel = ConfirmClosing();
   }

   private bool ConfirmClosing()
   {
      var result = MessageBox.Show(
         Message,
         Caption,
         MessageBoxButton.OKCancel,
         MessageBoxImage.Warning,
         MessageBoxResult.Cancel);

      return result == MessageBoxResult.Cancel;
   }
}

Attach the behavior to your window. Note that you can do this on any window.

<Window ...>
   <b:Interaction.Behaviors>
      <local:WindowClosingBehavior Caption="Close Window"
                                   Message="You have unsaved changes.&#13;&#10;&#13;&#10;Are you sure you want to close this form?"/>
   </b:Interaction.Behaviors>
   <!-- ...other markup. -->
</Window>

Do not be confused by the &#13;&#10; characters, those are newlines ( ) in XML.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...