UFO ET IT

WPF MVVM : 창을 닫는 방법

ufoet 2020. 11. 17. 21:30
반응형

WPF MVVM : 창을 닫는 방법


나는이 Button그것을 클릭 할 때 내 방 창문을 닫고 그 :

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

ie 에 a Command추가 할 때까지 괜찮습니다.Button

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

이제는 Command. 입력 EventHandler하고 this.Close()ie를 호출 하여이 문제를 해결할 수 있습니다.

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

하지만 지금은 내 코드에 코드가 SaveCommand있습니다. MVVM 패턴을 사용하고 SaveCommand있으며 내 코드에서 유일한 코드입니다.

뒤에서 코드를 사용하지 않도록 어떻게 다르게 할 수 있습니까?


이 주제에 대한 블로그 게시물방금 완료했습니다 . 간단히 말해서 접근 Action자를 사용하여 ViewModel에 속성을 추가 하십시오 . 그런 다음 생성자 에서 정의하십시오 . 마지막으로 창을 닫아야하는 바인딩 된 명령에서 작업을 호출합니다.getsetActionView

ViewModel에서 :

public Action CloseAction  { get; set;}

그리고 View생성자에서 :

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

마지막으로 창을 닫아야하는 바인딩 된 명령이 무엇이든 간단히 호출 할 수 있습니다.

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

이것은 나를 위해 일했고 상당히 우아한 솔루션처럼 보였으며 많은 코딩을 절약했습니다.


누군가가 언급했듯이 내가 게시 한 코드는 MVVM 친화적이지 않습니다. 두 번째 솔루션은 어떻습니까?

1st, not MVVM 솔루션 (참고로 삭제하지 않겠습니다)

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; }

두 번째, 아마도 더 나은 솔루션 : 연결된 동작 사용

XAML

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

모델보기

public ICommand OkCommand
{
    get { return _okCommand; }
}

Behavior Class 다음과 유사한 것 :

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();
    }

}

나는 개인적으로 이런 종류의 일을하기 위해 행동을 사용합니다.

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();
            };
        }
    }

그런 다음에이를 첨부 할 수 있습니다 WindowButton작업을 할 :

<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>

닫기 전에 명령을 실행할 수 있도록 CommandCommandParameter여기에 추가했습니다 Window.


매우 깨끗하고 MVVM 방식은 사용 InteractionTrigger하고 CallMethodAction정의하는 것입니다.Microsoft.Interactivity.Core

아래와 같이 두 개의 네임 스페이스를 추가해야합니다.

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

어셈블리 System.Windows.InteractivityMicrosoft.Expression.Interactions 및 아래 xaml 코드가 작동합니다.

<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>

뒤에 코드 나 다른 코드가 필요하지 않으며의 다른 메서드를 호출 할 수도 있습니다 Window.


작은 앱의 경우 창과 DataContext를 표시, 닫기 및 삭제하기 위해 자체 애플리케이션 컨트롤러를 사용합니다. 애플리케이션 UI의 중심점입니다.

다음과 같습니다.

//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();              
    }
}

ViewModels 에서의 호출 :

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

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

물론 내 솔루션에서 몇 가지 제한 사항을 찾을 수 있습니다. 다시 말하지만 작은 프로젝트에 사용하고 충분합니다. 관심이 있으시면 여기 또는 다른 곳에 전체 코드를 게시 할 수 있습니다.


이 문제를 일반적인 MVVM 방식으로 해결하려고 시도했지만 항상 불필요한 복잡한 논리로 끝납니다. 밀접한 동작을 달성하기 위해 코드 숨김 규칙에서 예외를 만들고 코드 숨김에서 단순히 좋은 이벤트를 사용했습니다.

XAML :

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

뒤에있는 코드 :

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

명령 / MVVM을 사용하여 이것이 더 잘 지원되기를 원하지만 이벤트를 사용하는 것보다 더 간단하고 명확한 솔루션은 없다고 생각합니다.


복잡한 클래스 종속성에 게시 구독 패턴사용합니다 .

ViewModel :

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

창문:

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

    public SomeWindow()
    {
        InitializeComponent();

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

Bizmonger.Patterns 를 활용하여 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
}

}

신청

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);
    }
}

이 작업에는 Expression Blend 3에 도입 된 동작 인 MVVM을 중단하지 않는 유용한 동작이있어 View가 ViewModel 내에 완전히 정의 된 명령에 연결할 수 있습니다.

이 동작은 ViewModel이 Model-View-ViewModel 애플리케이션에서 View의 닫기 이벤트를 관리 할 수 ​​있도록하는 간단한 기술을 보여줍니다.

이를 통해 컨트롤의 창을 제어 할 수있는 뷰 (UserControl)의 동작을 연결하여 ViewModel이 표준 ICommand를 통해 창을 닫을 수 있는지 여부를 제어 할 수 있습니다.

동작을 사용하여 ViewModel이 MV-VM에서 View 수명을 관리하도록 허용

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

위 링크는 http://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content 에 보관되었습니다 .


나는이 주제로 한동안 고생했고 결국 MVVM과 여전히 일치하는 가장 간단한 접근 방식을 사용했습니다. 버튼이 모든 무거운 작업을 수행하는 명령을 실행하고 버튼의 클릭 핸들러가 창을 닫도록합니다.

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!

사실, 여전히 코드 숨김이 있지만 본질적으로 그것에 대해 나쁜 것은 없습니다. 그리고 OO 관점에서 창을 닫으라고 말하는 것이 가장 의미가 있습니다.


.xaml 정의에 name 속성이 있습니다.

x:Name="WindowsForm"

그런 다음 버튼이 있습니다.

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

그런 다음 ViewModel에서 :

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

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

그런 다음 마지막으로 조치 방법 :

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

}

이 코드를 사용하여 응용 프로그램에서 팝업 창을 닫았습니다.


.Net Core 3.0을 기반으로하는 WPF 애플리케이션에서이 작업을 수행해야하는 것을 발견했습니다. 불행히도 Microsoft.Xaml.Behaviors.Wpf NuGet 패키지 에서는 아직 동작 지원이 공식적으로 제공되지 않았습니다 .

대신 Façade 디자인 패턴을 사용하는 솔루션을 사용했습니다.

상호 작용:

public interface IWindowFacade
{
    void Close();
}

창문:

public partial class MainWindow : Window, IWindowFacade

뷰 모델의 표준 명령 속성 :

public ICommand ExitCommand

제어 바인딩 :

<MenuItem Header="E_xit" Command="{Binding ExitCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>

명령:

public class ExitCommand : ICommand
{
    public void Execute(object parameter)
    {
        var windowFacade = parameter as IWindowFacade;
        windowFacade?.Close();
    }
}

Close()메서드는 이미 Window클래스에서 구현 되었기 때문에 창에 façade 인터페이스를 적용하는 것이 UI 레이어 뒤에있는 유일한 필수 코드입니다 (이 간단한 예제의 경우). 프레젠테이션 레이어의 명령 Close은 파사드 에서 메서드를 호출 할 때 무엇을 말하는지 알지 못하기 때문에 뷰 / UI 레이어에 대한 종속성을 피합니다 .


나는 같은 문제에 대한 해결책을 찾고 있었고 다음 작업이 잘 작동한다는 것을 알았습니다. 해결책은 OP가 그의 질문에서 언급 한 것과 비슷하지만 몇 가지 차이점이 있습니다.

  1. IsCancel재산이 필요 없습니다 .

  2. 코드 숨김은 창을 닫지 않아야합니다. 그냥 설정DialogResult

제 경우에는 먼저 코드를 실행 한 다음 버튼에 연결된 모델 명령을 봅니다.

XAML

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

코드 비하인드

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

모델보기

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

도움이 되었기를 바랍니다.


질문을 다시 말하고 그렇게함으로써 다른 해결책을 제시 할 수 있습니다. MVVM 환경에서 뷰, 뷰 모델 및 기타 항목 간의 통신을 활성화하려면 어떻게해야합니까? 중재자 패턴을 사용할 수 있습니다. 기본적으로 알림 시스템입니다. 실제 중재자 구현의 경우 Google에 문의하거나 저에게 요청하면 이메일로 보낼 수 있습니다.

보기를 닫는 것이 목적인 명령을 작성하십시오.

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

중재자는 알림 (토큰)을 발생시킵니다.

View 코드 숨김 생성자에서 다음과 같이이 알림 (토큰)을 수신합니다.

public ClientConfigView()
{
    InitializeComponent();
    Mediator.ListenOn(Mediator.Token.ConfigWindowShouldClose, callback => this.Close() );
}

Silverlight에는 다음과 같은 솔루션이 있습니다. 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;
        }
    }

MainPage.xaml :

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

나는 당신에게 모든 좋은 아이디어와 프로젝트를 기원합니다 ;-)


이것은 최소한의 코드 뒤에 mvvm을 사용하여 wpf 창을 닫는 데 도움이 될 수 있습니다 : http://jkshay.com/closing-a-wpf-window-using-mvvm-and-minimal-code-behind/


가장 간단한 방법은 이미 (거의) 포함되지 않은 것 같습니다. 새로운 종속성을 추가하는 Behaviors를 사용하는 대신 연결된 속성을 사용하십시오.

    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;
        }
    }

그런 다음 대화 버튼에서 설정하십시오.

<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>

나는 또한이 문제를 다루어야했다. 그래서 여기 내 해결책이있다. 그것은 나를 위해 잘 작동합니다.

1. 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. 명령 정의

        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.보기에서 명령 바인딩

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

4. 이제 창

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

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

그래서 xaml 파일에서 명령을 바인딩하고 FindAncestor로 창을 찾아 명령 매개 변수에 바인딩 할 수도 있습니다.


나를 위해 일한 wpf에서 창을 닫는 솔루션은 여기에 대답하지 않았으므로 내 솔루션도 추가 할 것이라고 생각했습니다.

        private static Window GetWindow(DependencyObject sender)
        {
            Window window = null;
            if (sender is Window)
                window = (Window)sender;
            if (window == null)
                window = Window.GetWindow(sender);
            return window;
        }
        private void CloseWindow(object sender, RoutedEventArgs e)
        {
            var button = (Button)sender as DependencyObject;

            Window window = GetWindow(button);
                if (window != null)
                    window.Close();
                   // window.Visibility = Visibility.Hidden; 
           // choose between window.close or set window.visibility to close or hide the window.

            //            }
        }

Add CloseWindow event to the button in you window as following.

<Button Content="Cancel" Click="CloseWindow" >

You can do it without code behind. Create command, in Execute method call "Save" method on viewmodel and after that call close method on edit window, which you can pass to the command by parameter:

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

Save&Close button XAML:

<Button Content"Save&Close" Command="{Binding SaveCmd}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"  IsDefault="True" />

참고URL : https://stackoverflow.com/questions/4376475/wpf-mvvm-how-to-close-a-window

반응형