Dynamic Application-level resources are not dynamic when hosted in ElementHost - wpf

I'm hosting a WPF UserControl in a WinForms container. Now, I want to be able to theme/skin the UserControl. To do this, I've got several resource dictionaries that define the "skins." When my app starts up I create a "new System.Windows.Application()" so that Application.Current exists. To change the theme the old skin is removed and a new skin is merged into the Application level resource dictionary at runtime. However, this does not change any of the dyanamically referenced resources in the UserControl. I tried this in a straight WPF application and it worked just fine. Am I missing something, or is it not possible to do this at all? By the way, if I add a skin into the application resources before the UserControl is initialized it will work but I cannot change the skin after that.
To repo this in the most basic way:
Create a new WinForms application. Add a WPF UserControl to the app. This is simple enough:
<UserControl ...>
<Grid>
<Button
Background="{DynamicResource ButtonBG}"/>
</Grid>
</UserControl>
Create two ResourceDictionaries, White.xaml and Black.xaml (or whatever) that have a SolidColorBrush with the key ButtonBG with respective color. In Form1.cs, add two Buttons and an ElementHost. Set the child of the ElementHost to an instance of the UserControl we just created. Wire up the buttons to events that swap the skin:
private void White_Click(object sender, EventArgs e)
{
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\White.xaml", UriKind.Relative)));
}
private void Black_Click(object sender, EventArgs e)
{
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\Black.xaml", UriKind.Relative)));
}
In Program.cs, ensure that Application.Current exists and set the initial skin:
[STAThread]
static void Main()
{
new System.Windows.Application();
Application.Current.Resources.MergedDictionaries[0] =
(ResourceDictionary)Application.LoadComponent(
new Uri(#"\WpfThemes;component\White.xaml", UriKind.Relative)));
...
}
Now, when the White button is clicked I would expect the button in the UserControl to turn white and when the Black button is clicked I would expect the button to turn black. This does not happen, however.
Does anyone know why? Is there a solution?
Edit: Idea: Perhaps, if there's a way to force re-evaluation of DynamicResources when the theme changes, that would work.
Thanks,
Dusty

I think this may be an overlooked issue in the WPF framework.
From what I can tell via Reflector, it appears that when the Application resource dictionary is catastrophically changed (a change that will likely have wide ranging effects like adding, removing, or replacing a skin), there is code that loops over all of the Windows in the application and forces them to re-evaluate their DynamicResources. However, other elements that I would consider top-level in WPF like ElementHosts do not get the same treatment. This leads to the behavior that I'm experiencing.
My workaround to this issue is to manually go through all of my ElementHosts individually and add, remove, or replace the skin ResourceDictionary file. It's not perfect, but it gets the job done.

Dr. WPF came to my rescue when I was trying to do something similar. He shows how to create the Application object in WinForms. Now you can reference everything as StaticResource just like in a WPF application.
http://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/

Another workaround would be to create a dummy window and specify the content of the elementhost as content.
If you look into the Application and check how it handles changes of resourcedictionaries, you see that it only notifies windows..
The only thing you should remind is to never show the window (-> exception), and to close it when disposing the elementhost, so the application can shutdown properly.

Related

WPF & MVVM, right way to do it

So I am doing my first WPF MVVM app. Just learning the right principle of MVVM, but there are some things that I don't understand ...
I already have several user controls defined.
First question is what is better to use, UserControl or DataTemplates to change content of the MainWindow?
And how to make "Binding" in the "MainWindow.xaml" to change UserControl/DataTemplates when button is pressed?
For example, When "next" button is pressed then contents of main window disappear and content of user control comes on the screen of "MainWindow.xaml".
Maybe with "" binding, to disable it and enable it?
I found some example which function on DataTemplate A Simple MVVM Example. It helped me to implement some things, but I see some discussions over "UserControl" vs. "DataTemplate" and how to do it? So now I am confused :)
I recently made a WPF application with the MVVM pattern, and I did the following:
I have one 'Window', the mainwindow, and in this window all UserControls are loaded.
Every UserControl has a different Viewmodel, for examples a "GeneralSettingsUserControl" has a GeneralSettingsViewModel for validation and databinding.
Every UserControl has its own codebehind where data is bound to its ViewModel
The following code I found on the internet (I don't know the url anymore) but for me it did the trick to change de ContentControl in the mainwindow.
Switcher.cs:
public static mainWindow mainWindow;
public static void switchPage(UserControl p_objNewPage)
{
mainWindow.navigate(p_objNewPage);
}
mainWindow.xaml.cs
public void navigate(UserControl nextPage)
{
PageContent.Children.Clear();
PageContent.Children.Add(nextPage);
PageContent.LastChildFill = true;
}
PageContent is the name of the Grid where the main content is located. In every UserControl you can call the Switcher.switchPage(new UserControl) to change the content of the window. So when you click a button you can call this method.
Hope it helps and good luck.

WPF How to handle Cut, Copy, Paste toolbar buttons with UserControls and WindowsFormsHost

I'm hoping someone out there can help me. I'm trying to convert a legacy winforms app to WPF using MVVM. I've broken up the main window of the application into 4 main UserControls. The UserControls display different types of data objects and each UserControl has it's own ViewModel. Some of the data objects are inter-changeable between the different UserControls, for instance 'User Control 1' can contain strings objects and so can 'User Control 2 and 'User Control 3' (see diagram below).
My question is how can I handle the Cut, Copy, Paste commands in the toolbar? To possibly make things more complicated, each UserControl can contain a selected object at the same time as the other UserControls contain a selected object and User Control 2 is a WindowsFormsHost wrapper around a winforms control.
So far I've tried using ApplicationCommands but I can't even get them to fire. I've pasted a snippet of the code I thought would work using the ApplicationCommands below. Any help with this would be really appreciated.
<Button Command="ApplicationCommands.Cut" />
and on the UserControls
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut" Executed="executed_Cut" CanExecute="canExecute_Cut" />
</UserControl.CommandBindings>
and finally in the UserControl's code behind (I know this isn't great)
public void executed_Cut(Object sender, ExecutedRoutedEventArgs e)
{
//execute code here
}
public void canExecute_Cut(Object sender, CanExecuteRoutedEventArgs e)
{
//can execute code here
}
I successfully use the behavior approach described in this question to avoid putting any code in my view model. Then whenever focus goes to a control which has a copy/paste behavior defined the toolbar cut/copy/paste buttons "light up" accordingly.

WPF routed events handling in UserControl

I have a WPF UserControl that contains a button. I also have a WPF Window that contains a button.
In both the UserControl and the Window I place the following line in XAML:
UIElement.PreviewMouseLeftButtonDown="OnPreviewMouseLeftButtonDown"
and in 'OnPreviewMouseLeftButtonDown' I've place a debug print that displays args.Source.
When I click on the button that is inside the window, I get the button as the EventArgs source. However, when I click the button that inside the UserControl (which is also inside a window, so I could test it, but not the same window) I get the UserControl as the EventArgs source.
I tired to see if there is some decorator around the UserControl (using snoop) but it seems straight forward.
I can't understand what is so special about UserControl in WPF that I don't get the right sender. Can someone please explain to me what am I missing?
While the question is old, I recently ran into a similar problem myself, involving the ContextMenuOpening event. Some amount of searching yielded the source code to UserControl here, which contains the following section:
// Set the EventArgs' source to be this UserControl
internal override void AdjustBranchSource(RoutedEventArgs e)
{
e.Source=this;
}
So, apparently UserControl sets the Source of ANY routed event to itself. I have no idea why it does this, though...
Use e.OriginalSource as mentioned above. If you need to find the button, then you can use VisualTreeHelper.GetParent to find the actual Button control.

Is it more efficient to build the "visual tree" in xaml rather than programmatically?

I’m working on a WPF application with a tabbed MDI interface. Basically, the user clicks a menu item and this creates and adds a new tab. A new 'MyUserControl' is created and set to be the tab's Content. Something like this:
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
this.TabControl.Items.Add(new TabItem() { Content = new MyUserControl() });
}
MyUserControl is composed of several nested controls (approx. 8 controls). When this approach is wired up to the actual control, the performance is unacceptable.
Unless I’m losing my mind, I’ve noticed that the performance hit seems to be much less when declaring the tab and content in xaml ahead of time and simply toggling the tab item's Visibility property:
<Controls:TabControl x:Name="TabControl" Grid.Row="1">
<Controls:TabControl.Items>
<Controls:TabItem x:Name="MyTabItem" Visibility="Collapsed">
<Controls:MyUserControl x:Name="MyUserControl" />
</Controls:TabItem>
</Controls:TabControl.Items>
</Controls:TabControl>
and
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
this.MyTabItem.Visibility = Visibility.Visible;
}
Can anyone explain this? Is it really more efficient to build the "visual tree" in xaml rather than programmatically? Or has the performance hit in my second approach just been moved to the overall form's load instead of when the menu item is clicked as in the first approach?
The second approach definitely seems to perform much better. Any thoughts?
It is no more efficient to declare it it xaml. I think you are correct in thinking that the performance hit has been moved to the form load.
If it is taking too long to load, maybe it is doing too much work in its constructor. See if you can minimize the work done during the loading of the control.
Otherwise, if the problem is just the sheer amount of controls in the user control, maybe you could keep a fully loaded tab in memory until it is ready to be used. Once the user clicks the menu item, add it to the tabcontrol and then start loading a new one in a background thread.
Yes, you moved the performance hit to the initialization of the Window. Even collapsed, the instance of the UserControl has been made - you've added it explicitly to the TabControl. Opening a tag in xaml is the same thing as saying new. Collapsed controls have to be constructed because even if you can't see it or interact with it on screen, other controls can have bindings to it and code-behind can work with the instance.
If you prefer your first approach, try caching the instance of the UserControl if you can instead of creating a new one each time.

DockPanel and User Control UI with Ribbon Control

I have a ribbon control with multiple buttons that need to display different windows/user control in the main area of the window. I'm thinking about creating a user control for each 'functional area' that relates to it's button in the ribbon.
Something like
Public Class RibbonViewModel
Public ReadOnly ucPreferences As UserControl = New ucPreferences
Public ReadOnly ucMain As UserControl = New ucMain
End Class
User clicks the Preference button and I'll do
dockMain.Children.Clear()
dockMain.Children.Add(oRibbon.ucPreferences)
This is working as far as the layout goes but I'm not sure what issues I might run into.
This will certainly work; however a more robust approach would be making use of Prism...more specifically the IRegionManager.
What this provides is a way to define regions within your application where you can push content to live within the region. An example would be...
<ad:DockingManager Grid.Row="1" Margin="0">
<ad:DocumentPane x:Name="WorkspaceRegion" prismrgn:RegionManager.RegionName="WorkspaceRegion"/>
</ad:DockingManager>
...where ad is the namespace for the AvalonDock assembly; however it could be your DockPanel just the same. This can then be referenced in the code behind as follows...
_regionManager.AddToRegion("WorkspaceRegion", workspaceContent);
_regionManager.Regions["WorkspaceRegion"].Activate(workspaceContent);
...where _regionManager is an instance received via DI within the contructor of the object placing workspaceContent in the WorkspaceRegion.
This provides nice decoupling with regard to the Ribbon action which will drive the interface to be displayed for that given action. It also provides an abstraction on the region itself, ie..is it a DockPanel or some other control.
As stated previously your initial approach will work. Whether you should opt for a framework such as Prism to isolate concerns as well as increased functionality is dependent on the scale of this project both now and in the future.

Resources