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
Related
I have a UserControl with two buttons in my Window. The UserControl has it's own DataContext with two corresponding Commands.
Naturally, the binding on the buttons was written like this
Command="{Binding SaveCommand}"
However, we have two launch mechanisms for our modules: one is via a WPF launcher and the other is via our old WinForms menu. If we use the WinForms menu, the binding does not work anymore. Instead the following Binding was the only way to make it work.
Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
Is this due to WinForms in combination with the way commands bubble up or something along those lines?
Note 1: Other modules seem not to be afflicted somehow. It might really be this exact structure / the double DataContext (window/control)
Note 2: It might be worth noting that we use Telerik's RadButton instead of regular buttons.
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.
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.
I have seen somewhere this is 100% doable using blend behaviors, cannot find example.
Even better example would be to pass event args and/or sender as CommandParameter to specific command.
<i:Interaction.Triggers>
<i:EventTrigger EventName="SizeChanged">
<ei:CallMethodAction MethodName="WndSizeChanged"
TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
WndSizeChanged method should be public and have same signature as event delegte it subscribing to.
a comfortable way to create behaviors is to use the DelegateCommand approach as in Prism:
Read here: Prism behavior
Caliburn Micro has some nice ways to do this.
From the docs:
<Button Content="Remove"
cal:Message.Attach="Remove($dataContext)" />
$eventArgs – Passes the Trigger’s EventArgs or input parameter to your Action. Note: This will be null for guard methods since the trigger hasn’t actually occurred.
$dataContext – Passes the DataContext of the element that the ActionMessage is attached to. This is very useful in Master/Detail scenarios where the ActionMessage may bubble to a parent VM but needs to carry with it the child instance to be acted upon.
$source – The actual FrameworkElement that triggered the ActionMessage to be sent.
$view - The view (usually a UserControl or Window) that is bound to the ViewModel.
$executionContext - The actions's execution context, which contains all the above information and more. This is useful in advanced scenarios.
$this - The actual ui element to which the action is attached.
Is it possible to create a command behavior using Prism's CommandBehaviorBase class for Silverlight's grid? I know that it is only intended for actual controls, so I was wondering if anyone might know if a workaround. I would like to create an attachable mouse over behavior for a grid, that executes a specific command, and ideally would like to use Prism for this approach, just can't seem to use CommandBehaviorBase for a Grid.
Thanks.
The arguably easier way to achieve this is to use Triggers. Doesn't require you to write any code, all you have to do is this:
<Grid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<si:InvokeDataCommand Command="{Binding DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
</Grid>
Here the DoSomethingCommand (defined in a ViewModel) will trigger when MouseEnter event is fired on the Grid.