InvalidOperationException raised when navigating back to a view - c#

I've a child view (SharedView) that's shared between two parent views so I add it to each parent view through a Region like this
<StackPanel>
<ContentControl cal:RegionManager.RegionName="SharedViewRegion" />
</StackPanel>
In the parent view's ViewModel I register the child view like this
regionManager.RegisterViewWithRegion("SharedViewRegion", typeof(SharedView));
When I run the application if I open only one of the parent views it works as expected but if I open the two parent views then I get the following exception
An exception occurred while creating a region with name
'SecondRegion'. The exception was: System.InvalidOperationException:
Specified element is already the logical child of another element.
Disconnect it first.
I've been googling and this is the closer solution I found to my problem InvalidOperationException occurs when the same view instance is added to multiple ContentControl regions
But I'm using the prism navigation feature so I'm instancing the parent view like this
regionManager.RequestNavigate("ModuleRegion", new Uri("ParentView1", UriKind.Relative));
Can someone help me to solve this?

Try doing the following:
Add a name to the ContentControl that is hosting the region
Now you have to remove the region's content before leaving the parent view so in the ViewModel add the following code to the OnNavigatedFrom method
public void OnNavigatedFrom(NavigationContext navigationContext)
{
ParentView.MyContentControl.Content = null;
}
Note: You can access the parent view importing it in your ViewModel.
Now you need to add the content to the region by hand because you removed it before leaving the region. Here's the code
public void OnNavigatedTo(NavigationContext navigationContext)
{
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
Note: In this method you must add some workaround because the first time you open this view you'll get a System.InvalidOperationException because PRISM will try to add the SharedView to MyContentControl.
Here's a possible workaround
bool isFirstTime = true;
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (isFirstTime)
{
isFirstTime = false;
return;
}
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
You have to do the same work in all parent views that share the SharedView
I hope this help you

You should directly add view to region instead of view discovery for this case. Give unique names to your parent views (you can use a counter to make up a unique name).
var region = this.regionManager.Regions["SharedViewRegion"];
var viewName = string.Format("SharedView{0}", this.countParent+1);
//Only add if it doesn't exist
var view = region.GetView(viewName);
if (null==view)
{
//Use container to get new instance of the parent view.
view = this.container.Resolve<SharedView>();
region.Add(view, name);
//Increment the counter as you added a new parent view
this.countParent++;
}
//Now activate the view
region.Activate(view);

Related

Passing values to generic list from one Blazor component to another

I have two variables within a generic list called RowData and it has 2 variables called RowPosition and RowInfo that would given data on the server side.
The generic list would be imported into a function that would build the form and use the data passed to it.
My question is what do I use on the server side to allow the data be passed? For example:
RowPosition : RowInfo
1 : blahyes
2 : blahno
3 : blahlol
4 : blahblah
You didn't post enough code for me to have much to go on as far as suggestions, but there are a few ways for components to talk to each other.
You can use a Cascading Value, and child components will be able to refer to the parent like this:
<CascadingValue Value="GalleryManager">
<ArtistListViewer></ArtistListViewer>
<ImageListViewer></ImageListViewer>
</CascadingValue>
Here my ArtistListViewer and ImageListViewer components can refer to the Gallery Manager with this property:
[CascadingParameter]
GalleryManager GalleryManager { get; set; }
If used too often this can lead to performance issues, depending on how busy your site is.
I wrote a blog post about another way:
Using Interfaces To Communicate Between Components
https://datajugglerblazor.blogspot.com/2020/01/how-to-use-interfaces-to-communicate.html
If you send anything complex, I prefer the method above where a parent object is a IBlazorComponentParent, and a child object is an IBlazorComponent.
Nuget Package: DataJuggler.Blazor.Components
Here is the way you can setup Parent property on the child like this:
<Login OnLogin="LoginComplete" Parent=this></Login>
The Parent=this is possible because my Login component is an IBlazorComponent, and my Index page is an IBlazorComponentParent.
The child setter registers with the Parent like this:
public IBlazorComponentParent Parent
{
get { return parent; }
set
{
// set the parent
parent = value;
// if the value for HasParent is true
if (HasParent)
{
// Register with the parent to receive messages from the parent
Parent.Register(this);
}
}
}
The Register event on the Index page stores the child as a property:
public void Register(IBlazorComponent component)
{
// If the component object exists
if (NullHelper.Exists(component, Children))
{
// If this is the Login component
if (component.Name == "Login")
{
// Set the Signup control
this.Login = component as Login;
}
// add this child
Children.Add(component);
}
}
At this point, my Login component knows about its parent, and the parent Index page has the Login component as a variable.
Then you can send a message with anything you like.
In my login complete method, I send a message to the parent:
// if the Parent exists
if (HasParent)
{
// create a message to send
Message message = new Message();
// Set the text
message.Text = loginResponse.Message;
// Send a message to the parent
Parent.ReceiveData(message);;
}
I am just passing in text above, but there is also a NamedParameters collection that you can pass any type of data (object).
There is a full working example and tutorial here if interested:
Code
https://github.com/DataJuggler/BlazorImageGallery
Video
https://youtu.be/3xKXJQ4qThQ

Navigate between nested View Controllers across a TabBarController - Xamarin iOS

I'm having trouble with navigation outside of the typical use cases for navigation controllers and tabbar controllers. Here's a drawing of a simplified version. (real version has a TabBarController that feeds into 9 NavControllers)
In my Home Nav Controller, there's a table view where the user could select a row and that should navigate them to a particular Events Detail View Controller. At that point, the user should be able to navigate back to the Events TableView Controller to see the list of events or use the TabBar to navigate to another section.
So far with my code I can push the correct detail view controller and select the correct index of the tabbar but it only works the first time:
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
var eventsDetailView = new EventsDetailController(eventPosts[indexPath.Row]);
//loop through ViewControllers array in case the user has set a custom tabbar order via the More Tab
foreach (UIViewController viewC in tbController.ViewControllers)
{
UINavigationController navController = viewC as UINavigationController;
if (navController.TabBarItem.Tag.Equals(9))
{
navController.PushViewController(eventsDetailView, false);
tbController.SelectedIndex = 9;
}
}
}
After the user has navigated to the events detail view controller, if they then:
travel to the Home ViewController via the TabBar
select a new TableRow to navigate to a different Events detail view controller
... then the same previous events detail view shows up. It doesn't matter which row the user selects in the Home View Controller. The initial detail view controller will always be the same one to be presented.
Any help pointing me in the right direction would be greatly appreciated!
Solution:
In my understanding, what you want is :
HomePageController --> EventDetailPage --> EvevtTableViewControll
So in the first step, you could push to EventDetailPage in HomePageController:
this.NavigationController.PushViewController(new HomeDetailUIViewController(), true);
Then, in your EventDetailPage, you can customize a back button, for example:
UIBarButtonItem backBtn = new UIBarButtonItem();
backBtn.Title = "back";
backBtn.Style = UIBarButtonItemStyle.Plain;
this.NavigationItem.LeftBarButtonItem = backBtn;
backBtn.Clicked += (sender, e) =>
{
// 1 here is an example, it should be the index of your EvevtTableViewControll
NavigationController.TabBarController.SelectedIndex = 1;
NavigationController.PopToRootViewController(true);
};
Set the NavigationController.TabBarController.SelectedIndex=1 first to make sure EvevtTableViewControll will show after you back from EvevtDetailViewControll, then your PopToRootViewController to back to the EvevtTableViewControll.
Try it and let me know if you have any problem.

Xamarin IOS navigate to another view controller without button on xamarin

i start using xamarin to create ios software.
in mainstoryboard, i navigate PageMain view controller to PageTo view controller.
I want navigate from PageMain view controller to PageTo view controller. i use this code and did not auto navigate:
var storyBoard = UIStoryboard.FromName ("MainStoryboard", null);
storyBoard.InstantiateViewController ("PageTo");
tried this one too but also not auto navigate :
PageMainViewController *viewController = segue. PageToViewController;
viewController.delegate = self;
tried this one too but also not auto navigate :
UIViewController pageto = new PageTo ();
pageto.Transition (PageMain, PageTo);
i know it, it easier use button to create push seque to PageTo view controller, but i did not want it.
please help me.
Another thing that you can do is push to another view controller...
AssignCaseController assignCaseController = this.Storyboard.InstantiateViewController("AssignCaseController") as AssignCaseController;
if (assignCaseController != null)
{
assignCaseController.CaseID = GetCurrentCaseID();
this.NavigationController.PushViewController(assignCaseController, true);
}
I hope this helps, I just had to do the same.
// Clear your notifications and other things
// Show the controller
// I am assuming your are using storyboards based in your use of Handle. I am also assuming the identifier in your storyboard for this is set to "WebViewController and its a custom subclass of UIViewController named WebViewController.
UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null); // Assume the name of your file is "MainStoryboard.storyboard"
var webController = Storyboard.InstantiateViewController("WebViewController") as WebViewController;
// ... set any data in webController, etc.
// Make it the root controller for example (the first line adds a paramater to it)
webController.CaseID = int.Parse(notification.UserInfo["ID"].ToString());
this.Window.RootViewController = webController;
// Note* the "Window" should already be set and created because the app is running. No need to remake it.

Retrieve the DataContext of the Content set by using ContentSource property

I need to get the DataContext of the View set by using ContentSource property of the ModernWindow, Could you please help.I am using MVVM framework with Modern UI. The ViewModel code from where I need to show another window is as follows,
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Owner = Application.Current.MainWindow;
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
// Here I need to get the DataContext of PromptWindow's Content
this.PromptWindow.Show();
}
I did some debugging and found that by inherting IContent interface from ModernUI in the 'OnNavigatedTo' event
public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e)
{
IPWPMainViewModel pwpMainViewModel = ObjectFactory.GetInstance<IPWPMainViewModel>();
pwpMainViewModel.PromptMainsCollection.Add(new ContentControl { Content = e.Content });
IPromptMainViewModel promptMainViewModel = ((UserControl)e.Content).DataContext as IPromptMainViewModel;
}
Here I am able to get the DataContext of the ModernWindow's Content i.e. of type 'IPromptMainViewModel' but here its very difficult to map/load the views into this ModernWindow as there are multiple instances of views, but I would like to do it in the ViewModel where 'ShowPrompt()' is present as there the Model will be associated with the View correctly so I can map there the views easily.
Thank you.
To get this done, I set the Content of the ModernWindow by myself (as shown in below code in a method in a ViewModel) without using the ContentSource DependencyProperty, If we use the ContentSource property it will be set for a ModernFrame type by the ModernWindow itself creating its Content instance after Navigation to that View completes in some method in ModernFrame class from ModernUI for WPF by using ModernFrame's Source DependencyProperty.
public void ShowPrompt()
{
this.PromptWindow = ObjectFactory.GetInstance<IPromptWindowViewModel>().Window as ModernWindow;
this.PromptWindow.Title = string.Concat("Control ", this.PromptOriginsEntity.PromptOriginsIdentity);
this.PromptWindow.Tag = this.PromptOriginsEntity.PromptOriginsIdentity;
this.PromptWindow.Owner = Application.Current.MainWindow;
// Store Window object in PromptWindowsCollection
this.PWPMainViewModel.PromptWindowsCollection.Add(this.PromptWindow);
this.PromptWindow.Show(); // inorder to retrieve the ModernFrame the ModernWindow is to be shown first
ModernFrame frameContent = (ModernFrame)this.PromptWindow.Template.FindName("ContentFrame", this.PromptWindow);
UserControl userControl = new UserControl { Content = GetView<IPromptViewModel>(), Tag = this.PromptOriginsEntity.PromptOriginsIdentity };
frameContent.Content = userControl;
this.PWPMainViewModel.PromptsCollection.Add(userControl);
IPromptViewModel promptViewModel = (IPromptViewModel)((IView)userControl.Content).DataContext;
promptViewModel.PromptEntity.Identity = this.PromptOriginsEntity.PromptOriginsIdentity;
}
I've uploaded a prototype app at https://wpfmvvmsamples.codeplex.com/SourceControl/latest
Thanks.

Close child window from parent viewmodel wpf mvvm

Close child Window after clicking ok/cancel.
I am invoking the child window from parent mvm model:
//parent view model
var optionSetViewModel = new OptionSetViewModel();
var optionSet = new OptionSet();
optionSet.SetViewModel(optionSetViewModel);
optionSet.SetOwner(_componentview);
optionSet.ShowDialog();
The code for ok and cancel for child Window is written in child view model:
private void OkClick()
{
_childWindow.Close();
}
It closing fine but after closing when it is returing to parent view model from where it is called it is throwing null reference exception. After close I am accesing the child window values:
_someText = optionSetViewModel.SomeText;
_noteText = optionSetViewModel.NoteText;
_optionsetLanguage = optionSetViewModel.OptionSetSelectedItem;
_optionsetselected = optionSetViewModel.OptionSetSelected.ToString();
With Catel (see http://www.catelproject.com/) you can close a view based on a view model. Thus closing a view means a call like this:
myChildViewModel.CloseViewModel();
To show a child window:
var myChildViewModel = new MyChildViewModel();
uiVisualizerService.Show(myChildViewModel);

Categories