I'm working on an MVVM and AddIns-based application. I want to build the menu (ribbon control) dynamically through the AddIn module which will contain MVVM based assembly. On the host side, I'm unable to fire the command which is bound through ViewModel, which is hosted in separate appdomain from host application. How should I approach this issue of dynamic ribbon tabs generation with all bindings to commands/icons from the respective AddIn module.
Edit:
I've developed an MVVM-based application. All application state and commands are in ViewModel and that is bound to the View, which works as expected.
Afterwards, we write code using Managed Addin Framework (MAF) for .net and incorporated that application through it. The issue is coming that we are unable to generate a menu based on the commands in the Addin at the host side. I wrote a property to return menus from the ViewModel Addin to the host, but it just returns the plain objects and commands do not get propagated through AddIn Framework. Here is a code sample:
public IEnumerable<TabViewModel> MenuTabs
{
get
{
var tab = new TabViewModel{ Header = "Tab 1"};
var group = new GroupViewModel {Header = "Group 1"};
var button = new ButtonViewModel{Content = "Say Hello", Command = HelloCommand};
group.Buttons.Add(button);
tab.Groups.Add(group);
return new[] {tab};
}
}
This code is written in say TestViewModel, and HelloCommand is an implementation of ICommand interface from WPF input library. The HelloCommand does not reach at the host side everything else is available and I'm getting Ribbon control's tabs and groups properly, but the commands are not being transmitted to the Addin Host.
Edit2
The issue is that, we want to show the Ribbon in the AddIn-Host window those buttons refer to the commands in AddIn's ViewModel, the final thing (i can) is to move the menu control to the AddIn too. Has someone encountered same problem and want to share the experience?
Related
I'm working on a project to integrate WPF Windows into a large application written in Visual FoxPro. The project has been progressing for a couple of years with success.
One downside to this project is the limitation of the UI thread. FoxPro is an STA app, so there is only one thread that both UI and application services must share. While the application is retrieving data, the UI is locked up. It is my hope that the method we're using to integrate WPF into this app gives us an opportunity to introduce a separate UI thread for the WPF Windows we're using.
How do we integrate WPF into a STA app?
We build a .NET COM server, not an ActiveX control. Instead we're using a simple COM control that exposes events. Our FoxPro client app subscribes to the events and handles UI requests through these events.
We start with a couple of .NET classes that the COM server will use.
// This class allows us to initialize an Application within a COM server
// since there is no App.xaml to do that for us.
[ComVisible(false)]
public class MyApp : System.Windows.Application
{ }
// This class allows us to assign the FoxPro app's main window as the owner
[ComVisible(false)]
public abstract class MyWindow : System.Windows.Window
{
private System.Windows.Interop.WindowInteropHelper _helper;
public void AssignOwner(int hWnd)
{
_helper = new WindowInteropHelper(this);
_helper.Owner = new IntPtr(hWnd);
}
}
The FoxPro app instantiates an instance of the COM server, subscribes to COM events, then calls a method to show the WPF window. The COM server's Show method first causes this bit of code to execute once per VFP app by delegating it to a static class.
if (Application.Current == null)
_ = new MyApp();
if (Application.Current != null)
{
Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
Application.Current.DispatcherUnhandledException += Application_DispatcherUnhandledException;
}
The Show method then builds a Window and displays it. There's nothing special here.
// Build a business model to access data and services through COM events
var model = new MyModel(this);
// ShellView is an implementation of MyWindow
var window = new ShellView { DataContext = new ShellViewModel(model) };
if (hWnd > 0)
window.AssignOwner(hWnd); && hWnd is a handle to the FoxPro window
window.Show();
The Question
Does the static code that builds the WPF Application instance give us the opportunity to construct a separate UI thread for the Dispatcher used by the WPF components?
I am developing an AddIn for SCVMM (System center for Hyper-V), It is a WPF application that allows users to develop Add-Ins and host their WPF UI (user controls) inside of SCVMM. It uses the Microsoft Add-ins and Extensibility framework.
I am trying to access the parent window instance, the main SCVMM window that hosts my add-in. I have searched on this topic and everybody says it is not possible to do this across App domains.
I am able to get the handle to the window but when i try to convert it into a window I get null.
HwndSource source = (HwndSource)HwndSource.FromVisual(this.btn);
IntPtr handle2 = source.Handle;
Window w = source.RootVisual as Window;
I have a Prism 4.0 enabled WPF application that uses RequestNavigate extensively, and it is working well. I have a scenario where I would like to render part of my UI to an image and store it for later use from a Windows service. I already know how to use RenderTargetBitmap to generate the image, but whenever my code tries to call RequestNavigate, nothing happens. I am calling the bootstrapper, so I would expect that all of the types are loaded, but it just is not working. Can anyone tell me if this should even be possible? Is there anything inherent to RequestNavigate that prevents it from working when there is no UI present?
Individual steps:
First, I call Run on my MefBootstrapper. This loads up all of the assemblies into the AggregateCatalog.
Then, I use MEF CompositionContainer.GetExportedValue to create a WPF UserControl that has a single ContentControl that is assigned a RegionManager.RegionName. This always creates the initial UserControl just fine.
Finally, I call MefRegionManager.RequestNavigate with the region name on my UserControl and the path to another UserControl that I want it to load. This fails to load the UserControl that I am attempting to navigate to.
If these are the only steps that I follow, then the final UserControl fails to load whether I am running from a Windows Service or from within my WPF application. However, if I call SetRegionManager to explicitely add the region from my host UserControl before calling RequestNavigate, then the last UserControl will load properly, as long as the code is run from within the WPF application. If this same code is run from my Windows service, then it still does not load.
RequestNavigate is exactly that, a request to navigate to a loaded (but inactive) region. It won't do any loading itself. You need to separately manage the loading of views (including views within views).
If your nested user control is only over loaded within the parent, and you don't need to manage it at runtime, then you can use ViewDiscovery. You register all the views in advance, and then when the region is created, it looks for (and loads) all the nested views. If you need to manage the views at runtime, switch them in and out, etc, then you can use ViewInjection.
For ViewDiscovery, in the Initialize method of the ModuleInit class in your module, insert the following line:
_regionManager.RegisterViewWithRegion("RegionNameOfYourNestedControl",
() => this.container.Resolve<NestedUserControl>());
Then when your region is loaded, the NestedUserControl will automatically be loaded into your ContentControl (region).
For more detail on ViewDiscovery, ViewInjection and UI composition, have a read of the prism documentation
I have a desktop application that is based on the Caliburn Micro framework. Everything works great. Now I am trying to port the same app into Outlook as a plugin.
In the desktop app, based on an entry inside app.xaml, Caliburn knows where to find the bootstrapper and instantiates it.
In case of the Outlook plugin, I've created an overridden bootstrapper that I instantiate explicitly inside ThisAddIn.ThisAddIn_Startup(). This one of course does not use the Application object.
I can even invoke a particular view using code similar to this
var windowManager = IoC.Get<IWindowManager>();
windowManager.ShowDialog(new MyViewModel());
And that will cause the view associated with the view model to be shown in a modal window on top of Outlook (hence validating that Caliburn Micro is able to find a view from a view model inside my Outlook plugin)
What I haven't figured out how to do is instantiate the Shell so that I can start using its functionality.
My expectation was that since my bootstrapper derives from BootStrapper, and I have registered my shell view model implementation with the MEF container as exporting IShell, Caliburn will automatically instantiate the shell view model and start using it. That is not happening.
My goal is to get the shell loaded inside my plugin's task pane as the container for other views that I will be loading based on user actions.
Any ideas or tips on how I can get this to work? In general has anyone got a shell implementation loading inside an Outlook or Office plugin's task pane?
Thanks!
Do you mean instantiate via Bootstrapper<Shell>. This uses the Window Manager underneath but I don't think that extends to outlook. There is nothing stopping you using the same code above to initialize your shell manually, composition will handle the rest of the application.
IoC.Get, by default calls Activator.CreateInstance so it is possible your problem is with MEF. The method that drives opening the Shell DisplayRootViewFor() calls this line.
windowManager.ShowWindow(IoC.GetInstance(viewModelType, null), null, settings);
If MEF is not hooked up properly it will fail causing your shell not to load.
I am playing around with the WP7 SDK and the Prism for WP7 beta and have come across a problem I can't figure out (or even a workaround for).
First of all, I'm new into WPF/Silverlight/WP7 and Prism, so I could be overlooking something very obvious.
So I have a Shell page that has my region that is used to hold my content pages, and all of this is working great! Now my problem is that I have a settings control that will allow the users to edit the settings of the application (names, locations, etc). Now I can get this page to work with no problems by having a button on one of my controls that will transition the region manager to the control.
However, I would like to use the application bar on the phone to have the button, but I cannot for the life of me figure out how to get access to my model object from within the page that is opened by the Application bar click. I can only do a NavigationService.Navigate() to a settings page, but the PhoneApplicationPage objects in WP7 do not allow injection on the constructors (the constructors must be parameterless) so I cannot pass in the object instance in that way.
So my question is, how can I access (or pass) objects between pages or controls?
Thanks!
In the examples they use this technique to set the data context of a form after it is navigated to from another form:
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
FrameworkElement root = Application.Current.RootVisual as FrameworkElement;
root.DataContext = some_object;