I was working on a WPF project today that had a main nav window and then 4 pages that were loaded within this main window (using NavigationService.Nagivate...).. Within the XAML this created a lot of duplicate code so I wanted to refactor the menu into a user control that I could then bind to each page. I tried to create a class to handle the navigation and loading of each page but I discovered that NavigationServices is a sealed class and cannot be instantiated.
Would anyone please provide a suggestion/solution on how to create a usercontrol with a menu item that will allow the ability to navigate to new pages within the project. I have been able to do this within pages and the direct code behind but I have not had any luck trying to separate the two. If this is too vague please let me know and I will provide more deails with code samples.
Thanks in advance
Create a NavigationService dependency property on your user control. Then, when you instantiate your control, bind this property to the NavigationService of the container where the navigation should occur. For example, a page might display the user control like this:
<local:NavBox NavigationService="{Binding NavigationService, RelativeSource={RelativeSource AncestorType={x:Type NavigationWindow}}}" />
Now when your UserControl calls Navigate on its NavigationService, that will effectively call Navigate on the containing NavigationWindow's NavigationService. (This can be modified in the obvious way to support Frame or Page instead of NavigationWindow.)
Related
I am completely new to MVVM and I am creating an UWP app for keeping track of my software development, I am still learning.
So what I want to make is:
An app that contains single page ->
In MainPage.xaml I have something like this:
<!--MainPage Content-->
<Grid>
<!--For SearchBox-->
<AutoSuggestBox x:Name="SearchBox"/>
<!--For Adding Item-->
<AppBarButton x:Name="AddAppButton"/>
<!--Listview that contains main data-->
<ListView x:Name="AppsListView"/>
<!--This is DataTemplate of listview-->
<DataTemplate>
<Grid>
<!--Icon of App-->
<Image/>
<!--Name of App-->
<TextBlock/>
<!--For Editing Item-->
<AppBarButton/>
<!--For Deleting Item-->
<AppBarButton/>
</Grid>
</DataTemplate>
</Grid>
In Model I have something like this:
public class DevApp
{
public string name { get; set; } // For App Name
public string Iconsource { get; set; } // For App Icon
public ICommand EditCommand; // For Edit AppBarButton
public ICommand DeleteCommand; // For Delete AppBarButton
}
In ViewModel, something like :
public class ViewModel
{
// For ItemSource of ListView
public ObservableCollection<DevApp> DevApps = new ObservableCollection<DevApp>();
// For Add AppBarButton
public ICommand AddCommand;
}
Now this is me first time trying to create a neat and clean Mvvm app.
Now I have this question:
I know how to bind command to button or AppBarButton but how am I supposed to bind a Methods of a Xaml Control such as Listview's SelectionChanged() or AutoSuggestBox's TextChanged() Methods to ViewModel ?
How can I Load Data from save file ? As there is no InitializeComponent() in ViewModel like in CodeBehind to start from, where shall I pull LoadData() method which loads data to ListView ? ( my viewmodel is bind to view using <MainPage.DataContext> and I wanna keep code behind completely empty. )
Where shall I put Data class that can manage load save and edit data to savefile.
How shall I distribute responsibilities among classes ?
I have seen people using mvvm and they create files like:
services, helpers, contracts, behaviours, etc.
and I have seen same thing in Windows Community Toolkit Sample App
Is it required for Mvvm.
And what are services and helpers.
Shall I really use Mvvm for this ?
I tried using Mvvm in this just for curiosity but like
ITS BEEN 1 MONTH I AM MAKKING THIS APP! but it gets messed up again and again,
If I used Code Behind it would have been done in few days.
BY time now I realize that Mvvm is good at data bind in complex apps but
When it comes to simple things like a simple app with listview, I think code-behind
is better and it keeps things simple.
Please answer these questions I am really struggling in making this app.
I know how to bind command to button or AppBarButton but how am I supposed to bind a Methods of a Xaml Control such as Listview's SelectionChanged() or AutoSuggestBox's TextChanged() Methods to ViewModel
You could bind SelectionChanged with command by using Xaml Behavior InvokeCommandAction, or using x:bind markup extension to bind a method, for more please refer to this link.
How can I Load Data from save file ? As there is no InitializeComponent() in ViewModel like in CodeBehind to start from, where shall I pull LoadData() method which loads data to ListView ? ( my viewmodel is bind to view using <MainPage.DataContext> and I wanna keep code behind completely empty. )
Base on the first question, you could detect Page Loaded event and Invoke CommandAction where in the ViewModel. Then loading the file in the viewmodel LoadedCommand.
<i:Interaction.Behaviors>
<ic:EventTriggerBehavior EventName="Loaded">
<ic:InvokeCommandAction Command="{x:Bind ViewModel.LoadedCommand}" />
</ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
Where shall I put Data class that can manage load save and edit data to savefile
The better place that savefile is current app's local folder, and it have full access permission, please refer to Work with files document.
How shall I distribute responsibilities among classes ?
I have seen people using mvvm and they create files like:
services, helpers, contracts, behaviours, etc.
and I have seen same thing in Windows Community Toolkit Sample App Is it required for Mvvm. And what are services and helpers.
For mvvm design, model view viewmodel are necessary. And it is not necessary to make services, helpers, contracts, behaviours, it should base on your design. For example if you want to make NavigateService, you need make static service class to manager current app's navigation. We suggest you make sample project with TempleStudio that contains some base service and behaviors.
Shall I really use Mvvm for this ?
I tried using Mvvm in this just for curiosity but like
ITS BEEN 1 MONTH I AM MAKKING THIS APP! but it gets messed up again and again,
If I used Code Behind it would have been done in few days. BY time now I realize that Mvvm is good at data bind in complex apps but
When it comes to simple things like a simple app with listview, I think code-behind
is better and it keeps things simple.
Your understanding is correct, But Decoupling(mvvm) your code has many benefits, including:
Enabling an iterative, exploratory coding style. Change that is isolated is less risky and easier to experiment with.
Simplifying unit testing. Code units that are isolated from one another can be tested individually and outside of production environments.
Supporting team collaboration. Decoupled code that adheres to well-designed interfaces can be developed by separate individuals or teams, and integrated later.
Improving maintainability. Fixing bugs in decoupled code is less likely to cause regressions in other code.
In contrast with MVVM, an app with a more conventional "code-behind" structure typically uses data binding for display-only data, and responds to user input by directly handling events exposed by controls. The event handlers are implemented in code-behind files (such as MainPage.xaml.cs), and are often tightly coupled to the controls, typically containing code that manipulates the UI directly. This makes it difficult or impossible to replace a control without having to update the event handling code. With this architecture, code-behind files often accumulate code that isn't directly related to the UI, such as database-access code, which ends up being duplicated and modified for use with other pages.
I have problem how to implement sub-page navigation in UWP. The page is in RootFrame, which I can use on navigation. But i want to use something like this:
<Page>
<Grid>
<Frame x:Name="MyFrame"/>
</Grid>
</Page>
What I want is, use Navigate method of control MyFrame in ViewModel. I can call the method from code-behind, but I'm developing my app using MVVM. I'm not sure, if Template10 can work with sub-frames.
I appreciate any advice.
EDIT:
More details:
I have pivot control which is in page. the pivot has 2 tabs (pivotitems). The content of the pivotitem must be navigable. What I mean: I pivotitem 1, I need to have one Frame and use it for navigation in the pivotitem. My problem is, how to use or how to call the frame in pivotitem from ViewModel, especially I need to call Navigate method. Now I'm using Template10's navigation service and it's working with rootframe. I don't know, how to use it for other let's say sub-frames.
You can always do this.
var nav = Bootstrapper.NavigationServiceFactory(BackButton.Attach, ExistingContent.Exclude, this.Frame);
This will give you a navigation service for the frame in your page. You can then use session state, if you like.
Bootstapper.SessionState["MyNav"] = nav;
From here your view-model can access the service and navigate. You can repeat this for as many frames as you have. And you can then handle navigation in your view-model without consideration of "where" the frame is, just that your logic requires it to nav.
Does this make sense?
I don't know how you are going to trigger the navigation change so I'll assume it will start from a button click. I am also assuming the button's Command property is already bound to an ICommand in the viewmodel (the same concepts can be applied to different kinds of views).
All we have to do now is to make the ICommand implementation call our custom NavigationService to perform the content switch. This NavigationService class will be nothing but a simple proxy to the window global frame. Its main navigation method can be as simples as:
public void Switch()
{
var rootFrame = Window.Current.Content as Frame;
if ((rootFrame.Content as ParentPage) != null)
{
rootFrame.Navigate(typeof(ChildPage));
}
}
So you have tagged this with Template10 but it seems to be a more general question for UWP as a whole. I wonder if you have considered all of the inherent complexities with this approach - specifically related to suspension and resume. For each frame you have, you would need to save and restore navigation state, which isn't straight-forward when you have nested frames. Have you also considered how global navigation would work?
Template 10 does support the concept of multiple NavigationServices and, therefore, multiple frames, but only from the perspective of you can create them. Template10 does not inherently understand how such frames may be related to each other, so cannot perform automatic back propagation where you have something like:
FrameA[Main->Page1->Page1:Pivot1.FrameB[View1->View2->View3]]
Here we have two frames - FrameA and FrameB. FrameA has navigated from
Main to Page1. Page1 has a Pivot that hosts FrameB in PivotItem1 and
FrameB has navigated from View1 to View 2 and from View2 to View 3.
Global navigation (i.e. the shell back, etc.) would be automatically wired to FrameA, so you would need to intercept that action, and then handle you own navigation activity for FrameB.
Take a look at the BackButtonBehavior to see how it is possible to intercept the global back and then put in place your own action.
I don't know if you can do something like that..
One possible workaround is to use a Messenger that sends a message from your viewmodel to the view's code behind.. I'm not a fan of this solution though, because as I said before you have to use the page's code behind..
I am currently trying to design an application that loads viewmodels through MEF imports.
So far so good, I navigate from viewmodel to viewmodel, having loaded each vm datatemplate through dictionaries.
Each time I navigate, I modify the content of the main contentPresenter in my Shell (MainWindow).
One of the viewmodel allows me to display a WindowFormHost for an activeX control (such as acrobat reader for example). Since WindowFormHost does not allow binding, I created the windowFormHost in the viewmodel and binded it to a ContentPresenter in the view.
And here is where it fails : when coming back to the same viewmodel, the view is created again... throwing a “Element is already the child of another element.” error.
How can I prevent that ? Should I unload WindowFormHost when view is reloaded ? Or Can I keep view instances so that I keep only one instance for each view and let data binding update controls ? (It looks better for memory consumption).
Thanks for your help !
[EDIT]
Loaded dictionary :
<DataTemplate x:Shared="False" DataType="{x:Type vm:DAVPDC3DVIAControlViewModel}">
<vw:MyUserControl />
</DataTemplate>
View :
<DockPanel>
<ContentControl Name="WFH3DVia" Content="{Binding Path=Control3DVIA, Mode=OneWay} </ContentControl>"
<!--<WindowsFormsHost Name="WFH3DVia"></WindowsFormsHost>-->
</DockPanel>
VM (singleton, mef module) :
[Export(typeof(IDAVPDC3DVIAControl))]
public partial class DAVPDC3DVIAControlViewModel : ViewModelBase, IViewModel, IPartImportsSatisfiedNotification
VM (main window)
[Export]
public class MainWindowViewModel : ViewModelBase, IPartImportsSatisfiedNotification
// CurrentUC binds main widow view to controller active viewmodel
public IViewModel CurrentUC
{
get
{
return myAddinManager.CurrentVM;
}
}
Main view :
Controler (displays module on event) :
private void ModuleReadyEventAction(string iModuleName)
{
if (null != this.Modules && this.Modules.Count() > 0)
{
foreach (var item in Modules)
{
IBaseModule ibasemodule = item as IBaseModule;
if (null != ibasemodule)
{
Type tp = ibasemodule.GetType();
if (0 == tp.Name.CompareTo(iModuleName))
{
CurrentVM = ibasemodule.GetViewModel();
break;
}
}
}
}
}
I'm also working on a project in WPF using Prism v4 and MVVM (except I'm using Unity). I also have at least two controls that I need to use which are Windows Forms controls that must be hosted in a WindowsFormsHost. Let me explain my thoughts on the process..
It seems to me, that you are trying to avoid any code in your View's code behind. That's the only reason I can think of that you are moving your WindowsFormsHost into your ViewModel. I think that this is fundamentally the wrong approach. The WindowsFormsHost exists for the reason of displaying a graphical Windows Forms control. Therefore, it belongs in the view!
Now, I understand the appeal of DataBindings. Trust me, I've wanted to able to DataBind many parts of my WindowForms control. Of course, to accept a WPF data binding the property must be a dependency property on a dependency object. The easiest solution, which is not unreasonable, is to simply add the code to configure your windows forms control in the code behind for your view. Adding your UI logic into your ViewModel is an actual violation of the MVVM design pattern, while adding code behind is not. (And in some cases is the best approach)
I've seen possible hacks to try to get around this limitation. Including using "proxies" which inject a databinding, or perhaps extending WindowsFormsHost and adding DependencyProperties which wrap a specific hosted control's properties, or writing classes using reflection and trying to throw in windows forms bindings. However, nothing I've seen can solve the problem completely. For example, my windows forms control can contain other graphical components, and those components would need to support binding as well.
The simplest approach is to simply synchronize your view with your viewmodel in your view's code behind. Your view model can keep the file or document that is open, filename, title, etc., but leave the display and display related controls up to the View.
Last, let me comment more directly on your question. I would need to see how you are registering your View and ViewModel with the MEF Container and how you are navigating to understand why you are receiving that error. It would seem to me that either your view or view model is getting created more than once, while the other is not. Are these registered as singleton types? Regardless, I stand by what I said about not including the WindowsFormsHost in your ViewModel.
The project is based on PRISM.
I've got a main region in the TabControl in the Shell.
<!-- Main Region : A tab control -->
<TabControl prism:RegionManager.RegionName="MainRegion" >
</TabControl>
Then, I've got several Modules where all of them contains the same views. And must be added to the tab control region.
|_ModuleA
| |_ViewA
|
|_ModuleB
|_ViewB
Check please the image. In tab ModuleA, it shows in the left of the tab content ViewA, and the right is a generic view because all the tabs must show the same UI.
How can I do this?
I guess 'lecrank' already answered the question. There's no problem to add Region inside other Region or View. What I did in my project:
Every Module receive a copy of IRegionManager and IEventAggregator objects in constructor (I use MEF)
In Initialize() function every Module register it own Regions with it own View, etc, etc...
Whenever event or program flow should be passed to another Module (for example) then I just .Publish() predefined event (CompositePresentationEvent) with parameters.
The Shell itslef it's just empty template that defined just couple 'global' Regions (like TabControl in your scenario) and everything that displayed on it is coming from different Modules Views and not implemented in Shell class or even in Shell containing project.
So feel free to define and implement Regions and Views in another modules - just pass somehow the IRegionManager and IEventAggregator instances into Module class.
Your question is a bit odd, but if i'm not mistaken u should look for 'composite presenters'. A presenter that registers a new region to the regionmanager where other views can be shown. U can make a region locally scoped as well. For some more information see http://msdn.microsoft.com/en-us/magazine/cc785479.aspx
And a good place to find answers for Prism is on the compositewpf codeplex site, someone asking perhaps the same question: http://compositewpf.codeplex.com/discussions/273120
I need some help with overlaying views using the prism framework.Its a little more complexed than that so let me explain.I could be over-thinking this as well :D
i have shell (wpf window) and i have 2 views(A & B - both usercontrols) in a module.
when the shell loads it loads view A. On view A i have a button to "popup" view B
for some user input. so naturally i would think to some sort of modal window/control, maybe even a popup. however the problem i face with the popup is that when i move the shell the popup remains fixed and it doesnt block events in view A. I've tried disabling view A to stop events being fired and i've also tried to use a to get the view B move with the shell. Only the canvas works but i now need a way to block it tho'. Is there anyway i can overlay a view on top of another view with prism? or how does everyone else create modal popups with prism & wpf? any advise or pointers would be greatly appreciated.
If you want to use embedded dialogs without an extra window, you can use Prism's RegionManager to achieve the outlined behavior. The trick is to put the PopUp region parallel to your main region in the visual tree:
<Grid>
<ContentControl cal:RegionManager.RegionName="MainRegion" IsEnabled={Binding IsNoPopUpActive} />
<ContentControl cal:RegionManager.RegionName="PopUpRegion"/>
</Grid>
Now use the RegionManager to put view "A" into the "MainRegion". Create a controller class similar to IPopUpDialogController. It should be responsible for putting your view "B" (or any other PopUpView in your application) into the "PopUpRegion" on demand. Addtionally, it should control a flag that signal the underlying "MainRegion" to be enabled or disabled. This way a user won't be able to play with the controls in your view "A" until the pop up is closed.
This can even be done in a modal fashion by using ComponentDispatcher.PushModal() before pushing a frame onto the Dispatcher. However, I would recommend avoid modal dialogs.
Update: As requested in a comment, the IsNoPopUpActive could be implemented in the backing view model. There you could link it to RegionManager's View collection for the popup region:
public bool IsNoPopUpActive
{
get { return _regionManager.Regions["PopUpRegion"].Views.Count() == 0; }
}
Remember to trigger a PropertyChanged event as soon as you modify the views collection (add/remove a popup).
Just for your information: nowadays I avoid disabling the controls in the background and instead insert a transparent panel. This avoids clicking on background controls. However, this does not handle keyboard input (tab-ing to controls). To fix the keyboard input you need to make sure that the keyboard focus is trapped in the popup (MSDN on WPF Focus concepts).
Adding the following focus attributes to the popup region should do the trick:
KeyboardNavigation.DirectionalNavigation="None"
KeyboardNavigation.ControlTabNavigation="None"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.TabIndex="-1"
If you are using WPF + MVVM with Prism you can take a look at this Message View overlay controller. The nice part about this approach is you can write unit tests on you view model using a mock overlay controller and have the mock controller return the result that the user would choose in the overlay.
You can find it here: http://presentationlayer.wordpress.com/2011/05/24/wpf-overlay-message-view-controller/
Hope this helps