ATTENTION: You are viewing a page formatted for mobile devices; to view the full web page, click HERE.

Other Software > Developer's Corner

Wanted: Recommendations for learning WPF (with C#)

<< < (2/2)

I wasn't sure how far into reading the text tutorials I'd have to go before being ready for the MVVM concept, so I decided to watch the next two videos by AngelSix since they build upon each other and the last of which is a video introducing MVVM. In it, he writes the MVVM from scratch rather than using something like Caliburn.Micro, which is what Tim Corey uses and recommends.

C# WPF UI Tutorials: 03 - View Model MVVM Basics

It definitely seems a lot simpler to me than what I recall from the Tim Corey MVVM video which I watched in January. And maybe it's close enough to what you had in mind, wraith808, that it will save you the trouble of having to write up your example project on GitHub. :Thmbsup:

It looks a lot closer to what I do, though I do something different than using nameof(class) in order to not have to worry about naming errors that doesn't rely on intellisense.  This is an example of one of my viewmodels that I've written:

--- Code: C# ---public class ConfigurationViewModel: INotifyPropertyChanged    {        public ConfigurationSettingsModel Model { get; set; }         public ConfigurationViewModel(ConfigurationSettingsModel model)        {            this.Model = model;            // create commands            this.OkCommand = new RelayCommand(param => this.OkCommandExecute(), param => this.CanOk);            this.CancelCommand = new RelayCommand(param => this.CancelCommandExecute(), param => this.CanCancel);        }         #region Commands         #region OkCommand         public ICommand OkCommand        {            get;            internal set;        }         private bool CanOk        {            get            {                return this.ValidateSettings();            }        }         public void OkCommandExecute()        {            this.Model.SaveSettings();            this.DialogResult = true;        }         #endregion         #region CancelCommand         public ICommand CancelCommand        {            get;            internal set;        }         private bool CanCancel        {            get            {                return true;            }        }         public void CancelCommandExecute()        {            this.Model.RefreshData();            this.DialogResult = false;        }         #endregion         #endregion         #region Observable Properties         private bool? _DialogResult = null;        public bool? DialogResult        {            get            {                return this._DialogResult;            }            set            {                if (value != this._DialogResult)                {                    this._DialogResult = value;                    this.NotifyPropertyChanged(() => this.DialogResult);                }            }        }         #endregion         #region Helper Methods         private bool ValidateSettings()        {            return true;        }         #endregion          #region INotifyPropertyChanged Implementation         public event PropertyChangedEventHandler PropertyChanged;         protected virtual void OnPropertyChanged(string propertyName)        {            PropertyChangedEventHandler handler = this.PropertyChanged;            if (handler != null)            {                var e = new PropertyChangedEventArgs(propertyName);                handler(this, e);            }        }         protected void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)        {            var lambda = (LambdaExpression)property;            MemberExpression memberExpression;             if (lambda.Body is UnaryExpression)            {                var unaryExpression = (UnaryExpression)lambda.Body;                memberExpression = (MemberExpression)unaryExpression.Operand;            }            else                memberExpression = (MemberExpression)lambda.Body;             this.OnPropertyChanged(memberExpression.Member.Name);        }         #endregion

But I will say that guy does seem to break it down a lot better than the first video that I saw, and I think his methods would be worth following from what I saw.

I didn't notice what he did for a bootstrapper, but I use the simple expident of making use of a couple of properties of the app.xaml.

In the app.xaml, I set it up like so:

--- Code: C# ---<Application x:Class="ApplicationName.App"             xmlns=""             xmlns:x=""             Startup="Application_Startup"             SessionEnding="App_SessionEnding">    <Application.Resources>             </Application.Resources></Application>
Normally that startup is set to the first form- but this gives me a chance to go into the bootstrapper.

Then in the app.xaml.cs, I set up those two events.  The startup event is below.

--- Code: C# ---private void Application_Startup(object sender, StartupEventArgs e)        {            this.RequestClose = false;            this.SettingsConfiguration = this.GetConfiguration();            this.SettingsModel = new ConfigurationSettingsModel(this.SettingsConfiguration);             this.ViewModel = new MainViewModel(this.SettingsModel);            this.View = new MainView(this.ViewModel);             this.View.Closing += this.OnClosing;             this.ViewModel.RequestConfigurationDialog += new EventHandler(ViewModel_RequestConfigurationDialog);            this.ViewModel.RequestShowAboutDialog += new EventHandler(ViewModel_RequestShowAboutDialog);             // When the ViewModel asks to be closed, it closes the window via attached behaviour.            // We use this event to shut down the remaining parts of the application            this.ViewModel.RequestClose += delegate            {                // Make sure close down event is processed only once                if (this.RequestClose == false)                {                    this.RequestClose = true;                     // Save session data and close application                    this.OnClosed(this.ViewModel, this.View);                }            };             this.InitializeViewCommandBindings();  // Initialize RoutedCommand bindings            this.View.Show();         }         private void App_SessionEnding(object sender, SessionEndingCancelEventArgs e)        {            e.Cancel = this.OnSessionEnding();        }         private bool OnSessionEnding()        {            if (this.ViewModel != null)            {                if (this.ViewModel.CanClose == false)                {                    MessageBox.Show("Application is not ready to exit.",                                    "Cannot exit application", MessageBoxButton.OK);                     return !this.ViewModel.CanClose; // Cancel close down request if ViewModel is not ready, yet                }                 return !this.ViewModel.CanClose; // Cancel close down request if ViewModel is not ready, yet            }             return true;        }         private void OnClosed(MainViewModel viewModel, MainView view)        {            // Persist data here            this.SettingsModel.SaveSettings();             Application.Current.Shutdown();        }
There's a bit more to it- but I thought that knowing how the bootstrapper is set up and an alternate way to hook up the INotifyPropertyChanged might be of benefit to perhaps encapsulate in what he refers to in the video.

Despite my initial request for text-based tutorials rather than video tutorials, I've actually spent quite a number of hours watching and/or following along with WPF/MVVM tutorial videos. My most recent find is a decent set of video tutorials by Tosker.

Here's one of his playlists that go over the basics, including MVVM: C# WPF Tutorials for Beginners

(Unfortunately, his early videos suffer from very low volume, which I worked around by downloading the videos, pulling out the audio, increasing its volume, then re-saving the videos with the louder audio. Many thanks to 4wd for instructions on how to accomplish this!)

But despite my many hours of exposure to seeing it in action, I can't help but keep asking myself, "Why use MVVM?" I admit that I don't fully understand how to properly use MVVM since there's so much new to me in all this, including XAML, WPF, and the MVVM pattern itself. But from what I've gleaned, using MVVM just seems like a ton of extra boilerplate and duplicate code, making even trivial things tedious and time consuming.

So I searched the internet and found that I'm far from the first person with the same question.

And the top answer to that question is really great. Both in explaining that, for trivial cases, using MVVM (or any design pattern) is unnecessary and only adds complication, and also in explaining what benefits MVVM might have on a project over the course of its lifetime.

And of course, along the way I also found more responses talking about "the Jason Dolinger MVVM video," which is something I came across early on in my searches for information but for one reason or another (probably the length) ended up putting off watching until now. So now I'm working on the Jason Dolinger video, trying to gain a better appreciation for how and why to use MVVM.

I'm looking for recommendations on good resources for learning how to use WPF to make GUI applications. Free is preferred. And I think I'd prefer the information in written/image format (e.g., a book or website) rather than video format.
-Deozaan (July 21, 2020, 08:00 PM)
--- End quote ---

Since it comes from Microsoft I would recommend to start there   :D
Introduction to WPF from Microsoft Docs.
Hope it help you.

And the top answer to that question is really great. Both in explaining that, for trivial cases, using MVVM (or any design pattern) is unnecessary and only adds complication, and also in explaining what benefits MVVM might have on a project over the course of its lifetime.
-Deozaan (September 27, 2020, 07:44 PM)
--- End quote ---

I find that true, but also true is the fact that the more that you use it (even on smaller projects) the more proficient that you become.  And if that small project happens to become large (many of my NANY projects suffer from this) you're not tied into the old way of doing things.  I still need to get you that example of the framework that I created and used- I'll try to get to that this week!


[0] Message Index

[*] Previous page

Go to full version