Author Topic: WPF Binding is making me lose my mind  (Read 2315 times)

0 Members and 1 Guest are viewing this topic.

Jeff_M

  • King Gator
  • Posts: 4087
  • C3D user & customizer
WPF Binding is making me lose my mind
« on: March 12, 2023, 06:01:52 PM »
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#: [Select]
  1.         public string OutputString
  2.         {
  3.             get;
  4.             set;
  5.         }
  6.  
  7.         internal  void recalc()
  8.         {
  9.             if (parcel != null)
  10.                 OutputString = calcparcel();
  11.             else if (crv != null)
  12.                 OutputString = calcpoly();
  13.         }
  14.  
  15.  

In the ViewModel:
Code - C#: [Select]
  1.         public string TextToDisplay
  2.         {
  3.             get { return _model.OutputString; }
  4.             set
  5.             {
  6.                 _model.OutputString = value;
  7.                 OnPropertyChanged(nameof(TextToDisplay));
  8.             }
  9.         }
  10.  
  11.  

And in the UserControl XAML:
Code - XML: [Select]
  1.         <TextBox Grid.Row="2" x:Name="tb_Text" IsTabStop="False" Margin="5" TextWrapping="NoWrap"
  2.                 Text="{Binding TextToDisplay, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
  3.                 FontFamily="Microsoft Sans Serif"  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
  4.                 HorizontalAlignment="Stretch" VerticalAlignment="Stretch" IsReadOnly="True"/>
  5.  

Any idea why this isn't working?

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8661
  • AKA Daniel
Re: WPF Binding is making me lose my mind
« Reply #1 on: March 12, 2023, 07:50:20 PM »
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

  • King Gator
  • Posts: 4087
  • C3D user & customizer
Re: WPF Binding is making me lose my mind
« Reply #2 on: March 12, 2023, 10:31:50 PM »
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

  • Bull Frog
  • Posts: 348
Re: WPF Binding is making me lose my mind
« Reply #3 on: March 15, 2023, 10:08:09 AM »
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

  • King Gator
  • Posts: 4087
  • C3D user & customizer
Re: WPF Binding is making me lose my mind
« Reply #4 on: March 15, 2023, 12:48:40 PM »
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!

Jeff_M

  • King Gator
  • Posts: 4087
  • C3D user & customizer
Re: WPF Binding is making me lose my mind
« Reply #5 on: March 15, 2023, 08:45:06 PM »
Norman, I had to use your first suggestion. Number 2 throws an error at loading due to the Binding unable to use a read only property. I changed it to a OneWay binding and it never updates the textbox. It is working fine with the get/set TextToDisplay so I'm happy, just wondering why the second option isn't working.

n.yuan

  • Bull Frog
  • Posts: 348
Re: WPF Binding is making me lose my mind
« Reply #6 on: March 16, 2023, 11:43:53 AM »
I think that when the UI is loading your data MOdel (_model) is null, so, the readonly property for binding (in the option 2) actually should be:

public string TextToDisplay=>_model==null? "" : _model.OutputString;




Jeff H

  • Needs a day job
  • Posts: 6144
Re: WPF Binding is making me lose my mind
« Reply #7 on: March 16, 2023, 11:45:44 AM »
WPF use to piss me off also, but after doing some ASP web apps, and some node/express apps it clicked and then makes sense. Many of the documentation and examples just assume you have MVM MVVM experience.

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: WPF Binding is making me lose my mind
« Reply #8 on: April 03, 2023, 03:02:49 AM »
https://github.com/Fody/PropertyChanged Changed my WPF life. I use it always, for years, even donate monthly to them.