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.79k stars 776 forks source link

Custom usage of ViewModelBinder.Bind(object, dependencyObject, object) not using conventions. #101

Open dealproc opened 9 years ago

dealproc commented 9 years ago

My assumption here is that I am doing something wrong, but need to ask the group and get a feedback that I can move forward with.

I am using MahApps Metro for a UI control suite, and Caliburn Micro to orchestrate the interaction of screens.

I have the following to show a custom dialog:

public IEnumerable<IResult> Handle(AggEvents.ShowDialog message) {
    var screen = _LifetimeScope.Resolve(message.View);
    if (screen != null) {
        var view = ViewLocator.LocateForModel(screen, null, null) as BaseMetroDialog;
        ViewModelBinder.Bind(screen, view, null);

        var activator = screen as IActivate;

        if (activator != null) {
            activator.Activate();
        }

        yield return Window.ShowMetroDialogAsync(view).AsResult();
    }
}

What is happening is that if I do not call cal:Message.Attach="Ok()" on my buttons, the commands are not binding appropriately on screen.

I honestly feel as if I'm not doing something appropriately (and yes, I did x:Name="Ok" for the Ok button, for example) already.

Anyone with any help/advice on what I may have made a mistake on above... the help would be greatly appreciated.

nigel-sampson commented 9 years ago

I can't see anything obviously wrong with that code, do any of the CM conventions work inside that view or is it just the buttons that aren't having conventions applied?

dealproc commented 9 years ago

I'm just getting into building the view out, and adopting other values from the ViewModel. Let me see what happens as I continue.

dealproc commented 9 years ago

It's looking like it's the button click events that are not binding unless I use the cal:Message.Attach method to bind out. Properties seem to be bound appropriately.

nigel-sampson commented 9 years ago

Can I confirm the buttons are located in the view?

It sounds a silly question, but often there's a content control representing the dialog that ends up containing the view and the buttons. If that's the case then the buttons won't have the conventions applied.

dealproc commented 9 years ago

Verbatim from screen:

<mahappsDialog:SimpleDialog x:Class="BTRG.DMF.GUI.Views.RunningProjectsView"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                            xmlns:mahappsDialog="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"
                            xmlns:cal="http://www.caliburnproject.org"
                            mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="650">
    <Grid MinHeight="400">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center" />
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1">
            <Button Style="{StaticResource GreenButton}" Content="Ok" cal:Message.Attach="Ok()" />
            <Button Style="{StaticResource BlueButton}" Content="Cancel" cal:Message.Attach="Cancel()" />
        </StackPanel>
    </Grid>
</mahappsDialog:SimpleDialog>
nigel-sampson commented 9 years ago

If you set the x:Name of the TextBlock to Title (and remove the binding) is the convention being applied?

mvermef commented 9 years ago

Namespace issues?

dealproc commented 9 years ago

It is not. I have never done that before, and hadn't realized that the labels had shared a similar convention to the buttons.

nigel-sampson commented 9 years ago

Can you add the following to your bootstrapper to enable logging for the view model binder.

var baseGetLog = LogManager.GetLog;

LogManager.GetLog = t => t == typeof (ViewModelBinder) ? new DebugLog(t) : baseGetLog(t);

You can view the output in your debug log.

dealproc commented 9 years ago

Ok... but what does this mean? Do I need to embed the controls within something specific under the base metro dialog control in order to provide the right hierarchy for the controls?

[Caliburn.Micro.ViewModelBinder] INFO: Binding BTRG.DMF.GUI.Views.RunningProjectsView and BTRG.DMF.GUI.ViewModels.RunningProjectsViewModel.
[Caliburn.Micro.ViewModelBinder] INFO: Attaching BTRG.DMF.GUI.Views.RunningProjectsView to BTRG.DMF.GUI.ViewModels.RunningProjectsViewModel.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_Title.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for set_Title.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for Ok.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for Cancel.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_Parent.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for set_Parent.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_DisplayName.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for set_DisplayName.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_IsActive.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_IsInitialized.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for add_Activated.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for remove_Activated.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for add_AttemptingDeactivation.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for remove_AttemptingDeactivation.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for add_Deactivated.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for remove_Deactivated.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for CanClose.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for TryClose.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for add_ViewAttached.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for remove_ViewAttached.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for GetView.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for add_PropertyChanged.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for remove_PropertyChanged.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for get_IsNotifying.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for set_IsNotifying.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for Refresh.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for NotifyOfPropertyChange.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for NotifyOfPropertyChange.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for ToString.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for Equals.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for GetHashCode.
[Caliburn.Micro.ViewModelBinder] INFO: Action Convention Not Applied: No actionable element for GetType.
mvermef commented 9 years ago

was there anything else near the bottom of the output for that dialog?

dealproc commented 9 years ago

This seems to work.

public IEnumerable<IResult> Handle(AggEvents.ShowDialog message) {
    var screen = _LifetimeScope.Resolve(message.View);
    if (screen != null) {
        var view = ViewLocator.LocateForModel(screen, null, null) as BaseMetroDialog;

        yield return Window.ShowMetroDialogAsync(view).AsResult();
        yield return Task.Factory.StartNew(() => {
            ViewModelBinder.Bind(screen, view, null);

            var activator = screen as IActivate;

            if (activator != null) {
                activator.Activate();
            }
        }).AsResult();
    }
}

This leads me to believe that the UI bits need to be created and shown before the binder can do its job onscreen... am I correct on this?

mvermef commented 9 years ago

did the x:Name="Title" get set on the textblock instead of the Text="{Binding Title}", I think what Nigel was looking for was whether or not the ViewModelBinder was actually finding the controls in question. That is why he asked if you could set x:Name="Title" on that textblock, because the DebugLog output would be slightly different.

dealproc commented 9 years ago

Yes, once I rolled over to this new way of building the code, all the x:name="..." works as expected. Now I'm just trying to understand the "why" of it.

mvermef commented 9 years ago

late binding?

nigel-sampson commented 9 years ago

Most of the time there's no problem creating the UI bits and binding them before showing them on screen. However it does appear that this is the problem there. Until the dialog is displayed it's not finding the named elements to display.

As a test can you call

 var namedElements = BindingScope.GetNamedElements(element);

before the ViewModelBinder.Bind(screen, view, null); in both the working and not working code and compare the results?

dealproc commented 9 years ago

I think, at this point, we'll call well-enough alone :)

Thanks for being a sounding board @nigel-sampson!

dealproc commented 9 years ago

working code - 74 elements. non-working code - 0 elements.

dealproc commented 9 years ago

It's not making sense. all of my x:Name="" are working as expected, my <TextBlock Text="{Binding ...}" /> stuff is working as expected too... but if you need any detail records, it doesn't seem to be inheriting the proper view scope. (e.g. do a binding to an ItemsSource and try to link the data context's button to a code-behind click event).

Is there a trick in the code base to find out what the button's context is, and how to resolve to get it to point to the right area? There are other controls in this same area that are getting their binding much like the above textblock.

Apologies if this sounds cross, just becoming a pain to try to deal with this particular way of binding up. completely lost.

nigel-sampson commented 9 years ago

Sorry I don't follow what you mean by "do a binding to an ItemsSource and try to link the data context's button to a code-behind click event".

dealproc commented 9 years ago

master... detail view models. I think ultimately, i'm building the dialog wrong. in the MahApps Metro project, someone offered a solution for running dialogs. Unfortunately, i'm under the gun (8 hours left in project), but I think i have to refactor to it.

Let me try that in the morning, and I'll get back to you.

dealproc commented 9 years ago

crap. i didn't realize i left this open... i need to review this, at some point, and see if it's resolved.

nigel-sampson commented 9 years ago

Don't think anything here has been resolved. I've left it open to come back to when I have time.