Notify ViewModel when View is rendered/instantiated
Asked Answered
S

4

6

I have a custom usercontrol (ChartControl) that I use within my WPF app (MainApp) and which I render as follows:

<ContentControl Grid.Row="1" Content="{Binding ChartControl, Mode=OneWay}" />

Upon starting MainApp the following are executed in the given order:

MainApp View MainApp ViewModel ChartControl ViewModel ChartControl View

I instantiate the ChartControl ViewModel from within the constructor of my MainApp ViewModel. The problem is that after instantiating the ChartControl ViewModel I also need to call a method of ChartControl from within MainApp.

The problem I am having is that I need the ChartControl view to be rendered (have its InitializeComponent executed) before I call the method as part of its viewmodel.

I thought one solution could be to notify the view model from the view when it is fully instantiated and set up. Is that a viable solution and if yes how would I do that?

In summary, I need the view to be fully set up before invoking a method of the matching viewmodel. The problem I am having is that in this case the view model is instantiated first and only then is the view rendered.

Any ideas?

Thanks

Spermatic answered 23/4, 2015 at 11:55 Comment(3)
What is that method, why that needs to be called after the InitializeComponent executed? What are you trying to do actually? This sounds like a xy problem for me.Ordway
It renders data series as chart on a chart surface and I am experiencing an issue in that the chart is not displayed. I suspect it is because the view model constructs the chart before the view is initialized and can render it.Spermatic
@SriramSakthivel, as you correctly hinted at, this did not lead me to a solution to my problem. My problem is that a custom chart library within a user control does not render chart series when I have the series rendered from the view model constructor of the hosting wpf app. Please see #29805560 for more details.Spermatic
E
8

You can make use of Interactivity triggers to fire Command on your VM on any UI event

You can listen to Loaded event of UserControl like below and bind it to Command on your VM:

<UserControl x:Class="Test.TestView.MyUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        x:Name="myControl" >

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding ElementName=myControl, Path=OnLoadedCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

And sure you will have Command in your VM as

public ICommand OnLoadedCommand { get; private set; }

public MyUserControl()
{
    OnLoadedCommand = new DelegateCommand(OnLoaded);
}

public void OnLoaded()
{
}
Eonism answered 23/4, 2015 at 12:6 Comment(2)
You should use Command="{Path=OnLoadedCommand}" if DataContext of your UserControl is set to your ViewModel (typically). myControl (this) doesn't contain OnLoadedCommand (ViewmModel does). In view (this = myControl = behind code) you can use event directly (throught event handler).Suiting
Really wished this worked with Xamarin.Forms and not just WPFJanitor
H
5

Another way to hook up the Loaded event, basically rendering the same result as nit's answer, is simply referencing your viewmodel in the constructor of the view and adding an event handler which in turn calls whatever method you need to call, like this:

public MyControl()
{
   InitializeComponent();

   this.Loaded += (s, e) => { ((MyViewModel)DataContext).MyInitializer(); };
}

If you find the syntax confusing you might want to read up on Anonymous methods and Subscribing to event handlers (using anonymous methods).

Humphrey answered 23/4, 2015 at 13:8 Comment(1)
of the various ways I have hooked up to the Visibility property of my UserControls, I like this one the best so far.Configuration
S
3

I'm using similar solution like Hogler only with reflection (lazy coupled solution). I dont want referencing specific type of my ViewModel (because of generality, interchangeability, etc.).

public MyControl()
{
   InitializeComponent();
   Loaded += MyControl_Loaded;
}

private void MyControl_Loaded(object sender, RoutedEventArgs e)
{   
  (DataContext.GetType().GetProperty("LoadedCommand")?.
    GetValue(DataContext) as ICommand)?.
    Execute(null);
}

ViewModel can (dont have to) contain desired command like property (LoadedCommand in this case). Nothing more.

Suiting answered 27/7, 2016 at 12:9 Comment(0)
M
1

In an MVVM world I found that when creating a visual item and putting it onto the view (in this case adding to a list) the item wouldn't be in the visual tree until the loaded event fired.

My view model contained the items list in an observable collection which the XAML view would display.

ObservableCollection<MyControl> Items;

I'd add an item to the list, but when I perform an operation that requires it to be in the visual tree and performs visual tree recursion, this couldn't happen immediately after. Instead I had to code something like this:

var newItem = new MyControl();

newItem.Loaded += NewItemLoaded;

Items.Add(new MyControl());

The event handler would then unhook and perform the operation - and at this point is was in the visual tree as required:

private void NewItemLoaded(object sender, RoutedEventArgs e)
{
    var item = sender as MyControl;
    item.Loaded -= NewItemLoaded;

    // now this item is in the visual tree, go ahead and do stuff ...
}
Meridethmeridian answered 12/1, 2017 at 16:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.