I have a a PopUp with a Region that contains another Region. This popup is invoked through the WPF Prism(MEF) InteractionRequest methodology. The structure looks like so:
PopUpUserControl
- ContentControl : Region(UserCatalogsCreateRegion)
- PopUpStageUserControl
- StackPanel
-ContentControl : Region(UserCatalogsCreateStackRegion) <--Disappearing Region
The problem manifests itself like this. When the application starts up and is running normally, I can list the Regions in the application and I can see that the RegionManager contains the Region named "UserCatalogsCreateStackRegion".
Now when I click the button that sets off the InteractionRequest for PopUpCreation, I can see that the list of Regions no longer contains "UserCatalogsCreateStackRegion". I verified that something is removing my Region because I added a CollectionListener to the Regions property of the RegionManager, and as soon as the Popup is created, my breakpoint is hit and the Notif..Action is "Remove" and the OldItem is the Region in question.
TL;DR Region disappears from RegionManager.Regions when the popup that contains said Region is created and invoked.
Any help is greatly appreciated. And I will try to answer as many other questions as possible as there is A LOT that can go wrong with a Region manager.
EDIT
Brian Lagunas' links pointed right to the doggone solution. This was the solution. My final working code for the PopUpStageControl looks like this, where ContentControl is the Region that kept "disappearing":
[ImportingConstructor]
public PopUpStageUserControl(IRegionManager regionManager)
{
InitializeComponent();
this.regionManager = regionManager;
//Fix Begin
RegionManager.SetRegionName(ContentControl, AppRegions.UserCatalogsCreateStackRegion);
RegionManager.SetRegionManager(ContentControl, regionManager);
//Fix End
RegionManager.SetRegionManager(this, regionManager);
RegionManager.UpdateRegions();
}
This is because a popup is not part of the visual tree, so the region manager will not be able to find the region. You will have to manually register the region. See these posts:
Region not loaded by the RegionManger
How to register regions inside user controls or control templates in RegionManager?
PRISM 6 Regions in WPF using DataTemplate/CustomControl
https://github.com/PrismLibrary/Prism/issues/251
Going off of my comment and then a quick Google result (old version of PRISM).
The IRegionMemberLifetime interface: Note also that the ModuleARibbonTab class implements the IRegionMemberLifetime interface. This interface is provided by Prism, and it controls whether a View is removed from a region when the user navigates away from the View.
By the sounds of it you might want to implement IRegionMemberLifetime and set KeepAlive appropriately--that might have an effect on when the RegionManager removes/persists the region.
Although you do not stated it in your posted code, I assume that you are using something like this when setting up InteractionRequest:
<prism:PopupWindowAction.WindowContent>
<inf:PopUpStageUserControl/>
</prism:PopupWindowAction.WindowContent>
So you have to be aware that at runtime Prism will replace all popup content with the one you specified in PopupWindowAction.WindowContent.
Related
This answer can be in either Prism and/or Caliburn.Micro. Changing user control in the main shell can be done with something like this:
Prism
_regionManager.RequestNavigate("ContentRegion", uri);
Caliburn.Micro
ActivateItem(new ViewModel());
But my question is, how can you change the user control that is shown in the shellview upon clicking a button inside another user control?
You can use the exact same line (for Prism, at least) in any command anywhere. In case your're wondering how to get the _regionManager - just have it injected as constructor dependency.
I have a region within a region. Main region (which holds all the others) is named ContentRegion and the other one which I used to display partial info is named SettingsRegion.
Under my bootstrapper I have defined the following:
Prism.Regions.IRegionManager contentRegion = Container.TryResolve<Prism.Regions.IRegionManager>();
#region Register Multiple Regions
//contentRegion.RegisterViewWithRegion("ContentRegion", typeof(MainWindow));
contentRegion.RegisterViewWithRegion("SettingsRegion", typeof(SettingsView_MainPage));
#endregion
I can navigate from my ContentRegion to SettingsRegion without a problem like following:
_regionManager.RequestNavigate("ContentRegion", Experiences.Navigation.SettingsView_MainPage.ToString());
_regionManager.RequestNavigate("SettingsRegion", Experiences.NavigationSettings.SettingsView_ShiftSettings.ToString());
Everything renders great, I can see both views (contents) at the same time. However when I try to navigate back to main page like following
_regionManager.RequestNavigate("ContentRegion", Experiences.Navigation.MainPage.ToString());
It gives me the following error:
{"Region with the given name is already registered: SettingsRegion"}
I have read multiple articles regarding nested Regions, however I could not implement none. I should probably also mention that I am using Unity as well.
I am also attaching pictures so that my explanation is less confusing.
Main View / Main Window
Second view where I want to show some detail under the nested region
So in the end I had to do the following adjustments:
remove completely the following line from Bootstrapper (I don't understand why I don't need to register it).
contentRegion.RegisterViewWithRegion("SettingsRegion", typeof(SettingsView_MainPage));
Afterwards had to do slight change under my SecondPageViewModel; originally I was directly navigating to sub page under the constructor, but once I implemented INavigationAware and moved the navigation to OnNavigatedTo then it suddenly started working.
Pre-warning, I'm new to C# and XAML, but I'm really enjoying Windows 10 UWP apps. I've got a question on how to appropriately handle a SplitView.
I've got a Main Page, in which I have a SplitView control. In the SplitView Content, I've added a Frame for navigation to other pages. I want to add the Hamburger button on the child page to open the SplitView on the Main Page, but I can't access the SplitView control from the child page. How can I make the SplitView control accessible so that the hamburger button within the sub-page can open the SplitView pane?
The alternative is to add a header in the Main Page and have a static hamburger button there, but I don't like this option as it makes handling the text header content more difficult. Another is to copy the SplitView to each page. I don't want to do this either.
Any advice would be fantastic! Thank you.
I would highly recommend you take your alternative option of including the hamburger button in the main page. Users always expect it to be in the same location every time and changing this approach will probably result in a bad user experience.
You also don't want to be repeating code and thus you don't want to recreate the button on every page as well as any additional functionality like the open/close commands.
Rather than referencing elements from one page to another, a better practice is to keep things loosely coupled. This can be done with a messenger plugin which sends an event from one page to the other which can give it instructions on what you want to do. That way the other page only has to listen for the event instead of holding strong references. To streamline some of this process you could inherit from a base class which implements the messenger functionality.
That would provide a solution to your button and your header text situations but setting them up is out of the scope of this question. Depending on the size of you app and your goals, you might like to look into existing frameworks which helps in designing maintainable apps. A good Mvvm framework I would recommend checking out is MvvmCross which also cross platform and contains a messenger plugin.
Good luck with your app.
I found that solution :
In the MainPage, in your SplitView pane button method, add a SplitView reference as parameter in Navigate() :
private void SlitViewPaneButton_Tapped(object sender, TappedRoutedEventArgs e)
{
var frame = contentFrame;
Page page = frame?.Content as Page;
if (page?.GetType() != typeof(ChildPage))
{
frame.Navigate(typeof(ChildPage), SplitViewName);
}
}
In your ChildPage.xaml.cs :
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SplitView sv = new SplitView();
sv = e.Parameter as NavigateControls;
}
You can now do sv.IsPaneOpen = false, in your ChildFrame code.
Note : if you want to pass several Controls, create a Class with these Controls as variables, and use an instance as parameter.
As stated above, it is better to keep your hamburger button in your main page for a couple of reasons. One is the consistency mentioned above. Second, you would have to recreate the hamburger button in each of your content pages instead of just once in the MainPage.xaml. Additionally, keep in mind, there are different kinds of interactions with the SplitView menu in terms of how it pops in and out and how it is displayed, all listed below.
Inline – When the menu pane is opened, it pushes the content over. When it’s closed, the content goes back to its original location
Overlay – When the menu pane is opened, it lays on top of the content. When it’s closed, it is invisible.
Compact Overlay – When the menu pane is opened, it lays on top of the content. When it’s closed, the pane is still visible in Compact Mode.
Compact Inline – When the menu pane is opened, it pushes the content over. When it’s closed, the content goes back to its original position but the pane is still visible in Compact Mode.
You can also see a quick intro into the SplitView here.
http://jamesqquick.com/windows-10-splitview-intro/
In my MainPage.xaml, I created a Pivot Control: <controls:Pivot Title="Powder God" Name="PivotControl">.
My first pivot view is a HubTile that summarize all other individual pages. So my application bar will be different between the first pivot view and all other ones.
That's why I put my application bar in App.xaml's resource section, then load based on selected index of my pivot.
My question is:
In the application bar I will be using for all individual pages, I want to have a delete option, where I will remove that specific item (a view model) from my data context.
I know I can use PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; to access navigation services, but I don't know how can I reference to my pivot, so that I can get the selected index and proceed forward.
Thanks!
Using MVVM you SHOULDN'T do this:
((PageType)Application.Current.RootVisual).PivotControl. //Blah
PageType is whatever type PhoneApplicationFrame is that contains your PivotControl. If this doesn't work you need a Property in the RootVisual
PAGE
public Pivot MyPivot
{
get
{
return PivotControl;
}
}
APP
((PageType)RootVisual).MyPivot. //Blah
On one level Microsoft's suggestion of putting the ApplicationBar in App.xaml is great as it can be referenced from everywhere and would appear to encourage code reuse: however this question highlights the limit to this approach. An application bar is typically used to provide actions which are specific to the current page (or pivot item) and just because the buttons are the same you may not want the exact same code to run in each case.
In this case I think it would better to create a factory method that creates your common ApplicationBar with the click handlers you specify specific to your page/pivot item. For bonus points put the method in a new class (not App) so it doesn't get lost in all the boilerplate code there. Call this factory method in your page constructor and remember your ApplicationBar in your class. For multiple app bars, create them all up front and you can then easily switch between these app bars in your Pivot SelectionChanged code.
The alternative of creating the ApplicationBar in App.xaml and then retrieving this from the App.xaml.cs "Resources" ResourceDictionary in code, modifying the click callbacks, is more complicated in my opinion.
I wish they'd done a better job of implementing the ApplicationBar so people wouldn't want to do this. I've found that using the ApplicationBar forces you to add code to your Page.xaml.cs even if you use a framework like MVVM Light. This is still OK in MVVM as it's UI specific code that belongs in the View, but it makes things inconsistent if you're using ICommand everywhere else. Last time I decided it was better to create the entire ApplicationBar in code rather than hack this kind of thing via App.xaml.cs.
Update: There is a UserVoice request for a data bindable ApplicationBar.
I am working on windows application form. I have a CustomControl (say MasterControl) on which i put a split panel and now my MasterControl is split into three parts say:
Pannel1
Pannel2
Pannel3
Now i develop three custom controls and put one in each of pannels e.g
Pannel1 have CustomControl1
Pannel2 have CustomControl2
Pannel3 have CustomControl3
Now somewhere in CustomControl3 I need to access a public member of CustomControl1. For which i wrote the following code:
((MasterControl)this.Parent)._oCustomControl1.PublicMember = this.PublicMember;
The code above doen't work in my case. When this line of code is executed in debug mode then a message box appears and states that "There is no code available for current location"
It's a really bad design for your controls to depend on how are the arranged on the parent container.
e.g. inside your third control, you are quering the property of the first one by accessing it from the parent, and then it's child control by name.
Your code will break very easily, if it can be compiled at all - I think the problem you're having is the order of compilation: in order for your parent form to be compiled, it needs to have child user controls finished. On the other hand the user controls you created need to have finished form.
It would be far better to set whatever behaviour you're after from the container of those controls - for example, by reacting to events from the control, and setting appropriate stuff on appropriate other controls (there are other ways as well ofcourse - the point is in the direction and flow of information - who's setting and using what).
If you have a split panel in your master control, you should go two levels up to find your master control:
((MasterControl)this.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
I found the answer by myself. I am positing here because it might help some one else.
The exact code is:
((MasterControl)this.Parent.Parent.Parent)._oCustomControl1.PublicMember = this.PublicMember;
Basically my coustomcontrol3 lies inside a split container panel, so when i wrote:
this.Parent then it points to Panel In which it is residing and if i wrote
this.Parent.Parent then it points to the spliter container in which above panel reside and if i wrote
this.Parent.Parent.Parent then it points to control in which this split container resides
I got the idea from "Farzin Zaker" answer, so thanks to him for his contribution