1
votes

I want to execute a method on TextChange event and for specific text, I want to do something and close the window using MVVM

for example on this part of my code I want to close my window:

if (text.Equals("12345"))
{
   //Exit from window
}

I know how can I do it from a button using a command, As I have in the code of the next example.

But how can I pass the window to a running property that binds to a text of the textbox?

or there is another way to close form?

I want to do it on the ViewModel and not the code behind to write my code as MVVM pattern close that I can

my XAML code :

<Window x:Class="PulserTesterMultipleHeads.UserControls.TestWindows"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PulserTesterMultipleHeads.UserControls"
        mc:Ignorable="d"
        Name="MainTestWindow"
        Title="TestWindows" Height="450" Width="800">
    <Grid>
        <StackPanel>

            <TextBox Text="{Binding Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>

            <Button Command="{Binding EndTestExit}"
                    CommandParameter="{Binding ElementName=MainTestWindow}">Exit</Button>
        </StackPanel>
    </Grid>
</Window>

the code behind XAML :

using PulserTesterMultipleHeads.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace PulserTesterMultipleHeads.UserControls
{
    /// <summary>
    /// Interaction logic for TestWindows.xaml
    /// </summary>
    public partial class TestWindows : Window
    {
        public TestWindows()
        {
            InitializeComponent();
            DataContext = new TestWindowsMV();
        }
    }
}

View Model code :

using PulserTester.ViewModel.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace PulserTesterMultipleHeads.Classes
{
    public class TestWindowsMV : INotifyPropertyChanged
    {
        private string text;
        public string Text {
            get {
                return text;
            } set {
                text = value;
                if (text.Equals("12345"))
                {
                    //Exit from window
                }

            }
        }


        /// <summary>
        ///  for the example
        /// </summary>
        private ICommand _EndTestExit;

        public ICommand EndTestExit
        {
            get
            {
                if (_EndTestExit == null)
                {
                    _EndTestExit = new GenericRelayCommand<Window>((window) => EndTestExitAction(window));
                }
                return _EndTestExit;
            }
        }

        private void EndTestExitAction(Window window)
        {
            window.Close();
        }


        public event PropertyChangedEventHandler PropertyChanged;
    }
}

************************* Edit as the answer **************************** The change in ModelView :

public string Text {
            get {
                return text;
            } set {
                text = value;
                if (text.Equals("12345"))
                {
                    CloseAction();
                }

            }
        }

The change in code behind:

public TestWindows()
        {
            InitializeComponent();
            DataContext = new TestWindowsMV();

            if (((TestWindowsMV)DataContext).CloseAction == null)
                ((TestWindowsMV)DataContext).CloseAction = new Action(this.Close);

        }
3
As workaround, you can make you close button invisible/disabled by default and enable it only when text in textbox meets your condition. Don't forget to fire propertychanged (currenly your Text property doesn't have it)Pavel Anikhouski
Is the window parameter variable? Why don't you set the window as a property within your View Model and just call EndTestExitAction(Window window) from the conditional in your setter?pseudorian
@PavelAnikhouski I want this hidden button to shot automatic when the right string is coming.. it is possible?user8417115
There is nothing against using the TextBox.TextChanged event to close your Window. On the other hand everything within MVVM is against having reference to your VIEW in the ViewModel. ViewModel should be separated and it should be working without a View. If you unit test your current ViewModel it will fail.Nawed Nabi Zada
@NawedNabiZada MVVM doesn't mean to separated my logic to my view? I have the login in my ViewModel and view on the windowuser8417115

3 Answers

1
votes

It is kind of hard with MVVM but what you can do is create an Event inside of your ViewModel and attach a function that will close your window in code behind of your View. Then you will be able to call your event inside of ViewModel whenever you need to close the window (or do something else that is more convenient to do in View rather than in ViewModel)

0
votes

The cleanest way to close a View from the ViewModel is to use an attached property

public static class perWindowHelper
{
    public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
        "CloseWindow",
        typeof(bool?),
        typeof(perWindowHelper),
        new PropertyMetadata(null, OnCloseWindowChanged));

    private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    {
        if (!(target is Window view))
            return;

        if (view.IsModal())
            view.DialogResult = args.NewValue as bool?;
        else
            view.Close();
    }

    public static void SetCloseWindow(Window target, bool? value)
    {
        target.SetValue(CloseWindowProperty, value);
    }


    public static bool IsModal(this Window window)
    {
        var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
        return fieldInfo != null && (bool)fieldInfo.GetValue(window);
    }
}

which you can then bind to an appropriate property in your ViewModel

<Window
    x:Class="...

    vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}">



private bool? _viewClosed;
public bool? ViewClosed
{
    get { return _viewClosed; }
    set { Set(nameof(ViewClosed), ref _viewClosed, value); }
}

More details on my recent blog post.

-1
votes

You have almost everything you need already implemented. All you really need to do is call private void EndTestExitAction(Window window) from your setter and supply window its value during construction:

public string Text {
            get {
                return text;
            } set {
                text = value;
                if (text.Equals("12345"))
                {
                    EndTestExitAction(window)
                }

            }
        }

where window is a property of your View Model.