SOLVED: TabViewModel - Data not displaying

Topics: Conventions, Getting Started
Aug 27, 2013 at 8:11 AM
Hi, I am trying to create a wpf app using a TabControl in my shell and have hit a block regards dealing with TabViewModels/TabViews. I seem to get the data, but it doesn't display in any of the controls. Unfortunately, I have tried so many things, I'm not sure which way I should be heading or even whether I should start again.

Background: I have an entity framework model that I am connecting to via a WCF service. My service and DAL are in separate projects. I am trying to use StructureMap for DI. The project shows details of Callers, their Calls, and the CallQueries made during each call (to make it more common, the domain concepts match to Customers, Orders and OrderDetails). The user selects from a list of callers in the shell view, and clicks 'Show Caller Details' which should add a new Tab to the TabControl containing details of that Caller. The 'Caller details' tab includes a list of calls (in its own view) which the user can select from and then view details relating to that call in a different tab - so far I haven't even tried to get this bit working, but it is the reason for having a separate TabViewModel (ie both CallerDetailsVM and CallDetailsVM will extend TabViewModel).

What happens: When the user selects the Caller Details button, the new tab is created and presented but the widgets in the view don't contain the data about the caller - except for the DisplayName (which is a property of the base TabViewModel).

First, am I doing the Exports right?
[Export(typeof(IShell))]
ShellViewModel : Conductor<IScreen>.Collection.OneActive, IShell
[Export(typeof(IScreen))]
TabViewModel : Screen
[Export(typeof(TabViewModel))]
CallerDetailsViewModel : TabViewModel
Second, do I need a TabView, given there is no concrete instantiation of the TabViewModel? I don't think it's hurting either way, but I don't think it is necessary.

Third, and most importantly, what am I doing wrong? :-) Where does the data go? I can see I have the correct data as the DisplayName on the Tab is correct (ie shows the Caller's name) and debug messages show I'm getting other fields. I have tried various forms of syntax for the CallerDetails widgets. Eg for the Caller's 'GivenNames' text box I have tried the following: x:Name="GivenNames", x:Name="Caller_GivenNames"; when these didn't work, I tried to revert to the traditional binding syntax, using Text="{Binding GivenNames}", Text="{Binding Caller.GivenNames}"; both with and without the x:Name syntax. Interestingly, none of these seem to give the usual Binding Errors when the binding engine can't match the data to the control.

Things that might help with diagnosis:
Solution: Image

Bootstrapper.Configure():
  {
     container = new StructureMap.Container(new RepositoryRegistry());
   }
RepositoryRegistry:
public RepositoryRegistry()
{
  For<Caliburn.Micro.IWindowManager>().Use<WindowManager>();
  For<Caliburn.Micro.IWindowManager>().Singleton();
  For<DLGCContactLog_Caliburn.Interfaces.IShell>().Use<ShellViewModel>();
  For<ICallerService>().Singleton().Use(() => new CallerServiceClient());
}
My apologies for the super-long first post. This mvvm stuff is not easy to grok (but Caliburn is the most understandable framework I've used, so I'm keen to makes sense of it all). If there's anything else I can provide to help, just let me know.

Many thanks
Aug 27, 2013 at 4:14 PM
Edited Aug 27, 2013 at 4:15 PM
so when you click on the caller to bring up the details it does bring up the appropriate view but no bound data, and the data is present in your property associated with the bindings. Caller_GivenNames for deep linking... etc.?? I am to assume that in your viewmodel for the "activeitem" there is a property named Caller?

How are you bringing the tab into activation?
Aug 28, 2013 at 5:28 AM
mvermef wrote:
so when you click on the caller to bring up the details it does bring up the appropriate view but no bound data, and the data is present in your property associated with the bindings.
Yes. As a check, after instantiating the CallerDetailsViewModel, I popup a dialog showing a couple of Caller properties. The CallerDetailsVM is blank apart from DisplayName (made up of the GivenNames and Surname properties of Caller): see pic Image
Caller_GivenNames for deep linking... etc.??
Not sure about the deep linking, I just tried to use Caller_GivenNames when GivenNames (by itself) didn't work.
I am to assume that in your viewmodel for the "activeitem" there is a property named Caller?
My CallerDetailsVM has a property named Caller - and also has properties relating to the Caller's properties (eg, Surname, GivenNames, EmailAddress).
My Shell has a Callers property to look after the list, and a SelectedCaller property when the user makes a selection.
How are you bringing the tab into activation?
The shell has a method 'ShowCallerDetails' invoked when the user clicks the 'Caller Details' button:
<Button x:Name="ShowCallerDetails" VerticalAlignment="Bottom"
                Content="Caller Details" 
                DockPanel.Dock="Left" 
            cal:Message.Attach="[Event Click] = [Action ShowCallerDetails]" />
ShowCallerDetails looks like this:
public void ShowCallerDetails()
{
      ActivateItem(new CallerDetailsViewModel(selectedCaller.CallerID)
      {
        DisplayName = selectedCaller.DisplayName,
        Surname= selectedCaller.Surname,
        GivenNames = selectedCaller.GivenNames
      });
      global::System.Windows.MessageBox.Show(string.Format("Activating: {0} {1} - {2}", selectedCaller.GivenNames, selectedCaller.Surname, selectedCaller.EmailAddress));
}
I have posted the CallerDetailsVM code on pastebin if it helps. Let me if there is anything else that will help.
Aug 28, 2013 at 9:34 PM
Edited Aug 28, 2013 at 9:35 PM
Can you post some of your xaml, some associated with the how the tabviewmodel is placed on the right side and some of the binding within the usercontrols that are of type TabViewModel? It might help.
Aug 30, 2013 at 1:20 AM
Edit: Curses! Foiled by the Preview button (it looked like it had been posted). This should have been here 18 hours ago.

I have pasted the Shell, TabView and CallerDetailsView.

Thank you again for looking at this
Aug 31, 2013 at 1:36 AM
Edited Aug 31, 2013 at 1:42 AM
Just for giggles do you have any sort of logging going on, using the built- in stubs? You might have to setup to send to the debug console.

Also

<Button Visibility="Visible" Content="Add Caller" Command="{Binding AddCallerCommand}" VerticalAlignment="Bottom"/>

can look like this...

<Button x:Name="AddCallerCommand" Content="Add Caller" />

then you can add a guard bool property named like such "CanAddCallerCommand" with some reason to make it enabled/disabled not necessary to bind to the Command property... CM does this automatically if you are using the built in conventions.

public bool CanAddCallerCommand { return someReason;}

but you will have to NotifyPropertyChanged( "CanAddCallerCommand" ); when the value changes... Minor but it might help in the long run...

I am still not seeing where this would be an issue unless the object isn't getting passed through your instantiation in ActivateItem();
Sep 1, 2013 at 12:11 PM
Edited Sep 1, 2013 at 6:13 PM
Logging: I have literally just started on this on Friday afternoon. Managed to get the default log info printing. I didn't see anything obviously wrong in that output. Will look further into how to locate errors in the logs on Monday.

Understood with the Button. I have ported this from a previous (non-Caliburn) attempt that used that syntax. I figured I'd get existing Callers to display before trying to add new ones. And yes, love the auto-guard conventions :-).
unless the object isn't getting passed through your instantiation
But it must be getting passed, otherwise the TabVM wouldn't know the DisplayName, as I understand things. Maybe it gets lost between the base instantiation (TabVM) and the subclass (CallerDetailsVM)?

Any logging hints to confirm where I've got the caller or where I haven't?

cheers
Sep 3, 2013 at 1:08 AM
Edited Sep 3, 2013 at 1:15 AM
Was looking over your code again...

use the constructor to setup your CallerId only, don't pass any other data in when you do your activateditem call only the ID...

[ImportingConstructor]
public CallerDetailsViewModel(int callerID)
{
   CallerId = callerId;     
}

there are more than one override take a look at them while you are at it..

protected override void OnActivate(){
do your service call here see what happens...

CallerService.Caller serviceCaller = new CallerServiceClient().GetCaller(callerID);
  Caller = new Caller
  {
    CallerID = serviceCaller.CallerID,
    Surname = serviceCaller.Surname,
    GivenNames = serviceCaller.GivenNames,
    EmailAddress = serviceCaller.EmailAddress,
    Phone = serviceCaller.Phone
  };
}

Also you won't have to do update triggers it's built into CM already. Just make sure if you wanted to make changes to values of the said underlying property you have to set Mode = TwoWay.

One other thing to give a try on is AllActive on the inherited Conductor for your Shell since your Shell is another UserControl, at least from what I see on snippets provided.
Sep 3, 2013 at 10:01 AM
I am taking a step back to try and clear things up a bit. Can I ask some theoretical type questions to see if I'm progressing in a sensible direction?

My ShellView is (now) a Window (I'm not sure why I originally made it a UserControl). The corresponding ShellVM implements/extends Conductor<IScreen>.Collection.OneActive, IShell. The view contains a TabControl which will hold my TabItem views.

1: Does the TabControl need to be called 'Items' for the purposes of the Conductor? (I'm not worried if it's not, I'm just checking I understand.)

I have a number of Views (all UserControls) to display data that can be one of two types. One type is essentially a TabItem and can be added directly to the TabControl, the other type is just gui widgets. The TabItem type view contains gui widgets and may have a widget-type view of its own. This mix is to allow master-detail displays (eg from Caller to Calls, and from Call to CallQueries).

2: Is the above composition sensible?

3: The VMs for the TabItem views extend a TabViewModel class, the VMs for the widget views extend Screen. Does this make sense?

4: What do my ViewModels export? I'm not sure if each VM should export its own type (eg: for CallerDetailsViewModel - [Export(typeof(CallerDetailsViewModel))]) or whether the TabItem type VMs should export IScreen, and the non-TabItem VMs export something else.

5: Should I have a TabViewModel class and have my TabItem VMs inherit from it? This is the current situation, however 'DisplayName' is the only property displaying data on my CallerDetails, and I'm thinking that this is because it has inherited the property from TabViewModel. If it's only going to display properties from the base class then perhaps it should be the base class itself.

I have dropped StructureMap from the mix and will run with MEF for DI for the time being as the examples use it. With the changes I have made today (including your suggestions above), I'm still getting the same issue, ie, it looks like the data has come across, it's just not being displayed properly anywhere. There's something called the snoop tool for WPF. Do you use it, and do you think it will help locate the missing data in this case?

cheers

cm
Sep 3, 2013 at 4:47 PM
1) Yes because the conductor when combined with Collection inherits a collection property Items, it will also know ActiveItem. Take a look at HelloScreens its a Silverlight variation but it shows screen composition. But also understand you can have more than 1 conductor to varying degrees controlling different parts of your applications structure. I think what is going is your application has one part that is being conducted and another that is not and therefore parts are not being activated.

2) Master / Detail pretty common design. Selecting a Caller will tell the system to get the "details" of that caller and present them on a tabitem (which is a usercontrol in context), cm does all the heavy lifting in this case.

3) Design choice they could has been derived from Screen its all a design choice. what I generally do is a ViewModelBase (which is of type Screen, I don't export based off of Screen but actual name of the ViewModel cause I call it directly not via Conductor mechanism) which has other stuff in it for myself but I am not using the Conductor in my application since I wasn't looking for dynamic construction of my application but instead on demand need. If it makes senses to you in the end then it works.

4) When you are exporting you effectively saying here I am do what you want, MEF is just looking for parts to distribute around application as you tell it. Don't forget though MEF also looks at signature of the request and if it doesn't match what is required by the request exactly it will just twitch and die...

5) Inherit from Screen, export off of IScreen is perfectly ok, since Screen inherits IScreen anyway.

Snoop might help, might not...
Sep 4, 2013 at 9:39 AM
Hi mvermef,

First up, an apology. I have found the immediate cause of the data not displaying. Part of the code that I was using from a different mvvm framework included some style settings (including changes to the default TextBox style). This morning I was checking to see if it was a read-only problem, and when I tried a non-editable control (a Label), all of a sudden the data appeared. I removed the TextBox style and my fields now display data. I'm still to investigate exactly what part of the style caused this, but for now, this issue at least is solved.

Thank you for your assistance. Hopefully, from now on, any issues I have will be proper technical problems, and not silly mistakes (currently, I am anticipating adding a Caller and dealing with the creation of the Caller's Organisation to be one of those technical problems).

cheers and thanks again
cm
Sep 4, 2013 at 5:42 PM
good to hear