The guiding rule of MVVM is that the ViewModel doesn't know the View. That's not always possible, so that's why Caliburn.Micro has OnViewLoaded through which you can get a reference to the view. This should only be used if the proper ways fail for reasons out of your control. The ViewModel doesn't need a reference to the view in general. The VM and V communicate which each other through the binding machinery. If you have your own custom control it should have custom DependencyProperties and Events that you can bind to data properties and actions in your ViewModel. You use a custom control just like a regular control. Since I don't know your custom control let me demonstrate this on a small example. In the following View we have a text box and a button. The TextBox Text DependencyProperty is two-way bound to the ViewModel and for the Button we bind the Click event to the ClearText action in the ViewModel.
<Window x:Class="SimpleMVVM.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBox Text="{Binding Text, Mode=TwoWay}" Width="200" Height="25" />
<Button Content="Clear" cal:Message.Attach="[Event Click] = [Action ClearText]" Width="100"/>
</StackPanel>
</Grid>
</Window>
The corresponding ViewModel looks like this.
namespace SimpleMVVM
{
[Export]
class MainViewModel : Screen
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
NotifyOfPropertyChange(() => Text);
}
}
public void ClearText()
{
Text = "";
}
}
}
So, whenever the Text property changes in the ViewModel, the ViewModel triggers a PropertyChanged event, which tells the TextBox to refresh. That's why we can simple assign an empty string in the ClearText action to clear the TextBox. Also, because we have defined the binding of Text as a Two-Way binding, if you type and tab out of the TextBox, the current value is automatically assigned to the Text property in our ViewModel. This is fundamentaly how View and ViewModels communicate. The two interfaces that make this all work are INotifyPropertyChanged and INotifyCollectionChanged for collections. Screen and Conductor in Caliburn.Micro implement INotifyPropertyChanged, so you can just call the NotifyOfPropertyChange method to trigger the event. BindableCollection which extends ObservableCollection implement INotifyCollectionChanged, which will refresh the View if for example you have a list box bound to a collection in your view model and you add or remove an item from the collection.
Now, Caliburn.Micro defines conventions that let you write less XAML. There are no conventions out of the box for your custom control, but you could write them yourself. For the standard controls like TextBox and Button, there are conventions, so the above XAML can be simplifed.
<Window x:Class="SimpleMVVM.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBox x:Name="Text" Width="200" Height="25" />
<Button x:Name="ClearText" Content="Clear" Width="100"/>
</StackPanel>
</Grid>
</Window>
Now, Caliburn.Micro will perform the binding automatically. The TextBox is named the same as the property in the ViewModel, so Caliburn.Micro automatically establishes a two-way binding, since our property has a setter. Same for the button. The button's default event, which is Click, is automatically bound to the ClearText action method.
Having said all this, in MVVM you rarely create custom controls. Instead you leverage view composition. You can see that demonstrated in TempHire. TempHire is built using a number of smaller Views and ViewModels, which at runtime are composed together to the final application.