How to execute operations after a UserControl is fully displayed? - c#

I have a main window that I use to display one of two possible views (ConfigView & AnalyzeView):
<Window.Resources>
<DataTemplate DataType="{x:Type vm:ConfigViewModel}">
<v:ConfigView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:AnalyzeViewModel}">
<v:AnalyzeView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
When transitioning from the ConfigView to the AnalyzeView, I want to first wait for the AnalyzeView to be fully displayed in the main window before performing the analysis operations. I initially added an EventTrigger for the Loaded event in the AnalyzeView as a way of starting the analysis operations:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCmd}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
However, I found that this event would be triggered before the AnalyzeView was displayed in the main window. In fact, it seems that the AnalyzeView is not displayed in the main window until sometime after the Loaded event.
Is there any event that I can trigger on so that I can start the analysis operations only after the AnalyzeView is fully displayed in the main window?
EDIT:
Ultimately, may main goal is to display a progress bar on the AnalyzeView that shows the progress of the analysis operations. Essentially, the user presses a "Start Analysis" button which transitions to the AnalyzeView and begins the analysis process, updating a progress bar as it goes.

I think what you're seeing is that the Loaded event fires after the UI thread has finished loading the control but if you have a breakpoint set in LoadedCmd, you'll notice AnalyzeView isn't rendered yet.
This is because WPF has a background rendering thread which hasn't yet had a chance to render the control, even though the UI thread is finished with it's loading. When your program hits the breakpoint, all threads are broken into and so it appears as though AnalyzeView never finished loading.
I'd suggest trying to allow the command to perform whatever operations are necessary to populate the AnalyzeView just as you've laid out here- if they're long running, and do not have to be loaded into DependencyProperties, you should run them asynchronously so that the UI thread can respond to input.
Edit 1: Following up on your comment's on #wimpSquad's answer - to keep this MVVM and show progress while keeping the UI responsive, you definitely will want to look into Tasks and reporting progress. Stephen Cleary has a good article here.
Further Reading:
WPF Threading Model
Async Loading Of Data

So, looks like you can - SO question - but I'm curious as to why you would want to. The View should only be a rendering of exposed properties in the ViewModel. What is your use case for this (morbid curiosity)?
Edit: based on your updated question, I'd recommend the following:
Load the AnalyzeView as normal, with a <ProgressBar/> bound to an exposed property that is incremented by the ViewModel (during your analysis process). This synchronicity can be achieved through event binding, and made even easier with PropertyChanged.Fody NuGet package, with the [ImplementPropertyChanged] attribute. This attribute was renamed in a more recent version of the package than what I am using, but with a quick search you should find what you are looking for there. In any case, the attribute handles triggering automatically, so as properties are updated in your ViewModel, the bound properties on your View update as well.
If you'd like to see an example of a <ProgressBar/> being used, just let me know. I've got one around here somewhere.

Related

Access DataContext of Page from MainWindow (with Telerik)

I am relatively new to WPF and I have stumbled across a problem that I just can't seem to find a solution for.
I am sure that there is already a thread concerning a problem like that but in regard of my lacking knowledge it is very likely that I haven't found it or simply did not understand it.
My problem:
I am developing a WPF-application in C#. It's an Outlook-Styled application with a big MainWindow with a huge ViewModel and XAML.
What I was trying to do, is to split up the single codefiles a bit to make it a little bit more modular and compact.
I am using Telerik Controls and tried to outsource the content of single SplitContainers into Pages, which worked fine until now.
Today, a new situation came up which is somehow stupid and wasn't looking too complicated, but somehow I can't get it to work.
Situation:
I have a Treeview in my "MainWindow" and whenever I change the selection in there, I want to change a property on my Page that I have made a binding to.
So, when I click on an item in "TreeView_3" I want to set a property via EventHandler (SelectionChanged_TreeView3) on the DataContext of "Page_X".
If I had to do this on the MainWindow, I would typically do it like that:
UserViewModel uvm = mainGrid.DataContext as UserViewModel;
Then just call whatever property of specific UserViewModel (ViewModel of the MainWindow) I want to access.
I can't do this the same the same way for the page obviously since "mainGrid.DataContext" will always refer to the MainWindow, since this is where the eventhandler is called.
So what I need would be a little explanation on how to access the DataContext from a page with a different ViewModel.
If you need any code in order to explain, let me know.
You need to separate your concerns. In your code behind your should have only code that handles view related stuff. Most often my codebehind is empty.
In your ViewModels you should handle your data related logic. So instead of casting the datacontext in your code behind, handle a click with a Commandin your viewmodel.
Since there is no possibility to bind a command to the SelectedItemChanged of your TreeView you can use an interaction trigger.
<TreeView xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding Path=SomeCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
Ruven it is hard to say without some example code. But it could be that you need to implement INotifyPropertyChanged on the ViewModels?
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-property-change-notification
By calling OnPropertyChanged("PropertyName"); in the setter of a viewmodel property the ui will pick up the change.
Also make sure both views are referencing the same object and not copies of the same object.

Caliburn Micro Hello Screens

I have a 3 part question about the "Hello Screens" Caliburn Micro example. (I'm using a WPF port of the project). This concerns the behavior of CustomerView.xaml which contains a "Save" and "Close" button pair. Willing to accept answers to just 1 and 2 as 3 probably becomes trivial at this point!
Whenever the AddressViewModel is activated by the DialogConductorViewModel the Save and Close buttons become disabled. This happens here in the EditAddress function of CustomerViewModel:
Dialogs.ShowDialog(new AddressViewModel());
1) How do the user controls of CustomerView know to become disabled here? My first thought was that CustomerViewModel (CMV) must've deactivated and maybe CM disables the the CustomerView UserControl for us as a result. However, if you override OnDeactivate in CMV the view model isn't being deactivated whenever it shows a dialog with the "ShowDialog" function.
2) From a WPF standpoint how do all the controls on CustomerView become disabled? If I put this code somewhere in CustomerView.xaml and run the project, the CustomerView UserControl remains enabled throughout:
<TextBlock Text="{Binding Path=IsEnabled,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBlock>
However, if I bind to IsEnabled on any control I place onto CustomerView.xaml that control's IsEnabled property is being set to false when a dialog is shown:
<TextBlock Text="{Binding Path=IsEnabled,
ElementName=TryClose }"></TextBlock>
3) I would like to write a style datatrigger to alter other aspects of CustomerView's appearance whenever it's displaying a dialog. Ideally I would be binding to a property on CustomerViewModel but the best I can tell, this ViewModel doesn't inherently know whenever it's displaying a Dialog through the DialogManager unless there's something I've missed. Does CM give me a way to detect whenever this dialog is being displayed from the point of view of the CustVM?
Thanks,
Sean

System.Windows.Controls.UserControl Loaded Event firing when 'Unloaded'

I'm getting a strange phenomenon where the UserControl Loaded Event is firing when the parent window's content control is changing from the current to a new one.
I've tested this behaviour on multiple UserControls and it's happening on all of them.
What I've done:
Window:
<xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"/>
<Controls:TransitioningContentControl Transition="RightReplace" Content="{Binding CurrentViewModel}"/>
When I change the CurrentViewModel Property the corresponding View is loaded into the content control.
UserControl:
<xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Load, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
When the UserControl is loaded (and for some reason when the UserControl is changed to another) the Load method is run.
Possible Solutions:
There are ways I could work around this for example I could create a Boolean property called ShouldLoad with an if formula in the Load method however this seems rather convoluted for what I'm trying to achieve.
I feel that this behavior should not happen however there is probably an explanation for this...
I'm currently encountering this issue in my app as well.
So far I have found that it has something to do with the <Controls:TransitioningContentControl>, how it is used or where it is placed. I changed my UI to only use <ContentControl> and the multiple Loaded events are not happening anymore for me. It also seems to work as expected for the <Controls:MetroContentControl>.
This helped me a lot... As it turns out the loaded event gets fired in many circumstances such as when the tab control is changed.
http://blogs.msdn.com/b/mikehillberg/archive/2006/09/19/loadedvsinitialized.aspx

Use SendKeys in a UserControl

I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.

MVVM Light - Multiple ViewModels (and connecting them up)

I am trying to learn the MVVM pattern (C#), having come from a Windows Forms background. I am using the MVVM Light toolkit, and so far I think it is brilliant.
I have made several small applications, however one thing I am struggling with is introducing a second view.
I want to (for example), have a button on my MainViewModel, which via a RelayCommand, opens up a new Window - let's say an "About" window. I have done hours of research on the web for this however it seems I can't get my AboutViewModel to communicate with/show my AboutView.
I have placed a receiving messenger in the code-behind constructor of the AboutView.xaml - however I can't get it to receive any messages from the AboutViewModel, and thus can't make it 'Show()'.
If anyone has an example of an Mvvm Light WPF app using multiple views that would be great :)
There are two ways I can think to do this easily
The first would be to use a Popup instead of a new Window. For example, I often put properties in my ViewModel for PopupContent and IsPopupVisible, and set those values anytime I want to display my Popup control. For example, a ShowAboutPopup relay command might run something like this:
void ShowAboutPopup()
{
PopupContent = new AboutViewModel();
IsPopupVisible = true;
}
You can display it using a Popup object, or a custom UserControl. I prefer to use my own custom Popup UserControl, which will usually end up looking like this:
<Window>
<Canvas x:Name="RootPanel">
<SomePanel>
<!-- Regular content goes here -->
</SomePanel>
<local:PopupPanel Content="{Binding PopupContent}"
local:PopupPanel.IsPopupVisible="{Binding IsPopupVisible}"
local:PopupPanel.PopupParent="{Binding ElementName=RootPanel}" />
</Canvas>
</Window>
The PopupContent property is a ViewModel (such as an AboutViewModel), and DataTemplates are used to tell WPF to draw specific ViewModels with specific Views
<Window.Resources>
<DataTemplate DataType="{x:Type local:AboutViewModel}">
<local:AboutView />
</DataTemplate>
</Window.Resources>
The other method is to have some kind of ApplicationViewModel that runs on startup, and is responsible for the overall application state, which includes which window(s) are open.
Typically I prefer to have a single ApplicationView that contains a ContentControl to display the current page
<Window>
<ContentControl Content="{Binding CurrentViewModel}" />
</Window>
however it can also be used to manage multiple windows. If you do use it to manage multiple Window objects, be warned that this will not be a pure ViewModel because it will need to access some View-specific objects, and referencing UI objects it not something a ViewModel should do. For example, it may subscribe to receive ShowWindow messages, and upon receiving those messages it would create the specified View and show it, and possibly hide the current window as well.
Personally, I try to avoid multiple windows as much as possible. My usual method is to have a single View that contains consistent application objects for any page, and a ContentControl containing dynamic content that changes. I have an example using this navigation style on my blog if you're interested
As i can see you want a navigation in your MVVM app?
Word goes to the creator of MVVM Light - Laurent Bugnion - with his post about using Navigation Service for switching Views. It's actually about Windows Phone & Silverlight but same should apply to WPF.
Also this answer in related question uses this approach.

Categories