1

I am relatively new to WPF, XAML and Data-bindings. I have a view (Window) and a view-model.

I have tried to implement the MVVM pattern which means neither the view nor the view-model hold a reference to each other. All data exchange happens via data-bindings.

So far so good but now I have run into a problem I can't find a solution for.

On my view I have a button Start which is bound to a command.

<Button Command="{Binding NextCommand}" Content="Next">

NextCommand is of type ActionCommand : ICommand

In my case NextCommand simply calls a private method within the view-model.

The problem I can not find a solution so far is the following:

How to close the window at the end of the view-models NextCommandAction method?

private void NextCommandAction(object o)
{
    ...
    ...
    // close the window
}

Since I do not have a reference to the view I can not just set DialogResult = true;

The only working solution I have found so far is to add a hidden radio-button to the view and bind it's value to a property CloseView and create a method CloseView within the xaml.cs file which is bound to the Checked event of the hidden radio-button. Within that method I set DialogResult = true;

Although this works I feel like there has to be a better solution than adding hidden elements to your view!

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
Thomas Huber
  • 1,282
  • 1
  • 13
  • 23
  • 1
    An answer on how to bind the DialogResult from the Window to the ViewModel could be found here: [How should the ViewModel close the form?](https://stackoverflow.com/q/501886/9758687) – Coden Jun 06 '20 at 13:47

4 Answers4

1

You can pass the window reference as CommandParameter to the Close command and do whatever required on the window.

<Button Content="Close" Command="{Binding Path=CloseCommand}" 
  CommandParameter="{Binding ElementName=Window}"/>

private void CloseCommand(object sender)
{
    Window wnd = sender as Window;
    wnd.Close();
}
Stealth Rabbi
  • 10,156
  • 22
  • 100
  • 176
1

CommandParameter="{Binding ElementName=Window}" assumes that you have an element in your XAML named "Window". e.g, your Window tag would need Name="Window"

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Stealth Rabbi
  • 10,156
  • 22
  • 100
  • 176
0

This question was one of the first things that came up when I googled to check if DialogResult is a dependency property (it isn't :-) )

Add a dependency property to your Window:

 public static readonly DependencyProperty InteractionResultProperty =
            DependencyProperty.Register(
                nameof(InteractionResult),
                typeof(Boolean?),
                typeof(MyWpfWindow1),
                new FrameworkPropertyMetadata(default(Boolean?), 
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                    OnInteractionResultChanged));

  public Boolean? InteractionResult
        {
            get => (Boolean?) GetValue(InteractionResultProperty);
            set => SetValue(InteractionResultProperty, value);
        }

        private static void OnInteractionResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((MyWpfWindow1) d).DialogResult = e.NewValue as Boolean?;
        }

I named my property InteractionResult though a good name would have also worked.

In the xaml right after the you can bind it with a style


 <Window.Style>
        <Style TargetType="{x:Type z:MyWpfWindow1}">
            <Setter Property="InteractionResult"
                    Value="{Binding UpdateResult}" />
        </Style>
    </Window.Style>

UpdateResult is the property in my viewmodel.

  private Boolean? _updateResult;

        public Boolean? UpdateResult
        {
            get => _updateResult;
            set => SetValue(ref _updateResult, value);
        }

The SetValue method is the usual notify property

 protected virtual Boolean SetValue<T>(ref T field, T value, 
            [CallerMemberName]String propertyName = null)
        {
            if (Equals(field, value))
                return false;

            field = value;

            RaisePropertyChanged(propertyName);
            return true;
        }

and the property gets set in the usual way

<Button Content="Cancel" 
        Command="{Binding CancelCommand}" />

ICommand CancelCommand { get; }


private void OnCancel()
{
   UpdateResult = false;
}

Disclaimer: works on my computer.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Ready Cent
  • 1,821
  • 3
  • 18
  • 25
  • Sorry for the newbie questions. But you use GetValue and SetValue when laying out defining a DependencyProperty but they are not defined. Then it appears a different SetValue is defined at the end of the post. Would you mind laying out the code for these methods? Thank you. – Kevin Burton Apr 24 '21 at 00:59
  • GetValue and SetValue are defined in DependencyObject which would the eventual base class of any visual type where a dependency property can be defined https://learn.microsoft.com/en-us/dotnet/api/system.windows.dependencyobject?view=net-5.0 – Ready Cent Sep 24 '21 at 02:55
0

Inspired by Chandrashekhar Joshi's answer (but not using the elements's name):

Define CommandParameter in Button:

<Button
  Command="{Binding CloseCommand}"
  CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
  Content="Close" />

Define Command (and Implementation):

CloseCommand = new DelegateCommand<Window>((w) => w.DialogResult = true);
BerndK
  • 1,045
  • 10
  • 14
  • 2
    Passing a UI control to the ViewModel goes against everything that MVVM stands for. A MVVM pure method of closing a window from the ViewModel is using an attached property - https://stackoverflow.com/a/3329467/967885 – Peregrine Jan 27 '21 at 12:07