0
votes

Warning -- I am a newb when it comes to WPF. So what I am trying to do is have a textbox that is bound to a string property in my ViewModel. When the user clears the textbox, I want it to automatically go back to what the user had initially (when window opened). Basically I am preventing the user from clearing the textbox.

Currently, my WPF has the binding as TwoWay and and I do have the UpdateSourceTrigger set to PropertyChanged. I think I want to keep that UpdateSourceTrigger because I like the property in my ViewModel to get updated when the user makes one little change. That way I can do other UI stuff when the user does something (ex - update my Save button because the user changed something).

My property in my ViewModel looks like this currently, with me attempting to use the original value:

public string SourceName
{
    get { return this.sourceName; }
    set
    {
        if (!this.sourceName.Equals(value, StringComparison.OrdinalIgnoreCase))
        {
            if (!string.IsNullOrWhiteSpace(value))
                this.sourceName = value;
            else
                this.sourceName = this.configuredSource.Name;

            RaisePropertyChanged("SourceName");
        }
    }
}

The problem I am having is that I think the View is ignoring my 'RaisePropertyChanged' because of the UpdateSourceTrigger I have set. If I take out the trigger, then this works, but I have to lose focus on the control to have the UI update. Hence why I want to keep the trigger if I can.

Anyone have a nice way to revert back to the original value if the user clears the textbox?

2
Can you post the related XAML?CodingGorilla

2 Answers

0
votes

The problem is not the UpdateSourceTrigger so much as the fact that, as far as WPF is concerned, the value it passes to your property is the value it will take on. It ignores the property changed event you raise to avoid a recursive cycle.

You need to raise the event in a separate message, like this:

public string SourceName
{
    get { return this.sourceName; }
    set
    {
        if (!this.sourceName.Equals(value, StringComparison.OrdinalIgnoreCase))
        {
            if (!string.IsNullOrWhiteSpace(value))
            {
                this.sourceName = value;
                RaisePropertyChanged("SourceName");
            }
            else
            {
                this.sourceName = this.configuredSource.Name;
                this.dispatcherService.BeginInvoke(() => this.RaisePropertyChanged("SourceName"));
            }
        }
    }
}

This is kind of pseudo-code because there is no standard way to get access to the Dispatcher from your VM (unless your MVVM framework provides one). I normally wrap it in a service with a nice interface for invoking synchronously and asynchronously, as shown above.

Anyway, the point is that WPF will get the event this time, because it's in a separate message. Consequently, the UI will reflect your change.

0
votes

I got it to work. Solution was to have my property be 'dumber' and allow it to be set to empty.

public string SourceName
{
    get { return this.sourceName; }
    set
    {
        if (!this.sourceName.Equals(value, StringComparison.OrdinalIgnoreCase))
        {
            this.sourceName = value;
            RaisePropertyChanged("SourceName");
        }
    }
}

Then I would have a RelayCommand (MVVM Light) that would get fired whenever the focus was lost on the textbox.

public RelayCommand SourceNameLostFocusCommand
{
    get 
    { 
        return new RelayCommand(() => 
            {
                if (string.IsNullOrWhiteSpace(this.SourceName))
                    this.SourceName = this.configuredSource.Title;
            }); 
    }
}

Here is a snippet of my xaml to get the RelayCommand to fire on lose focus:

xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<TextBox Text="{Binding Path=SourceName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="LostFocus">
         <cmd:EventToCommand Command="{Binding SourceNameLostFocusCommand, Mode=OneWay}" />
      </i:EventTrigger>
   </i:Interaction.Triggers>
</TextBox>