In my application I use a ContentControl like this:
<ContentControl x:Name="Content">
<ContentControl.ContentTransitions>
<NavigationThemeTransition />
</ContentControl.ContentTransitions>
</ContentControl>
The problem is that the NavigationThemeTransition is not triggered when changing the Content property of the ContentControl. I think this is because it is only triggered in a Frame control when calling the Navigate() method.
I need this transition to be run when the Content of the ContentControl changes...
Is there a way to trigger the navigation-in and navigation-out animation manually?
Or is there some visual state to which the control can got to run the animation?
To achieve this You would be needed to have Custom Transition for the Control shown here custom transitions and for implementing Transition Effects in coding you can have Reference from here Using Page Transitions via Code
You could try to use a Frame control instead of a ContentControl. If that doesn't work - you'd need to create a custom control that has a Frame in its template and when its content change happens - it would navigate to a new page to display the new content.
I haven't seen a way to trigger the built-in transitions other than invoking an action that these transitions were created for. Personally - I would rather create my own transition than hack around to invoke the built-in one. You should be able to create one that looks exactly the same as the built-in one.
Related
I'm epxloring different ways to best show dialog windows in my application.
MahApp Metro's IDialogCoordinator seems quite useful, but I couldn't quite adjust it to my use case yet.
Say I'm creating a UserControl (view), whose ViewModel needs to be able to display dialogues.
These dialogues should, when displayed, overlay/span the UserControl only, NOT the entire Window in which the UserControl is hosted.
Is there any way to achieve this?
Default behavior always seems to span over the entire window, and I haven't found any way to change this yet.
So far, I've been using the Dialog coordinator in a very straightforward way, doing the following in my view:
<UserControl
xmlns:Dialog="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
Dialog:DialogParticipation.Register="{Binding}">
and set set the instance in my view's constructor by,
viewModel.Initialize(DialogCoordinator.Instance);
which I'd then call in the viewmodel via
IDialogCoordinator _DialogCoordinator; // set with viewModel.Initialize() called from the view
private async Task _SomeCmdExecute()
{
await _DialogCoordinator.ShowMessageAsync(this, "HEADER", "TEST");
}
Thanks!
Dialogs in MahApps.Metro are always at the window level (see the container PART_MetroActiveDialogContainer in the window's style.)
What you can do is changing the styling of dialogs, so they don't stretch horizontally accross the entire window. See the default template MetroDialogTemplate for reference.
I'm using a Hub to allow the user to drill down through hierarchical data. The Hub scrolls horizontally, and each HubSection contains a ListView of various items.
When a user taps an item, I add a new HubSection to the right. I would like the Hub to scroll to bring the new HubSection fully into view.
Doing this doesn't work:
myHub.Sections.Add(myNewHubSection);
myHub.ScrollToSection(myNewHubSection);
Adding a delay means it does ultimately scroll to the right position, so I suppose the problem is that the new HubSection hasn't rendered yet:
myHub.Sections.Add(myNewHubSection);
await Task.Delay(1000);
myHub.ScrollToSection(myNewHubSection);
I also tried measuring the HubSections and animating manually as detailed here, but that fails too for the same reason - the new HubSection still has zero size when it's just been added.
Is there a nicer way to do this? Animated would be much preferable.
Can you subscribe to myNewHubSection.Loaded and trigger the myHub.ScrollToSection() there?
You could try to manually call "UpdateLayout()" prior to calling scroll to section. This may force it to render and be able to scroll.
For the animation piece, this blog article has a good walkthrough:
http://blogs.u2u.be/diederik/post/2015/02/23/An-animated-ScrollToSection-for-the-Universal-Hub-Control.aspx
Alternatively, if you can somehow know the offset, you can use the ScrollViewerExtensions from the WinRTXamlToolkit. It also provides extensions for the AwaitableUI library that could be used to wait for the new Hub Section to load.
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'm working on an application, and I'm using the MVVM approach.
Basically, there are currently two Pages, and 1 MainWindow.
I switch between the pages using a Frame inside MainWindow.
In the main window, there are 2 buttons which are basically global and should show in all pages; x (exit) and settings.
This is basically my 'shell', as I decided to not use a window border.
The problem is I'd like each page to have a different background and this is where it gets complicated:
- Settings page: Grey background.
- Main Page: Rotating background color that changes according to a property.
The thing is the background is being set in the main window, because it should apply to the global area as well (the top, where the exit and settings buttons are).
I first set the background (in MainWindow) as bound to a property the represents the current page (the value is then being translated into a color hex code with the help of a converter).
All in all, this results in a case where the background changes when a page is changed, but not when the property inside MainPage changes. I can clearly understand why, but I have no idea how to solve it.
The possible solutions I came up with so far:
Somehow causing the binding in MainWindow to update/refresh when the property is changed in MainPage.
Changing the background manually from inside each of the pages. (Although doesn't it negate the idea of mvvm?)
Move the background into each of the pages and set it from there, while making the global buttons on top of the page (which could be a bad thing in case controls end up overlapping).
If so, what would be the best solution to this problem?
If you haven't already, I'd suggest you install some package via NuGet to make MVVM style development more enjoyable. I personally prefer MVVMLight which is... well, light, but it also packs lot's of helpful features.
To communicate between ViewModels, you have (at least) two possible approaches.
1) ViewModelLocator (not recommended)
ViewModelLocator is central place holding references to all of your viewmodels. You could add a property that is then used by all of the viewmodels to get/set the background.
....
x:Name="Main"
DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}">
....
<Grid Background="{Binding Background, Converter={StaticResource StringBrushConverter}}">
...
2) Messenger (recommended)
When ever property changes in your viewmodel(s) or method is executed, you could send a message that your MainViewModel is registered to listen to. Sending a message would be as easy as...
Messenger.Default.Send(new UpdateBackgroundMessage(new SolidColorBrush(Colors.Blue)));
And you'd register for this message in your MainViewModel's constructor:
Messenger.Default.Register<UpdateBackgroundMessage>(this, message =>
{
Background = message.Brush;
});
Actual message class would be:
public class UpdateBackgroundMessage : MessageBase
{
public UpdateBackgroundMessage(Brush brush)
{
Brush = brush;
}
public Brush Brush { get; set; }
}
I know I'm simplifying things here but I hope you got the idea. Both approaches are valid even if you decide not to use MVVMLight.
Edit:
Here's Git repo with example https://github.com/mikkoviitala/cross-viewmodel-communication
I think you should use Application Properties for storing background. There are various benefit of this :
1) Globally available
2) Easy to remember or store user preference
3) Automatically maintain separate profile for each user as it store values in AppData folder of user.
you can use Messenger to notify that background property has changed so that main window or shell could pull out new background value and update it.
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