1

I am planning to implement code that will use an IValueConverter to display units of measurement in either metric or imperial based on a boolean the user sets. The issue I am facing is that the data stored in my database is required to always be in metric. I'm using Entity Framework database first if that makes a difference.

So the situation I am wondering about is this: if a user has chosen to display data in imperial units, but then alters one of the fields, and saves it - how do I ensure that it is saved properly in metric? From what I've gathered by researching online it looks like that would be part of the converter's ConvertBack method? Right now I call Textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); to save the data, and I don't understand how to make that work since, as I understand, it just grabs the value in the TextBox and saves it. Am I right, or if there is a ConvertBack method will that be called in order to get the TextProperty?

Also, generally speaking, am I going about this the correct way (i.e. using an IValueConverter to alter the display)? To be honest I am in way over my head on this project, but I have deadlines fast approaching, and have a dire need to do this right. Any help would be greatly appreciated.

Thanks.

user3900520
  • 71
  • 1
  • 2
  • 7
  • You need to bind your textbox with Mode=TwoWay. Then your converter ConvertBack will be called when value from textbox is set at your model. – Evk Oct 02 '16 at 14:09
  • (http://stackoverflow.com/a/6424151/3955716) – Rom Oct 02 '16 at 14:13
  • Ok @Evk . Just so I'm sure I get it: if I set Mode=TwoWay then when I Call Textbox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); then it should save the result of the ConvertBack() converter? Sorry if I sound dense, I just want to be certain. Thanks for the help. – user3900520 Oct 02 '16 at 14:17
  • Thanks for the link @Rom – user3900520 Oct 02 '16 at 14:18
  • You don't need to call UpdateSource at all. With TwoWay binding, when you change text in your TextBox (on lost focus, or right away, see linked question) - it will update value in your model (through ConvertBack method of your converter, if there is converter). – Evk Oct 02 '16 at 14:22
  • Sorry for taking an hour to followup, had some things going on. What I'm concerned about is I need the value in the database to only be updated if the user hits save. So right now my binding is like this: {Binding MetricUnit, Mode=TwoWay, UpdateSourceTrigger=Explicit}. So I'm unsure of how the TwoWay mode will work with my explicit updatesourcetrigger + the converter. I think I might be fundamentally misunderstanding something @Evk – user3900520 Oct 02 '16 at 15:24
  • So when user hits save - all values in your model will already correspond to the values in textboxes. Why do you need to update them explicitly? – Evk Oct 02 '16 at 15:28
  • Like I said, I'm really out of my league here, shouldn't have taken on this project. So once again I really appreciate the help. Let me see if I get it - instead of doing the update like I am, I should be calling SaveChanges() on my database context or something? Is that what you mean by saving with the values from my model? The datastore that my control sees is an ICollectionView, and it doesn't have a way to save, so that's why I was saving the way I was. If there's a better way to do it please let me know. Once again, thanks for the help, sorry for being so ignorant. @Evk – user3900520 Oct 02 '16 at 15:42

1 Answers1

1

So you have some model, and you want to edit some properties of that model using TextBox and ValueConverter. Let's take some dummy value converter, which will multiply value by 10, and in ConvertBack will divide value by 10

public class MyConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        if (value == null || value == DependencyProperty.UnsetValue)
            return null;
        return (decimal) value*10;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
        if (value == null || value == DependencyProperty.UnsetValue)
            return 0;
        decimal d;
        if (decimal.TryParse((string) value, out d))
            return d/10;
        return 0;
    }
}

Now some test model

public class MyModel : INotifyPropertyChanged {
    private decimal _someValue;

    public decimal SomeValue
    {
        get { return _someValue; }
        set
        {
            if (value == _someValue) return;
            _someValue = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

And then you have your textbox

<TextBox Width="150" Height="20" Text="{Binding SomeValue, Mode=TwoWay, Converter={StaticResource myConverter}, UpdateSourceTrigger=PropertyChanged}" />

If you use UpdateSourceTrigger=PropertyChanged, on each user edit, the value will be passed to your ConvertBack method, and the result will be assigned to SomeValue of your model.

When user presses save button, you just save your model to database using whichever method you have for that. You don't need to explicitly update properties of your model, because they are already up-to-date. You can also use UpdateSourceTrigger=LostFocus (default one). Then whenever field loses focus - property in your model will be synchronized with it (again, through ConvertBack)

Evk
  • 98,527
  • 8
  • 141
  • 191
  • Ok. I think I get it now. For some reason I thought calling UpdateSource() was doing the actual saving to my database. I looked more closely, and that actually happens when I call DTE.ChangeTracker.DetectChanges(); DTE.SaveChanges(); Where DTE is my database context. I see now. Cool. The only thing stopping me from implementing exactly what you put there is my models don't implement INotifyPropertyChanged because I used database first. Might just have to bite the bullet and use CodeFirst. Either way, you've given me enough to point me in the right direction. – user3900520 Oct 02 '16 at 16:01
  • Thank you very very very much, @Evk. I really appreciate you taking the time to go over this with me in a detailed manner. You are awesome. I'm marking your answer as accepted. I hope you have an excellent day. – user3900520 Oct 02 '16 at 16:02