WPF MVVM : 창을 닫는 방법


78

나는이 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있으며 내 코드에서 유일한 코드입니다.

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


16
참고- IsCancel = "True"확인 버튼을 설정 하는 것은 좋지 않습니다. 이 속성은 취소 버튼 용입니다.
Greg D

답변:


62

이 주제에 대한 블로그 게시물 을 방금 완료했습니다 . 간단히 말해서 및 접근 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

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


이것은 나를 위해 작동하지 않습니다. 내가)합니다 (CloseAction를 호출 할 때, 그것은 CloseAction가 null가보기에 코드에도 불구이라고 말한다
다니엘

11
내 무지를 실례하지만 이것이 어떻게 View와 ViewModel을 분리하는 원칙을 위반하지 않습니까? View에서 ViewModel을 인스턴스화하는 경우 MVVM을 사용하지 않을 수도 있습니다. 가장 좋은 방법은 View와 ViewModel을 개별적으로 인스턴스화하고 DataContext를 View 자체 외부의 View로 설정하는 것입니다.
saegeoff 2014

2
Action을 정적 속성으로 만들어 해결했습니다. 다 아아!
Talal Yousif

12
나는 이것이 늙어 가고 있다는 것을 알고 있지만, 내가 알지 못하는 엄격한 정의가 없으면이 방법이 MVVM을 깨지 않는다고 주장합니다. 궁극적으로 MVVM을 사용하려면 VM이 뷰를 인식하지 못하지만 뷰는 VM을 인식해야합니다. 뷰를 교체하면 어떤 방식 으로든 VM이 손상되지 않습니다. 인스턴스화되지 않은 작업이있을 수 있지만 MVVM 규칙이 위반된다는 선언이 아니라고 생각합니다. "WPF DataContext Instantiation"을 검색하면 많은 기사에서 바로이 방법이 나타납니다.
flyNflip 2015

6
null 확인을 없애기 위해 속성 주입 대신 생성자 주입을 수행 할 수 있습니다. programmers.stackexchange.com/questions/177649/… this.DataContext = new ViewModel(this.Close); 그런 다음 ViewModel의 생성자에서 CloseAction에 가깝게 할당합니다. 또한 CloseAction을 가져 오기 전용으로 만드는 이점도 있습니다.
DharmaTurtle

22

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

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

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

Microsoft.Xmal.Behaviours.Wpf 어셈블리 가 필요하면 아래 xaml 코드가 작동합니다.

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

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


이것은 코드 뒤에 코드가없고 ViewModel을 View에 연결하지 않는다는 점에서 지금까지 본 것 중 가장 깨끗한 접근 방식입니다. 명령에서도 작동합니다. 몇 개의 추가 DLL을 배포해야하며, 명령 내에서 닫기를 취소하려면 추가 작업이 필요합니다. 코드 뒤에 Click 이벤트가 있고 Close ()를 호출하는 것과 크게 다르지 않습니다. 이벤트 핸들러 뒤에있는 코드는 close 이벤트를 취소하는 close 명령의 시나리오를 더 쉽게 처리 할 수 ​​있도록합니다 (예 : 오류 저장이있는 경우 데이터). 감사합니다 Massimiliano
Richard Moore

2
MS가 WPF 동작을 오픈 소스로 만들고 Microsoft.Xaml.Behaviors.Wpf NuGet 패키지로 이동했기 때문에 Rajnikant의 코드는 VS 2019에서 더 오래 작동합니다. 정보 출처는 발행 할 주석입니다 : developercommunity.visualstudio.com/content/problem/198075/… . 코드를 리팩토링하는 자세한 단계는 다음 위치에 있습니다. devblogs.microsoft.com/dotnet/…
Eric Wood

더 깨끗하지만 표준 닫기 버튼이 아닌 명령에 속하기 때문에 뷰가 아닌 뷰 모델에 의해 제어되어야한다고 생각합니다.
Daniel Möller

17

누군가가 언급했듯이 내가 게시 한 코드는 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();
    }

}

33
당신은 결코 나와 함께 반복하지 않으며 결코 창을 ViewModel과 결합하지 않습니다. 이제 문장 100 개 인스턴스 : 쓰기
이그나시오 솔러 가르시아

4
+1 IMHO 이것이 최고의 솔루션입니다. 가장 짧고 복잡한 인프라가 필요하지 않고 MVVM 방식으로 문제를 해결합니다. @SoMoS-여기에는 커플 링이 없습니다. 조금도. VM은 View의 존재를 인식하지 못합니다. 명령은 닫을 항목을 알아야하므로 Window를 매개 변수로 가져옵니다.
Ilia Barahovski 2013 년

2
+1 @SoMoS Ilia에 동의합니다. 이것이 정확히 분리 된 솔루션입니다. 저장 및 창 논리를 함께 결합하지는 않지만 이것은 또 다른 문제입니다
makc

8
@Barahovski : 창은 WPF 개체입니다. 뷰 모델은 WPF 또는 무거운 프레임 워크에 의존해서는 안됩니다. 단위 테스트 (UI없이)는 이것을 테스트하기 위해 Window 인스턴스를 어떻게 얻을 수 있습니까?
g.pickardou 2014

@IgnacioSolerGarcia 귀하의 댓글에 +1. 첨부 된 행동 접근법이 더 나은 해결책일까요? 내 대답을 업데이트했습니다.
Simone

13

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

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.


1
이 파티에 조금 늦었지만 버튼에 직접 동작을 추가하여 더 단순화 할 수 있습니다. 및 후크에 Window.GetWindow(AssociatedObject)?.Close()대한 재정의에서 연결 / 분리 되는 호출 (물론 적절한 null 검사 포함) 을 호출하는 Click 이벤트에 대한 처리기를 정의 할 수 있습니다 . 세 가지 사소한 기능, 0 속성 및 동일한 (또는 다른) 창에서 임의의 수의 버튼에 연결할 수 있습니다. OnAttachedOnDetaching
bionicOnion

흠,이 경우, 대신 창으로 동작을 부착하고, 버튼을, 당신은에 동작을 첨부 할 수있는 전달의 더 나은 디자인되지 않을 것 버튼을 하고 창을 통과를?
Sören Kuklau

9

작은 앱의 경우 창과 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);

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


7

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

XAML :

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

뒤에있는 코드 :

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

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


6

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

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 를 활용할 수 있습니다. 를 활용하여 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);
    }
}

4

이 작업에는 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 에 보관되었습니다 .


4

나는이 주제에 대해 한동안 어려움을 겪었고 결국 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 관점에서 창을 닫으라고 말하는 것이 가장 의미가 있습니다.


4

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

}

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


2

.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 레이어에 대한 종속성을 피합니다 .


2

현재 창 xaml.cs파일에서 아래 코드를 호출하십시오.

var curWnd = Window.GetWindow(this); // passing current window context
curWnd?.Close();

이것은 일을해야합니다.
그것은 나를 위해 일했습니다. 희망은 당신을 위해 똑같이 할 것입니다)


1

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>

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



1

가장 간단한 방법은 이미 (거의) 포함되지 않은 것 같습니다. 새로운 종속성을 추가하는 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

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

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로 창을 찾아 명령 매개 변수에 바인딩 할 수도 있습니다.


0

나는 같은 문제에 대한 해결책을 찾고 있었고 다음 작업이 잘 작동한다는 것을 알았습니다. 해결책은 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.
}

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


0

질문을 다시 말하고 그렇게함으로써 다른 해결책을 제시 할 수 있습니다. 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() );
}

0

나를 위해 일한 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.

            //            }
        }

다음과 같이 창의 버튼에 CloseWindow 이벤트를 추가합니다.

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

무례하게 굴고 싶지는 않지만 창 자체의 코드에서 Click 이벤트를 처리 한 경우 Close ()를 호출하여 창을 직접 닫을 수 있습니다. 닫을 수 있도록 Button의 Window 부모를 검색 할 필요가 없습니다. 귀하의 답변은 MVVM과 관련이 없으며 코드를 다시 시작할 수 있습니다. private void CloseWindow (object sender, RoutedEventArgs e) {Close (); }
Alexandru Dicu

0

간단한 접근 방식은 saveComand 구현에서 창을 닫는 것입니다. 창을 닫으려면 아래 코드를 사용하십시오.

Application.Current.Windows[1].Close();

자식 창을 닫습니다.


-2

뒤에 코드없이 할 수 있습니다. Create command, in Execute method call "Save"method on viewmodel and then 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();
}

저장 및 닫기 버튼 XAML :

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

코드 숨김은 이와 같은 뷰 모델에 뷰의 인스턴스를 갖는 것보다 낫습니다. 문제는 MVVM 패턴을 따르는 것에 관한 것입니다.
Alexandru Dicu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.