0

I am using Caliburn.Micro to try to bind items in a ListBox to one of two views but a single viewmodel. I am able to display the items in the ListBox, but when any item is selected I get 'Cannot find view for CustomerViewModel.'

Here are the relevant pieces of this application:

AppBootstrapper:

public class AppBootstrapper : BootstrapperBase
{
    public AppBootstrapper()
        : base()
    {
        Initialize();
    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
        base.DisplayRootViewFor<CustomerWorkspaceViewModel>();
    }
}

In my Views/Customers folder I have a CustomerViewModel:

public class CustomerViewModel : Screen
{
    private string name;

    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            NotifyOfPropertyChange(() => Name);
        }
    }
}

and a CustomerWorkspaceViewModel:

public class CustomerWorkspaceViewModel : DocumentWorkspace<CustomerViewModel>
{
    private CustomerViewModel selectedItem;
    public CustomerViewModel SelectedItem
    {
        get { return selectedItem; }
        set
        {
            selectedItem = value;
            NotifyOfPropertyChange(() => SelectedItem);
        }
    }

    public CustomerWorkspaceViewModel()
    {
        Items.AddRange(
            new ObservableCollection<CustomerViewModel>
            {
                new CustomerViewModel {Name = "Customer 1" },
                new CustomerViewModel {Name = "Customer 2" },
                new CustomerViewModel {Name = "Customer 3" }
            });
    }
}

I have four views in my Views/Customers folder: In a Views/Customers/CustomerWorkspace, I have and Edit View and an Read view:

Edit.xaml:

<Grid>
    <StackPanel>
        <Label Content="Edit View"/>
        <TextBlock Foreground="White"
                   FontSize="20"
                   Text="{Binding Name}" />
    </StackPanel>
</Grid>

and Read.xaml:

<Grid>
    <TextBlock Foreground="White"
               FontSize="20"
               Text="{Binding Name}" />
    <Label Content="Read view"/>
</Grid>

Finally I have an empty CustomerView user control in Views/Customers, and a CustomerWorkspaceView in Views/Customers:

    <Grid>
    <ListBox x:Name="Items"
             Margin="5"
             SelectedItem="{Binding SelectedItem}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}" Margin="5" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <ContentControl cal:View.Context="{Binding State, Mode=TwoWay}" cal:View.Model="{Binding SelectedItem}"/>
</Grid>

Finally I have DocumentWorkspace, at the root folder, with AppBootstrapper:

    public abstract class DocumentWorkspace<TDocument> : Conductor<TDocument>.Collection.OneActive
    where TDocument : class, INotifyPropertyChanged, IDeactivate, IHaveDisplayName
{
    public enum DocumentWorkspaceState
    {
        Read,
        Edit
    }

    DocumentWorkspaceState state = DocumentWorkspaceState.Read;

    public DocumentWorkspaceState State
    {
        get { return state; }
        set
        {
            if (state == value)
                return;

            state = value;
            NotifyOfPropertyChange(() => State);
        }
    }
}

What I am desiring (and expecting) is when selecting an item in the ListBox, which is composed of DocumentWorkspace objects, which are Conductors, to switch from one view (Edit) to another (Read). The select is working, the SelectedItem setter is getting fired, and the State in DocumentWorkspace is set correctly. But Caliburn.Micro cannot seem to find the view for the resulting CustomerViewModel that is SelectedItem. I've really tried to include in this post only what is needed to reproduce the problem here.

Note the documentation for what I am trying to do follows the discussion at

Will
  • 421
  • 2
  • 8
  • 23
  • Caliburn micro uses a convention when looking for views and view models. for this case you want a `ViewModels/Customers` to hold the view models. the views would be in `Views/Customers`. It will be looking for a `Customer` or `CustomerView` in the views folder based on default naming convention. – Nkosi May 15 '17 at 22:39
  • Hi @Nkosi, all my customer-related viewmodels are in ViewModels/Customers, and my views are all in Views/Customers. If you look at the documentation, I have a folder called Views/Customers/CustomerWorkspace to hold my Read and Edit views as well. – Will May 15 '17 at 22:48
  • 1
    somewhere in your namespaces its jacked up. Make sure everything jives otherwise you will just get `namespace.viewmodel.somefolder.somestring` displayed by `conventionmanager` in place of the view. .. – mvermef May 16 '17 at 05:01

1 Answers1

1

So mvermef pointed me in the right direction. I needed to fix my namespaces. I also needed to create a folder called "Customers" under "Views" and move the Edit and Read views to it. Then I needed to fix my namespaces again. Resharper is a great tool for that, by the way.

So what was wrong, besides the namespaces? In the CustomerWorkspaceView, I had

<ContentControl cal:View.Context="{Binding State, Mode=TwoWay}" cal:View.Model="{Binding SelectedItem}"/>

This caused Caliburn.Micro to look for a State property in the CustomerWorkspaceViewModel (actually in the base class DocumentWorkspace) and in this case the result was "Read". It just needs a string here. There was a "Read" View, but it was in a folder called "CustomerWorkspace". It needed to be in a folder called Customer, as the SelectedItem property is of type CustomerViewModel.

It turns out I didn't even need the empty CustomerView. As long as Caliburn.Micro can find a folder with the same name as your ViewModel type minus "ViewModel" and the namespaces match the folder structure, it should find your view.

Here's my complete folder structure: enter image description here

Will
  • 421
  • 2
  • 8
  • 23
  • awesome, I had learned the hard way with that exact structure for my application many moons ago. It was a shot in the dark without seeing your folder structure. – mvermef May 17 '17 at 16:09