My main page has DevExpress TabPage control (similar to Xamarin Forms TabbedPage), it looks something like this:
<dxn:TabPage>
<dxn:TabPageItem>
<NavigationPage Title="Page1">
<x:Arguments>
<local:Page1/>
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Page2">
<x:Arguments>
<local:Page2/>
</x:Arguments>
</NavigationPage>
</dxn:TabPageItem>
</dxn:TabPage>
When I logout from my app I use navigation like this:
NavigationService.NavigateAsync( "/LoginPage" );
Destroy method in MainPageViewModel is called, but not called on any of child view models e.g Page1, Page2 etc. I've added IDestructible interface to each child page.
Is this a bug or by design or I'm doing something wrong?
In case of Xamarin.Forms the DestructibleRegionBehavior calls Destroy on views that are removed from a region. It does not consider child views. Consequently, the answer is yes, this is the current behavior and I guess it is by design, since Prism does not support every use-case. If you need this functionality, you could implement your own region behavior based on the original one. If your view hierarchy is fixed, it might even be easier to create a specialzed behavior. You could also consider creating a special region adapter for e.g. the TabPage if there is no need to reuse or generalize it.
The support for IDestructible in WPF was added with Prism 7.2.0.1367, as you can see in this pull request 1709. The previous statement applies to this behavior, too, it is essentially the same. You could extend it to traverse the child views using the VisualTreeHelper or another approach that fits your requirements.
Related
I want to create resuable components, so naturally I want bindings to pass from parent custom view to child and child of child custom views.
To achieve this I don't use MVVM pattern for custom views, but rather use
BindingContext = this
in my custom views constructors.
Of course I would implement BindableProperty for each property I want but oh boy was I surprised by BindableProperty not working at all!
All because of my "BindingContext = this".
Basically each time I have to write my bindings by hands and not by
<!-- Label in child view -->
<Label x:Name="InnerLabel" Text={Binding LabelText} />
I suspect I'm deeply mistaken somewhere, and there is an easier way to break views into smaller hierarchy of parent-child dependent views, without writing huge amount of boilerplate code like
InnerLabel.SetBinding(Label.TextProperty, new Binding(... etc etc etc
How to write reusable views properly in Xamarin Forms? Why my BindableProperty stopped working with BindingContext = this in my custom views constructors?
Generally speaking, any Forms element will inherit it's parent's BindingContext unless otherwise specified. By assigning BindingContext = this; you are breaking that built in inheritance.
As #Jason properly mentioned in the comment section, the problem with
BindingContext = this
inside custom views constructors.
In chase for easy way wanting to use {Binding CustomViewProp} I was breaking chain of parent -> child context, making BindableProperty properties to receive nothing from parent contexts.
THE PROPER WAY TO USE CUSTOM VIEW PROPERTIES IN XAML, example:
<ContentView x:Name="Self" ... ... ... all other initializing parent element stuff
<Label Text="{Binding CustomViewProp, Source={x:Reference Self}}" />
I'm still learning GWT, yet already have to face some kind of challenge for a work I have to do. Can't show any specific code so I'll try to explain it well.
Here's the situation: A certain class "Navigator" creates and save the Presenter instances of my architecture to allow reusing them. There is a method show() inside that same class that actually displays the view related but that system only works full screen by calling RootPanel.get().
What i'd like to do is showing that presenter instance's view inside of a flex panel element declared in a class myView (related to a class myPresenter) that basically uses Flex Panel to structure it's content.
To make it maybe more clear:
class myView{
...
flexPanel.setWidget(firstWIdget)
flexPanel.setWidget(secondWidget) //secondWidget to be replaced by a "thirdWidget"
...
}
I'd like the secondWidget to be replaced by another one, let's call it thirdWidget, that consists of a specific presenter instance's view.
To resume, I'd like my presenter instance's view to not go full screen but only occupy a certain area of the screen.
The displaying is managed almost entirely programmatically, means very limited use of css files and no use at all of xml ui files.
How can I manage this ?
Thanks
Use a SimplePanel as a container for your views returned by your Navigation class instead of adding them directly to root panel, and use that instance of SimplePanel where ever you want.
I am making a Panorama Windows Phone 8 app. This is the first time I have actually used one in an app.
I am having problems showing the data in runtime. Instead I am only seeing a list:
RuntimeOne
RuntimeTwo
RuntimeThree
etc..
I don't have a clue what has happened, it worked the other day. I am going into the SampleData folder and changing LineOne, LineTwo, LineThree, etc but it's not doing anything when I deploy the app to the Windows Phone Emulator.
What's happening is that there are two different sets of data, and the DataContext at runtime is different from design time.
The data that you see in design mode ('design one', 'design two') is
stored in MainViewModelSampleData.cs, so changing that doesn't affect
the runtime experience.
The data at runtime is coming from the LoadData method in
MainViewModel.cs
At the top of MainPage.xaml, you'll see
d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
and the "d" namespace here is a mnemonic for 'design'. If you look at the sample data XAML file, you'll notice it declares a class called MainViewModel with a collection of Items.
At runtime, MainViewModel.cs (specifically the LoadData method) adds items one by one to the Items property of the MainViewModel class, and that class is in turn set to be the runtime DataContext in the constructor of MainPage
The panorama control itself has markup like
<phone:LongListSelector Margin="0,0,-22,0" ItemsSource="{Binding Items}">
so it's expecting to see a collection called Items on whatever the current DataContext is, and the fact two different data contexts are in play explains what you're seeing.
The data binding magic is incredibly cool and powerful, but sometimes does leave you scratching your head.
After looking at some of the MVVM toolkits(MVVM Light, SimpleMVVM) out there the common theme seems to try to use as many blendable events as possible and not use codebehind.
I am not sure how to do all the events. For instance 2 events come to bind when doing windows phone 7.
OnNavigatedTo/From
On Load
Does anyone have examples on how to do this in MVVM? I am using SimpleMVVM but I would hope that the examples might be similar and maybe MVVM light toolkit or even just general MVVM tutorial showing this might help.
I only found ones that show how to do like the button click.
Edit
I am sort of confused on when to use code behind events or use blend events to commands.
For instance in the the MVVM Light tutorials they use MVVM for navigation but why is that better than using a codebehind event?
I am also kinda confused now when people say
Codebehind isn't evil; its the mix of business logic and codebehind
that is problematic. Let your UI handle UI tasks in your codebehind.
Well in the MVVM light examples they have "isbusy" in one of the examples where when the list or whatever it was(forgot) is loading a "loading sign comes up". This was all done in ViewModel and not a codebehind event.
So this seems kinda conflicting to me(maybe I am missing something). What also puzzles me is that if the ViewModel does not know anything about loading how do you know when the loading has started or finished?
As HighCore commented, use EventToCommand. It's pretty simple to use, although you'll need the Blend SDK first.
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4"
xmlns:im="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"
xmlns:ic="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
...
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<cmd:EventToCommand Command="{Binding GetTweetsCommand}" CommandParameter="Twitter" />
</i:EventTrigger>
</i:Interaction.Triggers>
Also, just a general point on the code-behind: having UI-related functionality in the code-behind isn't the worst thing in the world! It's separate from your ViewModel and it's in a logical place. However, I find behaviours easier to test. For example, using a class inheriting from TargetedTriggerAction allows a storyboard to be controlled by both property changes and controls:
public class ImageAnimationTrigger : TargetedTriggerAction<Storyboard>
{
protected override void Invoke(object parameter)
{
if (Target == null)
return;
if (parameter is DependencyPropertyChangedEventArgs)
{
DependencyPropertyChangedEventArgs args = (DependencyPropertyChangedEventArgs)parameter;
if ((bool)args.NewValue)
Target.Begin();
else
Target.Stop();
}
else if (parameter is RoutedEventArgs)
{
RoutedEventArgs args = (RoutedEventArgs)parameter;
if (!(args.OriginalSource as Button).IsEnabled)
Target.Begin();
else
Target.Stop();
}
}
}
I use this behaviour with a PropertyChangedTrigger as follows:
<i:Interaction.Triggers>
<ic:PropertyChangedTrigger Binding="{Binding Loading}">
<behav:ImageAnimationTrigger TargetName="animStoryboard" />
</ic:PropertyChangedTrigger>
</i:Interaction.Triggers>
As Laurent Bugnion says, use the code-behind if you need to!
1) Unfortunately I didn't find good examples so you are on your own.
2) if you keep your code-behind blank (by using commands instead of button click) you concentrate your code in ViewModel only. I can see two points of benefits:
a) in case you use one ViewModel for different Views (and have some common commands in several vies),
b) in case you use some private variables/methods of your ViewModel which are not available in the View.
3) You may use RaisePropertyChanged to load (reload) data, load data in ViewModel constructor to avoid using OnNavigatedTo or something else.
I have a region named "ActiveModule" and want to re-use it with different views, for example you press the search button and I show the search view in there, etc. etc.
The only way I can ATM do that is to deactivate all the active views in that region and then activate the view I like, this is a bit dirty, is there a "viewManager" or something similar I can use?
If your region is a ContentControl or derives from ContentControl, then there can only be one active view at a time, and you only need to activate the search view on the region.
Did you consider to use a type of contentControl that is able to show multiple views?
For example you can use a TabControl like this:
<TabControl Name="MainRegion" Regions:RegionManager.RegionName="MainRegion"/>
You can now add more than one view to the region. Use INavigationAware and IActiveAware interfaces from Prism to be able to do navigation on the views (activate them, find the correct view etc.).
If you are using a IRegionManager, you can remove all of the views whose types you recognize and then add your own.
foreach (var view in _regionsManager.Regions["MyRegion"].Views.ToArray())
{
if (view is MyType ||
view is MyOtherType)
_regionsManager.Regions["MyRegion"].Remove(view);
}
_regionsManager.AddToRegion("MyRegion", typeof(MyView));
Its by no means ideal, but it works. :)
To my knowledge what you are doing is the only way, theoretically in SCSF the top most view was activated by the framework. You could create ur own ViewManager or a ShowViewService equivalent to get this done. MAtter of fact, thats what i have done!
Not sure how you laid out your framework but if you are using navigation related framework you can simply call
regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri("your target view" + parameters, UriKind.Relative));
the above line will take care of deactivating other views in the region.
Otherwise if you do view discovery or view injection you can use the approach here
region.Activate(view);