Code Red > .NET

WPF Binding is making me lose my mind

(1/2) > >>

Jeff_M:
It has taken a while for me to grasp the WPF MVVM process but I finally have converted an old WinForm control to a WPF UserControl. Well, mostly converted. The control is on a PaletteSet and has 12 controls all used with Binding back to the ViewModel and all business logic is in a Model. I had actually started this process a few years ago but got overwhelmed with the different process from what I'd been using. Last week I decided to tackle this again and started from scratch. As of today 11 of the data bound controls are working perfectly. I can open Acad, load the assemblies, issue the command and the palette comes up populated just as I expect. Part of the command needs the user to select a single entity, this is handled in the Model and works just fine, once a selection is made a calculation is done and a property is set with the results and the user can select a new object. The Model property is referenced by a Property in the ViewModel which is bound to a TextBox in the View, except the view never updates with the new text for the textbox. I know that the binding works at startup since the property in the Model is set with text to advise the user to select an object, and this is displayed in the textbox. I know that the Model property successfully updates with the calculation values because I can see it update during Debug stepping. But the ViewModel property never gets notified of the changed Model property.

In the Model:

--- Code - C#: ---        public string OutputString         {             get;             set;         }         internal  void recalc()        {            if (parcel != null)                OutputString = calcparcel();            else if (crv != null)                OutputString = calcpoly();        }  
In the ViewModel:

--- Code - C#: ---        public string TextToDisplay        {            get { return _model.OutputString; }            set            {                _model.OutputString = value;                OnPropertyChanged(nameof(TextToDisplay));            }        }  
And in the UserControl XAML:

--- Code - XML: ---        <TextBox Grid.Row="2" x:Name="tb_Text" IsTabStop="False" Margin="5" TextWrapping="NoWrap"                 Text="{Binding TextToDisplay, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"                 FontFamily="Microsoft Sans Serif"  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"                 HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsReadOnly="True"/> 
Any idea why this isn't working?

It's Alive!:
if WPF makes you lose your mind and WinForms makes you go blind.
MFC will certainly push you over the edge  :laugh:

after a few years of UI coding  :lol:

Jeff_M:
Well that's a comforting thought, Daniel!  :crazy2:

I found this and implemented the PropertyChanged suggestion.
https://stackoverflow.com/questions/15439841/mvvm-in-wpf-how-to-alert-viewmodel-of-changes-in-model-or-should-i

This is working, for now...

n.yuan:
If you implement INotifyPropertyChanged to your Model's property, you effectively makes the Model plays the role of ViewModel. In this case, the Model wears 2 hats: while it holds business data logic, it would also works as "indirect" sub ViewModel, causing ViewModel changes with its property change.

If you want to keep clean boundary between Model and ViewModel, the logic would like:

1. user select object, so the text data is obtained to set the Model property
2. Then you need to somehow trigger OnPropertyChanged("TextToDisplay) for View to be notified the change. This is where your original code did not do.

With your original code, if you placed a break point at TextToDisplay's "set" accressor, you would have noticed the setter is never called (thus the View was not updated).

There are 2 ways to do it that are easier that implement INotifyPropertyChanged to the Model's properties (well, it might be right solution in right scenarios):

1. You do not mix the Model and the ViewModel in the ViewModel's property:

private string _textToDisplay
public string TextToDisplay
{
  get=>_textToDisplay;
  set
  {
     _textToDisplay=value;
     OnPropertyChanged(nameof(TextToDisplay));
  }
}

In this case, the code for the entity pick process would be like (code in a method of the ViewModel):

var pickedText=PickEntity();
if (!string.IsNullOrEmpty(pickedText) _model.OutputString=pickedText; //Model is updated
TextToDisplay=_model.OutputString; // Update the view

2. Or, you can simply do this:

// readonly ViewModelProperty
public string TextToDisplay=>_model.OutputString;

Then in the picking process, you do:

var pickedText=PickEntity();
if (!string.IsNullOrEmpty(pickedText) _model.OutputString=pickedText; //Model is updated
OnPropertyChanged(nameof(TextToDisplay); //Update view

this way, you omit the need of a private member _textToDisplay.

Jeff_M:
Thank you, Norman. For me to implement your suggestion 2 will require another substantial rewrite (I'm converting old code which all resided in the class with the CommandMethod and had numerous references to the actual form control properties. This was one of my first .NET projects and was poorly written...but it functioned well so I never revisited the code.) Just getting it to where it is was a major challenge, taking the next step to get the selection portion into the viewmodel was something I had considered. I guess it is the smart thing to do based on your input.

Thanks again, Jeff <<<----still have a lot to learn!

Navigation

[0] Message Index

[#] Next page

Go to full version