Propagate Events to UserControlls view model.

May 18, 2011 at 2:38 PM

Is it possible to receive OnInitialize() in UserControll viewmodel that is part (child) of Page?

May 18, 2011 at 5:45 PM

Could you please elaborate your question a little more? I don't quite understand your exact need.
Every VM implementing IActivate or inheriting from Screen gets called on initialization by its parent Conductor.
Do you want to handle the initialization in the VM or in the view?

May 18, 2011 at 8:22 PM

Could you check my example with AllActive Conductor:

 

MainPageViewModel impelements Conductor<IScreen>.Collection.AllActive other viewmodels are Screens.

 

public MainPageViewModel( FirstViewModel first, SecondViewModel second, ThirdViewModel third){

Items.Add(first);

Items.Add(second);

Items.Add(third);

}

May 19, 2011 at 10:44 AM

Sorry, I understand the structure of your VM, but I'm still failing to get where (and why) do you need to handle the initialization.

FirstViewModel, SecondViewModel and ThirdViewModel could handle their own initialization overriding OnInitialize method.
Please note that the initialization only occurs the first time each screen is activated.
Being your conductor a Conductor<IScreen>.Collection.AllActive, it should activate ALL the child screens when IT gets activated, and NOT when the children are added to the collection.

Does it help?

May 19, 2011 at 10:47 AM

I'm only wondering if i properly wire everything up in "Parent" view by adding "Children" views to Items collection in constructor.

May 19, 2011 at 11:57 AM

As for children VM lifecycle management, your approach is correct.
To also have the corresponding views displayed in the parent view, you have to put an appropriate "hosting element".
That is, usually, an ItemsControl (or similar) bound to conductor's Items proprerty:

<!-- in MainPage -->
...
<ItemsControl Name="Items" />
...

Note that correct binding and template are automatically created by CM, since the element name matches the name of the property.

May 19, 2011 at 12:49 PM

When i put Name="Items" then text from Header attribute is not shown.

	<controls:Panorama Title="Title" x:Name="Items">
            <controls:PanoramaItem x:Name="First" Header="first" />
            <controls:PanoramaItem x:Name="Second" Header="second" />
            <controls:PanoramaItem x:Name="Third" Header="third" />
        </controls:Panorama>

Do i have to manually set DisplayName for each of them?

May 19, 2011 at 4:02 PM

Sorry, I forgot to mention that you need to add a specific convention for the Panorama, otherwise you have to manually bind it.
Call this code in the Configure override of your bootstrapper: 

ConventionManager.AddElementConvention<Panorama>(Panorama.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
				(viewModelType, path, property, element, convention) =>
				{
					ConventionManager
						.GetElementConvention(typeof(ItemsControl))
						.ApplyBinding(viewModelType, path, property, element, convention);
					ConventionManager
						.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
					ConventionManager
						.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
				};

After that, the CM conventions will take care of binding the Header content with the DisplayName proprerty. If you want to use another value, you should be able to provide a custom data template for Header.
BTW, if the items of the Panorama are generated through binding, you should not add hardcoded PanoramaItem-s in Xaml, they will be generated by the Panorama itself.

May 19, 2011 at 4:14 PM

So i have to remove

    <controls:PanoramaItem x:Name="First" Header="first" />
    <controls:PanoramaItem x:Name="Second" Header="second" />
    <controls:PanoramaItem x:Name="Third" Header="third" />
?

But then I have to specify DisplayName on "code" not view side.

May 19, 2011 at 6:10 PM

I somewhat understand your complaint, but using MVVM with model-first approach the UI is very often data-driven.
If you feel like using exactly the three fixed children, you may try something along this path:

 

class MainPageViewModel {
	
	public FirstViewModel First {get;set;}	 
	public SecondViewModel Second {get;set;}	 
	public ThirdViewModel Third {get;set;}	 
	
	public MainPageViewModel( FirstViewModel first, SecondViewModel second, ThirdViewModel third){
		Items.Add(first); First = first;
		Items.Add(second); Second = second;
		Items.Add(third); Third = third;
	}

}
        <controls:Panorama Title="Title" >
            <controls:PanoramaItem x:Name="First" Header="First" />
            <controls:PanoramaItem x:Name="Second" Header="Second" />
            <controls:PanoramaItem x:Name="Third" Header="Third" />
        </controls:Panorama>

Note that since the name of each item matches a corresponding property in the MainPageVM, each content is filled with the view corresponding with the child VM exposed by the property.

May 19, 2011 at 9:04 PM

It was my starting point. The only problem is that when MainPageViewModel derive from Conductor<IScreen>.Collection.OneActive events like OnActivate are not fired in first/second/third view models. When it derive from AllActive it works just fine and when it's wired automatically by using x:Name="Items" it works too.

BTW there was PivotFix and now it's missing.

May 19, 2011 at 9:38 PM
Edited May 19, 2011 at 9:38 PM

Conductor<IScreen>.Collection.OneActive behaves differently; you have to choose one of the two alternatives according to the desired behavior.

It only has one screen active out of N, as name suggest, so initialization just occurs at first activation.
Also, unlike .AllActive, the .OneActive conductor DOES NOT automatically activate items added to Items collection, if not explicitly instructed to do so using ActivateItem method.
So, in your scenario, you had no active screen at all, hence no OnInitialize was fired.

May 19, 2011 at 9:48 PM

I do active first view in constructor (it was not in example) but it's not a problem. The problem is that when change screens(sub views) in panorama/pivot then OnActivate is not fired in subviews. Maybe it's something connected with

    protected override void OnViewLoaded(object view) {
        pivotFix.OnViewLoaded(view, base.OnViewLoaded);
    }

    protected override void ChangeActiveItem(IScreen newItem, bool closePrevious) {
        pivotFix.ChangeActiveItem(newItem, closePrevious, base.ChangeActiveItem);
    }

May 19, 2011 at 11:28 PM
Edited May 19, 2011 at 11:33 PM

I think that i've found out what's going on. I would have to manually set

 

<Controls:Pivot/Panorama SelectedItem="{Binding ActiveItem, Mode=TwoWay}"


to get notified but it trows an exception with "SelectedIndex" message. When i use automatic CM binding (x:Name="Items") everything (almost - i have to set DisplayName manually) goes fine.


BTW I think that SelectedItem="{Binding ActiveItem, Mode=TwoWay} is unnecessary in PageTwo on http://caliburnmicro.codeplex.com/wikipage?title=Working%20with%20Windows%20Phone%207&referringTitle=Documentation example.

May 20, 2011 at 7:34 AM

So i think the final solution is.

View:

 

        <controls:Pivot/Panorama Title="Title" x:Name="Items">
            <controls:PivotItem x:Name="First" Header="first" />
            <controls:PivotItem x:Name="Second" Header="second" />
            <controls:PivotItem x:Name="Third" Header="third" />
        </controls:Pivot>

 

ViewModel:

public class PageViewModel : Conductor<IScreen>.Collection.OneActive {

		public PageViewModel( FirstViewModel first, SecondViewModel second, ThirdViewModel third ) {
			this.First= first;
			this.Second= second;
			this.Third= third;
			Items.Add( first);
			Items.Add( second);
			Items.Add( third);
			this.ActivateItem( first);
		}

		protected override void OnInitialize() {
			Page page = ( page ) this.GetView();
			this.First.DisplayName = emailPivotPage.First.Header.ToString();
			this.Second.DisplayName = emailPivotPage.Second.Header.ToString();
			this.Third.DisplayName = emailPivotPage.Third.Header.ToString();
		}

 

May 20, 2011 at 2:05 PM

I'm an incurable nitpicker, I know... but if you ended up with setting DisplayName in code, I see no point in having fixed controls:PivotItem.
Just let the Pivot build its children from the data source (Items).

May 20, 2011 at 2:09 PM

I only want to have all UI stuff (text) in view - this is the only way.