Caliburn-Micro / Caliburn.Micro

A small, yet powerful framework, designed for building applications across all XAML platforms. Its strong support for MV* patterns will enable you to build your solution quickly, without the need to sacrifice code quality or testability.
http://caliburnmicro.com/
MIT License
2.8k stars 779 forks source link

Problem rebinding viewmodel to usercontrol #57

Closed matopeto closed 10 years ago

matopeto commented 10 years ago

Hi I have problem in conductor page, I thing it may be a bug.

I have Conductor<IScreen> that is holding one and only active viewModel (DashboardViewModel or LoginViewModel)

In xaml I have:

<!-- it is visible only when Login is not null ->
<con:myControl1 cal:Bind.Model="{Binding LoginViewModel}" />

<!-- it is visible only when Dashboard is not null ->
<con:myControl2 cal:Bind.Model="{Binding DashboardViewModel}" />

in myControl1 I have another control in myControl1 with binding text and events to action

<TextBox x:name="SomeText" />
<con:PinKeyboard
                x:Name="keyboard"
                cal:Message.Attach="[Event CharAdded] = [Action CharAdded($eventArgs)]; [Event CharRemoved] = [Action CharRemoved]" />

In conductor I switch betwean DashboardViewModel and LoginViewModel. (Only one is active) and at activation time it is created new one:

if (logout) {
    LoginViewModel = new LoginViewModel(); // It is notif. property.
    ActivateItem(LoginViewModel);
}

At first start everything is fine. CharAdded is calling. But if I recreate LoginViewModel after user logout, it is not rebinding to user control.

I try:

<con:myControl1 cal:Bind.ModelWithoutContext="{Binding LoginViewModel}" />

It is not working also, and "SomeText" isn't filled at all.

I try also:

<myControl1 DataContext="{Binding LoginViewModel}" cal:Bind.ModelWithoutContext="{Binding}" />

It works only first time, second time, SomeText is binding to new value but, attatchet actions (e.g CharAdded ) are executed on old ViewModel.

How I can switching betwean viewModels with binding and actions?

Thank for answer (and sorry for bad english :))

nigel-sampson commented 10 years ago

This looks like a rather involved scenario, do have a example project that shows the issue?

matopeto commented 10 years ago

Hi, example project is here (WP8 silverligt project): https://dl.dropboxusercontent.com/u/81893/Conductor.zip

It has a four examples to bind viewModel to usercontrol, but no one fully work.

problem is that I don't know how to bind PageViewModel (with dynamic creation) to user control with right Datacontext, "NamingConvention Action" and "custom Attached event to action".

nigel-sampson commented 10 years ago

Thanks for that, I've manged to recreate the issue you're seeing. I'm not 100% sure this is a "bug" but it's definitely odd.

What's happening is that Bind.Model only really applys conventions between an existing view and view model. When the view model changes (when you recreate PageViewModel) is goes to reapply the conventions. However because we had the conventions previously applied those bindings existing on the view and we take care never to override existing bindings.

The x:Name convention on the user control doesn't work, because that's a convention applied to ContentControl's which user control doesn't inherit from.

If I rename PageControl to PageView and switch that first sample to

<ContentControl Margin="20" x:Name="PageViewModel" />

you'll get the result you're after. The different here is that when the view model is changed a new view is created so we don't have the issues with bindings.

Hope this helps.

matopeto commented 10 years ago

Thanks for reply. ContentControl woks (but it is relation 1:1 view to viewModel), but I would like to have more UserControl (views N:1 Views to viewModel) on One Page (it is panorama, and action/information are on more paces/panoramaItems), with binded the same dynamicly generated viewmodel.

Is there any way to do it?

nigel-sampson commented 10 years ago

Ok, this requirement seems different from your first post and I'm a bit confused.

For the first post about essentially having two separate states for your view model (using a conductor). One way to achieve this is make use of the Conductor's ActiveItem property that will get changed depending on whether you active LoginViewModel or DashboardViewModel.

Your view would contain something like:

<ContentControl x:Name="ActiveItem" />

This would mean for instance when you logout and activate LogOutViewModel the view changes to the correct view and binds correctly.

You're right this is using the 1:1 relationship. If you're looking to break the panorama items up into separate views / view models then your PanoramaViewModel could inherit from Conductor<IScreen>.Collection.OneActive where you create a collection of view models, each corresponding to a separate view for each of the panorama items.

tibel commented 10 years ago

@matopeto What you are looking for is described in in http://caliburnmicro.com/documentation/composition#multiple-views-over-the-same-viewmodel. Please use StackOverflow for questions and not the issue tracker.