How does WindowStartupLocation CenterOwner play with a view model? - c#

In a WPF MVVM application, you have view models that contain all the logic behind the view, and views that display things. Doing things is linked through commands, which are implemented in the view model class.
When I want to open a new dialog window at the center of the initiating window, I need to set WindowStartupLocation = CenterOwner as well as Owner = something to the new dialog window. Unfortunately, the Owner property must be set to a view, but the view is never known in a view model.
So how is that supposed to work together?
Wild guess: Do I have to bind the view's location and size to the view model and position the new dialog manually, not using the CenterOwner automatics?

but the view is never known in a view model.
Ideally the ViewModel would be totally unaware of the View(s) that use it. But as you found out that is not always practical.
It is therefore common to have a light coupling, for example as a View property in BaseViewModel.
But your VM should not use any specific Controls or other details from the View.

Related

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).

dynamic bottom of Dockpanel as function of chosen Ribbonpage using MVVM

I currently have a Ribboncontrol on top of my main window. On the Ribboncontrol there are several RibbonPages and depending on which RibbonPage is selected I want to display below the RibbonControl a different IDE Layout that suits the need of the chosen RibbonPage.
Is my approach correct that I create different views/viewmodels for each IDE layout and that with each different RibbonPage that is selected a corresponding view/viewmodel is perused? I like to use this approach because then I would not need to communicate between views/viewmodels because the functionality in each RibbonPage is self-contained.
Each view would be a UserControl that encapsulates a DockLayoutManager. I included some screenshots below (SS1 = MainWindow onto which I want to load different views; SS2 = the view that represent a UserControl that in turn represents a DockLayoutManager with all associated LayoutPanels, DocumentPanels, ....)
Question: How would I go about implementing that and is that a workable solution to display different views as a function of the chosen RibbonPage?
It is common to display different view pages that relate to different functions and or tabs of a RibbonControl. Typically you'd have a base view model class that all of your view models extend and a property of that type in your parent view model... let's call it YourViewModelProperty. To change the view, you'd just need to set a new view model to that property:
YourViewModelProperty = new SomeDerivedViewModel()
You can link each UserControl to its related view model in DataTemplates declared in App.xaml. In this way, they'll all be available to every view in the application. You can find out more information regarding this method in my answer to the WPF - automatic view resolving for view model question here on Stack Overflow.
UPDATE >>>
There is a much better explanation available in my answer to the WPF MVVM navigate views question.

Is there a way to share a ViewModel between a Page and a child user control?

I am working on a Silverlight navigation project. MainPage contains a navigation frame. Inside the navigation frame, my first page is called LiveView. LiveView.xaml contains a user control NavigationControl. I'm unsure if I am utilizing MVVM correctly here.
Let's say LiveView uses the view model LayoutsViewModel. The LayoutsViewModel applies to elements in both LiveView and the user control NavigationControl. How do I "share" this view model between LiveView and NavigationControl? Is that even the correct approach?
Currently, I set the LayoutsViewModel property of LiveView to an instance of LayoutsViewModel. Then, I set the DataContext of NavigationControl to this same instance of LayoutsviewModel, like so:
LayoutsViewModel = new LayoutsViewModel();
NavigationControl.DataContext = LayoutsViewModel;
This way, I can access the LayoutsViewModel in LiveView via this property and I can access the LayoutsViewModel in NavigationControl via DataContext as LayoutsViewModel. This seems wrong to me, but it works.
Is there a way to share a view model between a page and a child user control? Am I approaching this the wrong way?

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.

Datacontext of nested user controls

Background :-
I have a wpf view containing a combobox which gets populated by the view model using caliburn micro / ninject and mvvm pattern; this view also contains a stackpanel area. When the user selects the appropriate option from the combobox I insert the appropriate user control into the stackpanel presenting the user with a seemless transition to the related display.
Each "nested" user control which gets displayed in the stackpanel has it's own view model automatically associated by caliburn micro.
Problem :-
The "nested" user control bindings all try and refer back to the parent view model and not the view model associated with them specifically.
I can, initially, work around this by specifying :-
<UserControl.DataContext>
<vm:UserControlSpecificViewModel/>
</UserControl.DataContext>
but this requires a parameterless constructor in the view model but I need to be able to have paremeters passed to this view model so that Ninject can inject objects such as EventAggregator.
Going around in ciricles as I am fairly new to WPF so any help would be appreciated.
Thanks.
James.
One way to solve your problem could be to just initialize your view model in code behind and call the appropriate constructor. If you have a dislike of code behind in your WPF applications then I suppose you could possibly just bind your view model to the IEventAggregator object.
XAML:
<UserControl1 x:Name="myUserControl">
</UserControl>
Codebehind:
public MainWindow() // Constructor for window
{
InitializeContext();
MyViewModel vm = new MyViewModel(...);
myUserControl.DataContext = vm;
}

Categories