WPF - MVVM start action when view is opened - c#

I am writing a little piece of MVVM for training to wrap my head around the feature.
I have created a Model for my Image model class so that each Image item contains an ID, Name, Link and other stuff like height and width etc.
I have created a View as well to show the data. Nothing fancy. Just a simple ItemsControl that gets put inside my MainWindow Grid on startup.
I am now creating the ImageViewModel class which is where I am stumbling a little.
I have defined that class as such - not sure if I did that correctly:
public class ImageViewModel : Screen
I have also written some code that the software should perform to go get the data from the net and just parse stuff and retrieve a new Image item for each available new item on the net. the code should work fine as it worked perfectly when i coded this without the MVVM feature.
My issue is that i do not know how to make the action (called public void FindNewImages) launch the second the View is loaded inside my MainWindow grid on startup... How can I achieve this?

It depends on how you've wired up your view and view models. It sounds like you may be doing view first, where your view model is a resource of the view. In which case, you can invoke your FindNewImages method in the constructor of the view model.
I would however strongly recommend that you use an MVVM framework, such as Caliburn.Micro which provides screen life cycle. In this case, you could use a view model first approach, and invoke your method in the OnActivate method of the Screen type provided by Caliburn.Micro.

Related

Wpf MVVM: What exactly needs to be a view?

I'm new to WPF and MVVM and attempting to write a firmware programmer so I can update stuff via USB and save and upload setting/state data. MVVM seems like it could work for this. I currently have pages and can navigate around the app (although the nav service is in code behind for now) but I'm stuck on how to implement things that aren't in the standard 'customer'/'person' examples.
On a couple of pages, there are subsections that I can see being sub-divided into separate views hosted in the page, and these subsections are used more than once in the app.
For instance, I want to have a TextBlock that displays the connection status and updates based on signposts in the connection process, firmware update, backup, etc. Progress bars with the % are another. Sections that are used to display errors, data or a selection box depending on what happens connecting would be another.
Having a sub-section house 3 completely different outputs all stacked on top of one another and shown based on the situation seems messy. I can see that section being a ViewBox and creating a unique view for each case being a better solution there (and possibly the other examples above).
Or take the 'status display', I can see implementing it as it's own view and the page's view model will use a messenger to pass the current status back to the 'status display' view model. I can also see it all just handled by the page's view model via calls to it's own methods. I can also see potentially using a global model to hold the status strings (maybe an enum?) and the view model can be made to pull the correct string into a 'currentStatus' variable.
What is the proper way to approach this? Keep it all a single page? Subdivide the dynamic/changing parts from the static parts?
OP:
Obviously the pages themselves are views, but would it be best to have the 'Status:'display TextBlock and it's value, and the Error/selector section be views also?
If you are asking whether the status and error displays should be UserControls then yes they can be "a view" irrespective of whether the control is using a view model or not.
Incidentally, it is generally better to use DependencyPropertys instead of view models in a UserControl otherwise you will end up having duplicate properties in both the view (so that MainWindow can databind to it) and in your control's view model (purely for the benefit of the user control).
If your UserControl uses DependencyPropertys then both users of the control and your view can both databind to the same set of properties without duplication. In this way you will realise that UserControls have no need for a separate VM.
OP:
...the page's view model will use a messenger to pass the current status back to the 'status display' view model...
Don't do this, this is what data binding is for.

Dispose UserControl MVVM

So I am little bit stuck here, therefore I decided to ask the question in so. Maybe someone can clarify me on this and tell me what I am doing wrong...
Few words on the application:
Our architecture is based on mvvm light and unity.
All view model's implement IDisposable.
Datacontext binding is defined in app.xaml, therefore views are instantiated through corresponding view model.
View Models are instantiated through Unity.Resolve()
So far so good, now to the point I don't understand:
I have one user control with some elements inside, one of the elements has "LayoutUpdate" event declared which is handled in code behind (only for the test purposes). This calls some console output.
Afterwards I change the view (create a new view model instance) and set is as actual view in the content presenter (in main window view model). Prior to that I call dispose on the previous view model and all view models inside (recursively).
What I have noticed afterwards is that the console output still happens in the background although the view model is disposed and not visible to the user (screen).
I have tried everything to kill the usercontrol without success (unity deregister view model, etc.). Why this user control is still "alive"? Am I doing something wrong? Why this layout updated is still called although the control is not visible?
thanks help me understand this (problem).

Battleship in WPF using MVVM strucutre

I am completely new to WPF and MVVM, that's why I decided to start working on that. I am developing a Battleship game. If I create Canvas in my View, and create another canvas in ViewModel and bind them - everything works fine. However there is a problem. To begin with, I add Rectangles and TextBlocks to my Canvas to represent the grid. My "Field" Class (Model) is used in ViewModel for the logic to be done. And I want to have the property inside that class IsLegendHidden (bool). If I set that to true, then the method is called. In that method the linq looks for the textblocks related to my legend and then sets their visibility to hidden. It works fine, however having logic in the Model class is wrong in MVVM structure. But moving the algorithm to the ViewModel side will eliminate the ability to use class property "IsLegendHidden". But I want to have that property inside the model class. How can I solve that?
Your Model Class is free to implement INotifyPropertyChanged and it can notify the ViewModel when a particular property changes. This allows you to have a simpler model and get the logic done in the ViewModel.
You can also have a separate modeling of properties that are more focused on view concerns that the ViewModel can consult.
When using MVVM I like to try to separate things as much as possible. The model objects should just represent the concept they're modeling. The ViewModel is tied to a specific view so it will interpret or translate the model's properties into what is important to the view. You can have something on the Model that isn't 'IsLegendHidden', but that powers the ViewModel's 'IsLegendHidden' property.

Updating two labels in two different WPF Windows

I've recently built a Volley Scoreboard Application in Windows Forms that I'm trying to convert to WPF.
My first Window has all the controls to manage the game, while the second one is the window that will be projected on the big screen.
In Windows Forms I was able to update both labels (example, home_score on both Main and Projected) from a single point in code, but I don't seem to find a way to do this in WPF.
Any chance I'm missing something? Relatively new to WPF.
If it makes sense to have both pages (Views) utilizing the same View Model; which it sounds like it might given your use case, then the answer is simple:
Bind both labels (really, TextBlocks) to the same property. When the
property is updated; the binding engine will update both UIs.
If that isn't an option; the "master" View Model could invoke a method on the Model that would raise an event (ScoreUpdated). The "display" View Model would register for this event and update an appropriate property so that its View will pick up the change.
You need to:
to name the label (TextBlock Probably) in the xaml (Name="ScoreLabel")
keep a reference to the instance of the window.
after this:
otherWin.ScoreLabel.Text = "123";

How can control in the view get specific data from view model?

I have multiple of views (user controls), each with its own ViewModel. To navigate between them I am using buttons. Buttons display image and text from corresponding view model and also need column and row (because there are like 10 views: 10 columns with different number of rows each).
Right now buttons are created dynamically (I made a Navigator control for this) and for view models I have base class to hold text, image, column and row. Number of views available will be different (depends on user level and certain settings), that's why it's I need control here.
Question: how shall my control get data from view models?
Right now I have interface INavigator, defined in (lol) control itself. And view models implement it. I could go opposite, let my control to know about view models. Both looks wrong.
There is a single Navigator control what has, lets say, Items bound to a list of view models. It can cast each view model to INavigator or ViewModelBase (common for all pages) to obtain specific view model image, text, column and row. So either view model knows about control (to implement INavigator) or control knows about ViewModelBase.. And this is a problem, both solution bind tight control and view models, which is bad in mvvm.
Schematically
The way you've drawn your diagram answers your own question as to how you should structure the code for this.
What you need is one VM (let's call it MainVM) which contains an ObservableCollection<VMBase> of the other VMs (using your base type so that they can all happily live in the same collection).
Your View needs an ItemsControl (bound to your ObservableCollection<VMBase>) where you specify a DataTemplate for the Button using the properties exposed by the VMBase type only. Set the Command property in the Button to call SwitchCommand, CommandParameter is set to the item itself (i.e. {Binding .}).
Your View also needs a ContentControl bound to a SelectedVM property on MainVM which you can populate.
Implement SwitchCommand to set the SelectedVM property based on the value from the CommandParameter.
public void ExecuteSwitchCommand(object parameter)
{
var vmBase = parameter as VMBase;
if (vmBase != null)
SelectedVM = vmBase;
}
All properties mentioned here should be INotifyPropertyChanged enabled so that the View registers when they change and updates the UI.
To get the different UIs for the ContentControl, add type-specific DataTemplates for each of your specific VM types to the Resources file of your View (or if you're smart and are building a custom plug-in framework, merge the Resource Dictionaries).
A lot of people forget with MVVM that the whole point is that there is a purposeful separation of View from ViewModel, thus meaning you can potentially have many Views for a single ViewModel, which is what this demonstrates.
I find it's easiest to think of MVVM as a top-down approach... View knows about it's ViewModel, ViewModel knows about its Model, but Model does not know about its ViewModel and ViewModel does not know about its View.
I also find a View-first approach to development the easiest to work with, as UI development in XAML is static (has to be).
I think a lot of people get to wrapped up in 'making every component (M, V, VM) standalone and replaceable', myself included, but I've slowly come to the conclusion that is just counter-productive.
Technically, sure you could get very complicated and using IoC containers, create some ViewLocator object which binds a View-type to a ViewModel-type, but... what exactly does that gain you besides more confusion? It makes it honestly harder (because I've done this at one point) to develop because now you've lost design-time support first and foremost, among other things; and you're still either binding to a specific view model interface in your view or creating the binding at run-time. Why complicate it?
This article is a good read, and the first Note: explicitly talks about View vs. ViewModel. Hopefully, it will help you draw your own conclusions.
To directly answer your question, I think having your ViewModels implement an INavigator interface of some sort is probably ideal. Remember your VM is 'glue' between your view and model/business logic, its job is to transform business data into data that is consumable by your views, so it exists somewhere between both your UI and business layers.
This is why there are things like Messengers and View Services, which is where your navigator service on the ViewModels can fit in nicely.
I think the design has led to a no way out situation.
I believe that creating a custom button control where the dependency properties tie the image, the row and column actually provide a way for the page, which it resides on ,to get that information to them; whether they are dynamically created or not.
Continuing on with that thought. There is no MVVM logic applied to a custom control, the control contains what it needs to do its job and that is through the dependency properties as mentioned. Any functionality of the button should be done by commanding; all this makes the button data driven and robust enough to use in a MVVM methodology or not.
Question: how shall my control get data from view models?
There should only one viewmodel which is the page the control resides on. The control is simply bound to information which ultimately resides on that VM. How it gets there, that is up to the programmer. If the button is going to contain state data, that is bound from its dependency property in a two way fashion back to the item it is bound to.
By keeping VMs out of the buttons and only having one VM that is the best way to segregate and maintain the data. Unless I am really missing something here....
Same as others here I find it a bit hard to actually understand what you are asking, so this is quite general. The answer to the question header is simply: the Control gets the data from the ViewModel through bindings, always. You set the DataContext of your Control to the corresponding ViewModel, and from there you keep the ViewModel and the Control synchronized:
If you add an ItemsControl containing buttons to the View, you add an ObservableCollection<ButtonViewModel> to the ViewModel and bind the ItemsSource of the ItemsControl to this.
If you allow the user to dynamically add content to the View, the actual code that does it resides in the ViewModel, e.g. when the user clicks on a button "Add Button", you use the Command property to call a ViewModel method that adds a ButtonViewModel to the collection and the View will automatically reflect your changes.
There do exist complicated cases that are impossible to code exclusively in the ViewModel, I have found Behaviors to be the missing link there, but I'll get into that when you show me the specific case.
If you'd like to get a working example, please provide as much code as you can, with your exact expectations of what it should do.

Categories