I am trying to display a login window once my MainWindow loads while sticking to the MVVM pattern. So I am trying to Bind my main windows Loaded event to an event in my view
You're going to have to use the System.Windows.Interactivity dll.
Then add the namespace in your XAML:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then you can do stuff like:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding MyICommandThatShouldHandleLoaded}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Please note that you will have to use an ICommand
(or DelegateCommand is you use Prism, or RelayCommand if you use MVVMLight), and the DataContext of your Window must hold that ICommand.
A more generic way using behaviors is proposed at AttachedCommandBehavior V2 aka ACB and it even supports multiple event-to-command bindings,
Here is a very basic example of use:
<Window x:Class="Example.YourWindow"
xmlns:local="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
local:CommandBehavior.Event="Loaded"
local:CommandBehavior.Command="{Binding DoSomethingWhenWindowIsLoaded}"
local:CommandBehavior.CommandParameter="Some information"
/>
Update:
I made a post about a new more flexible version of the method binding that uses a slightly different syntax here:
http://www.singulink.com/CodeIndex/post/updated-ultimate-wpf-event-method-binding
The full code listing is available here:
https://gist.github.com/mikernet/7eb18408ffbcc149f1d9b89d9483fc19
Any future updates will be posted to the blog so I suggest checking there for the latest version.
Original Answer:
.NET 4.5+ supports markup extensions on events now. I used this to create a method binding that can be used like this:
<!-- Basic usage -->
<Button Click="{data:MethodBinding OpenFromFile}" Content="Open" />
<!-- Pass in a binding as a method argument -->
<Button Click="{data:MethodBinding Save, {Binding CurrentItem}}" Content="Save" />
<!-- Another example of a binding, but this time to a property on another element -->
<ComboBox x:Name="ExistingItems" ItemsSource="{Binding ExistingItems}" />
<Button Click="{data:MethodBinding Edit, {Binding SelectedItem, ElementName=ExistingItems}}" />
<!-- Pass in a hard-coded method argument, XAML string automatically converted to the proper type -->
<ToggleButton Checked="{data:MethodBinding SetWebServiceState, True}"
Content="Web Service"
Unchecked="{data:MethodBinding SetWebServiceState, False}" />
<!-- Pass in sender, and match method signature automatically -->
<Canvas PreviewMouseDown="{data:MethodBinding SetCurrentElement, {data:EventSender}, ThrowOnMethodMissing=False}">
<controls:DesignerElementTypeA />
<controls:DesignerElementTypeB />
<controls:DesignerElementTypeC />
</Canvas>
<!-- Pass in EventArgs -->
<Canvas MouseDown="{data:MethodBinding StartDrawing, {data:EventArgs}}"
MouseMove="{data:MethodBinding AddDrawingPoint, {data:EventArgs}}"
MouseUp="{data:MethodBinding EndDrawing, {data:EventArgs}}" />
<!-- Support binding to methods further in a property path -->
<Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" />
View model method signatures:
public void OpenFromFile();
public void Save(DocumentModel model);
public void Edit(DocumentModel model);
public void SetWebServiceState(bool state);
public void SetCurrentElement(DesignerElementTypeA element);
public void SetCurrentElement(DesignerElementTypeB element);
public void SetCurrentElement(DesignerElementTypeC element);
public void StartDrawing(MouseEventArgs e);
public void AddDrawingPoint(MouseEventArgs e);
public void EndDrawing(MouseEventArgs e);
public class Document
{
// Fetches the document service for handling this document
public DocumentService DocumentService { get; }
}
public class DocumentService
{
public void Save(Document document);
}
More details can be found here: http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension
The full class code is available here: https://gist.github.com/mikernet/4336eaa8ad71cb0f2e35d65ac8e8e161
Use Attached Behavior. That is allowed in MVVM ....
(code below may / may not compile just like that)
XAML ...
<Window x:Class="..."
...
xmlns:local="... namespace of the attached behavior class ..."
local:MyAttachedBehaviors.LoadedCommand="{Binding ShowLogInWindowCommand}">
<Grid>
</Grid>
</Window>
Code Behind...
class MainWindowViewModel : ViewModelBase
{
private ICommand _showLogInWindowCommand;
public ICommand ShowLogInWindowCommand
{
get
{
if (_showLogInWindowCommand == null)
{
_showLogInWindowCommand = new DelegateCommand(OnLoaded)
}
return _showLogInWindowCommand;
}
}
private void OnLoaded()
{
//// Put all your code here....
}
}
And the attached behavior...
public static class MyAttachedBehaviors
{
public static DependencyProperty LoadedCommandProperty
= DependencyProperty.RegisterAttached(
"LoadedCommand",
typeof(ICommand),
typeof(MyAttachedBehaviors),
new PropertyMetadata(null, OnLoadedCommandChanged));
private static void OnLoadedCommandChanged
(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = depObj as FrameworkElement;
if (frameworkElement != null && e.NewValue is ICommand)
{
frameworkElement.Loaded
+= (o, args) =>
{
(e.NewValue as ICommand).Execute(null);
};
}
}
public static ICommand GetLoadedCommand(DependencyObject depObj)
{
return (ICommand)depObj.GetValue(LoadedCommandProperty);
}
public static void SetLoadedCommand(
DependencyObject depObj,
ICommand value)
{
depObj.SetValue(LoadedCommandProperty, value);
}
}
DelegateCommand
source code can be found on the internet... Its the most suited ICommand API available for MVVM.
edit:19.07.2016 two minor syntax errors fixed
For VS 2013 Update 5 I wasn't able to get around "Unable to cast object of type 'System.Reflection.RuntimeEventInfo' to type 'System.Reflection.MethodInfo". Instead in a "Core" directory I made a simple interface
interface ILoad
{
void load();
}
My viewModel already had the load() function implementing ILoad. In my .xaml.cs I call the ViewModel load() through ILoad.
private void ml_Loaded(object sender, RoutedEventArgs e)
{
(this.ml.DataContext as Core.ILoad).load();
}
The xaml.cs knows nothing about the ViewModel except the POCO ILoad, the ViewModel knows nothing about the xaml.cs. The ml_loaded event is mapped to ViewModel load().