WPF & MVVM Light - Closing a specific child window

2019-05-30 08:19发布

问题:

In my project I am able to open multiple child windows, display and return information from them, and then close them with a button click. The problem that I am having is that clicking the "Accept" or "Cancel" button closes all open windows. I need to find a way to only close the correct window and I haven't been able to figure out how. I am using MVVM Light and I'm thinking tokens may be the key but I haven't figured out how to make them work. If anybody could help me I'd greatly appreciate it.

Creating the child window in MainWindow:

Messenger.Default.Register<OpenWindowMessage>(this, message =>
    {
        var uniqueKey = System.Guid.NewGuid().ToString();
        var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(uniqueKey);
        adventurerWindowVM.Adv = message.Argument;
        var adventurerWindow = new AdventurerView()
        {
            DataContext = adventurerWindowVM,
            Owner = this
        };
        adventurerWindow.Closed += (sender, args) => SimpleIoc.Default.Unregister(uniqueKey);
        adventurerWindow.Show();
    });

Sending the close window message from AdventurerViewModel:

private void ExecuteAcceptCommand()
{
    Messenger.Default.Send(Adv.Name);

    Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage());
}

Receiving the close window messsage in AdventurerView:

Messenger.Default.Register<CloseWindowMessage>(this, x => Close());

回答1:

Token approach:

You have a uniqueKey you generate each of your AdventurerView with. Just use that when sending the CloseWindowMessage as the token.

Firstly in AdventurerViewModel add a new property of type string say "WindowKey"

public string WindowKey { get; set; }

Next add a constructor to take the unique key in AdventureView. In AdventurerView.xaml.cs:

public AdventurerView()
  :this(string.Empty) {}

public AdventurerView(string uniqueKey) {
  InitializeComponent();
  Messenger.Default.Register<CloseWindowMessage>(this, uniqueKey, s => Close());
}

Next in MainWindow.xaml.cs

switch

Messenger.Default.Register<OpenWindowMessage>(this, message =>
    {
        var uniqueKey = System.Guid.NewGuid().ToString();
        ...
        adventurerWindow.Show();
    });

to

Messenger.Default.Register<OpenWindowMessage>(this, message =>
    {
        var uniqueKey = System.Guid.NewGuid().ToString();
        var adventurerWindowVM = SimpleIoc.Default.GetInstance<AdventurerViewModel>(uniqueKey);
        adventurerWindowVM.Adv = message.Argument;
        adventurerWindowVM.WindowKey = uniqueKey;
        var adventurerWindow = new AdventurerView(uniqueKey)
        {
            DataContext = adventurerWindowVM,
            Owner = this
        };
        adventurerWindow.Closed += (sender, args) => SimpleIoc.Default.Unregister<AdventurerViewModel>(uniqueKey);
        adventurerWindow.Show();
    });

Finally in AdventurerViewModel.xaml.cs:

switch

private void ExecuteAcceptCommand() {
    Messenger.Default.Send(Adv.Name);
    Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage());
}

to

private void ExecuteAcceptCommand() {
    Messenger.Default.Send(Adv.Name);
    Messenger.Default.Send<CloseWindowMessage>(new CloseWindowMessage(), WindowKey);
}

Alternate:

Even-though the above would work perfectly fine, there is an alternate to this approach. Your "Message" is already a custom strong type(CloseWindowMessage). Now you can just add WindowKey as part of the message and have each Window when it receives a new CloseWindowMessage check the WindowKey in the message against it's own key and Close() accordingly.