2

In the start-screen of my program I am showing thumbnails of a (non-interactive) UserControl DocumentView to display different documents inside a Tile-like UserControl LoadTileView, on which the user can click to load the document into the main view. This DocumentView is also used in the main view to graphically represent and edit the selected document. Since this DocumentView is graphically heavy, it takes some time to load, which causes a huge startup time, since I am showing multiple instances of the DocumentView inside the start-screen (e.g. various Tiles from which the user can select, which Document to edit).

I am therefore working on a way to save a cached image of the DocumentView instance that belongs to each LoadingTileView, so that it can be shown instead of the actual DocumentView, when my program is started the next time.

I am currently working out how to save the cache images. My idea is to have the each LoadTileView call its LoadingTileViewModel via a MVVM-Light RelayCommand once it is loaded and pass its instance of DocumentView to the command. I put DocumentView inside a DataTemplate so that I can replace it with the its cached image (and corresponding View), once it exists by setting CurrentDocumentViewModel accordingly in LoadingTileView.

I have found an explanation on how to pass a UIElement to the ViewModel (here) and how to use the RelayCommand with an argument (here). Combined with blender interaction-triggers to fire the ViewLoadedEventHandlerCommand in the ViewModel came up with the reduced code shown below.

This code compile and runs, but parameter in ExecuteViewLoadedEventHandlerCommand(object parameter) is Null. I have also tried using DocumentView directly, instead of the ContentControl, but parameter is still Null.

I am not sure what I am doing wrong here, since I have used RelayCommand and the Interaction.Triggers this in other cases, where they worked correctly. Perhaps somebody can spot my error?

Code for LoadingTileView:

<UserControl.Resources>
    <DataTemplate DataType="{x:Type ViewModels:DocumentViewModel}">
        <View:DocumentView/>
    </DataTemplate>
</UserControl.Resources>

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding ViewLoadedEventHandlerCommand}"
                               CommandParameter="{Binding ElementName=DocumentViewInstance}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

<Controls:Tile Command="{Binding LoadProgramCommand}">
    <Viewbox>
        <ContentControl x:Name="DocumentViewInstance" Content="{Binding CurrentDocumentViewModel}"/>
    </Viewbox>
</Controls:Tile>

Code for LoadingTileViewModel:

public LoadingTileViewModel()
{
    ...
    ViewLoadedEventHandlerCommand = new RelayCommand<object>((obj)=>ExecuteViewLoadedEventHandlerCommand(obj));
    ...
}

public RelayCommand<object> ViewLoadedEventHandlerCommand { get; set; }
private void ExecuteViewLoadedEventHandlerCommand(object parameter) // object is NULL
{
    UIElement toSave = (UIElement)parameter;
    //OnViewLoaded();
}

Update:

Stacktrace, when breaking at line UIElement toSave = (UIElement)parameter;:

Software.UI.ViewModel.dll!ProgramEditor.ViewModel.LoadingTileViewModel.ExecuteViewLoadedEventHandlerCommand(object parameter) Line 168  C#
Software.UI.ViewModel.dll!ProgramEditor.ViewModel.LoadingTileViewModel.get_ViewLoadedEventHandlerCommand.AnonymousMethod__50_0(object i) Line 184   C#
[Native to Managed Transition]  
[Managed to Native Transition]  
GalaSoft.MvvmLight.dll!GalaSoft.MvvmLight.Helpers.WeakAction<System.__Canon>.Execute(System.__Canon parameter)  Unknown
GalaSoft.MvvmLight.dll!GalaSoft.MvvmLight.Command.RelayCommand<object>.Execute(object parameter)    Unknown
System.Windows.Interactivity.dll!System.Windows.Interactivity.InvokeCommandAction.Invoke(object parameter)  Unknown
System.Windows.Interactivity.dll!System.Windows.Interactivity.TriggerBase.InvokeActions(object parameter)   Unknown
System.Windows.Interactivity.dll!System.Windows.Interactivity.EventTriggerBase.OnEvent(System.EventArgs eventArgs)  Unknown
System.Windows.Interactivity.dll!System.Windows.Interactivity.EventTriggerBase.OnEventImpl(object sender, System.EventArgs eventArgs)   Unknown
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised)    Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args)   Unknown
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e)  Unknown
PresentationFramework.dll!System.Windows.BroadcastEventHelper.BroadcastEvent(System.Windows.DependencyObject root, System.Windows.RoutedEvent routedEvent)  Unknown
PresentationFramework.dll!System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(object root) Unknown
PresentationCore.dll!MS.Internal.LoadedOrUnloadedOperation.DoWork() Unknown
PresentationCore.dll!System.Windows.Media.MediaContext.FireLoadedPendingCallbacks() Unknown
PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()    Unknown
PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget)    Unknown
PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget)    Unknown
PresentationCore.dll!System.Windows.Media.MediaContext.Resize(System.Windows.Media.ICompositionTarget resizedCompositionTarget) Unknown
PresentationCore.dll!System.Windows.Interop.HwndTarget.OnResize()   Unknown
PresentationCore.dll!System.Windows.Interop.HwndTarget.HandleMessage(MS.Internal.Interop.WindowMessage msg, System.IntPtr wparam, System.IntPtr lparam) Unknown
PresentationCore.dll!System.Windows.Interop.HwndSource.HwndTargetFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)   Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)   Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)  Unknown
[Native to Managed Transition]  
user32.dll!74d862fa()   Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]    
user32.dll!74d86d3a()   Unknown
user32.dll!74d86ded()   Unknown
user32.dll!74d86e4c()   Unknown
ntdll.dll!773a011a()    Unknown
user32.dll!74d872c1()   Unknown
user32.dll!74dad4ff()   Unknown
user32.dll!74d862fa()   Unknown
user32.dll!74d86d3a()   Unknown
user32.dll!74d90d37()   Unknown
user32.dll!74d90d5d()   Unknown
WindowsBase.ni.dll!6b4a6e70()   Unknown
[Managed to Native Transition]  
WindowsBase.dll!MS.Win32.HwndSubclass.DefWndProcWrapper(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)    Unknown
[Native to Managed Transition]  
user32.dll!74d862fa()   Unknown
user32.dll!74d86d3a()   Unknown
user32.dll!74d90d37()   Unknown
user32.dll!74d90d5d()   Unknown
WindowsBase.ni.dll!6b4ced1a()   Unknown
[Managed to Native Transition]  
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)  Unknown
[Native to Managed Transition]  
user32.dll!74d862fa()   Unknown
user32.dll!74d86d3a()   Unknown
user32.dll!74d86ded()   Unknown
user32.dll!74d86ded()   Unknown
user32.dll!74d88fb7()   Unknown
ntdll.dll!773a011a()    Unknown
user32.dll!74d92832()   Unknown
user32.dll!74d92802()   Unknown
user32.dll!74d9289a()   Unknown
[Managed to Native Transition]  
MahApps.Metro.dll!Standard.NativeMethods.SetWindowRgn(System.IntPtr hWnd, System.IntPtr hRgn, bool bRedraw) Unknown
MahApps.Metro.dll!Microsoft.Windows.Shell.WindowChromeWorker._SetRoundingRegion(Standard.WINDOWPOS? wp) Unknown
MahApps.Metro.dll!Microsoft.Windows.Shell.WindowChromeWorker._UpdateFrameState(bool force)  Unknown
MahApps.Metro.dll!Microsoft.Windows.Shell.WindowChromeWorker._ApplyNewCustomChrome()    Unknown
MahApps.Metro.dll!Microsoft.Windows.Shell.WindowChromeWorker._WindowSourceInitialized(object sender, System.EventArgs e)    Unknown
PresentationFramework.dll!System.Windows.Window.OnSourceInitialized(System.EventArgs e) Unknown
PresentationFramework.dll!System.Windows.Window.CreateSourceWindow(bool duringShow) Unknown
PresentationFramework.dll!System.Windows.Window.CreateSourceWindowDuringShow()  Unknown
PresentationFramework.dll!System.Windows.Window.SafeCreateWindowDuringShow()    Unknown
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox)   Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()   Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state)  Unknown
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)   Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)  Unknown
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()   Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()  Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)  Unknown
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)   Unknown
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)  Unknown
[Native to Managed Transition]  
user32.dll!74d862fa()   Unknown
user32.dll!74d86d3a()   Unknown
user32.dll!74d877d3()   Unknown
user32.dll!74d8789a()   Unknown
WindowsBase.ni.dll!6b4ceff4()   Unknown
[Managed to Native Transition]  
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)   Unknown
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame)   Unknown
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)   Unknown
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)  Unknown
PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window)  Unknown
PresentationFramework.dll!System.Windows.Application.Run()  Unknown
ProgramEditor.exe!ProgramEditor.App.Main()  C#
[Native to Managed Transition]  
mscoreei.dll!7143cc0b() Unknown
mscoree.dll!714b7f16()  Unknown
mscoree.dll!714b4de3()  Unknown
kernel32.dll!76d6338a() Unknown
ntdll.dll!773c9902()    Unknown
ntdll.dll!773c98d5()    Unknown
Community
  • 1
  • 1
packoman
  • 1,230
  • 1
  • 16
  • 36
  • If you are setting LoadingTileView's DataContext in the constructor then make sure you do this *before* the call to InitializeComponent(). – Andrew Stephens Jul 07 '16 at 11:42
  • I'd say, if the Binding to the Command works the DataContext is there, so that's not it. I actually think the code looks ok... How does the stacktrace look like when you hit `ExecuteViewLoadedEventHandlerCommand` maybe you'll find a hint about where it goes wrong... – Markus Hütter Jul 07 '16 at 11:44
  • @MarkusHütter Thanks for the comment. Unfortunately the callstack does not give me much enlightenment. See below. Perhaps you have another suggestion? `LoadingTileViewModel.ExecuteViewLoadedEventHandlerCommand(object parameter) LoadingTileViewModel..ctor.AnonymousMethod__24_0(object obj) [External Code] [Frames below may be incorrect and/or missing, no symbols loaded for user32.dll]` – packoman Jul 07 '16 at 12:22
  • https://blogs.msdn.microsoft.com/saraford/2008/08/11/did-you-know-how-to-show-external-code-288/ – Markus Hütter Jul 07 '16 at 12:28
  • @packoman instead of `{Binding ElementName=DocumentViewInstance}`, write only `someinput` and see it reaches `ExecuteViewLoadedEventHandlerCommand` . – AnjumSKhan Jul 07 '16 at 12:29
  • @MarkusHütter Thanks for the heads-up. Added the StackTrace to the question. @AnjumSKhan Doing what you describe, `someinput` reaches the command. Thanks for the idea BTW! – packoman Jul 07 '16 at 12:44
  • *reads the title* that's not mvvm. Save yourself headache and move your saving code into the codebehind. –  Jul 07 '16 at 13:41
  • 1
    @Will This is an excellent point, that I had not considered. I got my code working for the moment, but I should clean it up to be more in line with the MVVM pattern. – packoman Jul 07 '16 at 15:06

2 Answers2

1

It turns out that the problem was, that the Loaded event of the TileView fired before the DocumentView inside the DataTemplate loaded. I found this out by attaching another Command in my DocumentViewModel to the Loaded event of the DocumentView. Afterwards I saw Markus' answer and this is essentially what he suggested.

In the end I got my code working by moving the command for the Loaded event to the DataTemplate. For this I used a StackPanel to bind to its Loaded event, so that I ended up with:

<DataTemplate DataType="{x:Type ViewModels:DocumentViewModel}">
    <StackPanel>
        <View:DocumentView x:Name="DocumentViewInstance"/>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <i:InvokeCommandAction Command="{Binding Path=DataContext.ViewLoadedEventHandlerCommand,
                                                         RelativeSource={RelativeSource FindAncestor,
                                                                                        AncestorType={x:Type UserControl}}}"
                                       CommandParameter="{Binding ElementName=DocumentViewInstance}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </StackPanel>
</DataTemplate>
packoman
  • 1,230
  • 1
  • 16
  • 36
  • Thank you @packoman for the suggestion. I was able to retrieve the Button control in my RelayCommand parameter. This worked for me :- ` – surajitk Feb 25 '20 at 04:16
0

in the comments you say that someinput reaches the command. That means the elementname binding is either failing entirely or late.

So: for testing, add a Button that has the same i:InvokeCommandAction but on an <i:EventTrigger EventName="Click"> that way you can test if a later Binding will work.

Also keep an eye on the Output window in VS while debugging. If the binding fails there should be a message.

My assumption is that the problem is within your Tile control. I'd guess your Tile control hasn't initialized its child elements yet when the Loaded event happens.

Markus Hütter
  • 7,796
  • 1
  • 36
  • 63
  • Yes in the end, this is was the case. As describe in my answer, the DataTemplate is being initialized after the `Tile` control. I wonder, whether this is default behavior for a `ContentControl`? – packoman Jul 07 '16 at 15:04