Centering WPF dialog spawned from VSTO Outlook Add-In - wpf

I'm working on an Outlook 2010 add-in that provides a dialog for user input. The code necessary for displaying the button in the ribbon is in its own Outlook 2010 Add-in project. That project has a reference to a WPF User Control Library that is responsible for the bulk of the work.
I use a static method in the WPF User Control Library project that is responsible for configuring Caliburn.Micro correctly and displaying the dialog. All of this works as expected except that I cannot figure out how to correctly position the dialog. I would like it to display centered over the Outlook window. I know I have access to the Microsoft.Office.Interop.Outlook.Application.ActiveWindow(), but I do not see how that helps me since I cannot translate it to a PlacementTarget as expected in the settings for Caliburn.Micro WindowManager's ShowDialog method.
WPF User Control Library
namespace WpfUserControlLibrary {
public static class Connector {
public static void ShowDialog() {
new AppBootstrapper();
var windowManager = IoC.Get<IWindowManager>();
windowManager.ShowDialog( new ShellViewModel() );
}
}
}
Outlook 2010 Add-in
WpfUserControlLibrary.Connector.ShowDialog();

I was able to track down a solution. Thanks to the help of this question, I was able to pass the appropriate parent window location and size parameters to the Connector. I checked the Caliburn.Micro source and noticed that I'm actually creating a ChildWindow--not a Popup. Therefore, I just needed to set the Top and Left values of the settings for the dialog.
WPF User Control Library
namespace WpfUserControlLibrary {
public static class Connector {
public static void ShowDialog(System.Windows.Rect parent) {
new AppBootstrapper();
var windowManager = IoC.Get<IWindowManager>();
// Popup is always 600 x 400
dynamic settings = new System.Dynamic.ExpandoObject();
settings.Left = (parent.Left + parent.Width / 2) - 300;
settings.Top = (parent.Top + parent.Height / 2) - 200;
windowManager.ShowDialog(new ShellViewModel(), settings: settings);
}
}
}
Outlook 2010 Add-in
var win = ThisAddIn.Application.ActiveWindow();
var parent = new System.Windows.Rect(win.Left, win.Top, win.Width, win.Height);
WpfUserControlLibrary.Connector.ShowDialog(parent);

Related

Dynamically Change Content by clicking RibbonPane using Prism

I'm using Microsoft Ribbon and Prism to develop my application. I have in my main window 2 regions: one for the ribbon and the other to inject a view depending on the button clicked in my ribbon.
That works pretty good, but I would like to have the same functionality if I click a specific ribbon tab.
Has anyone done anything like this using Prism?
As you wanted, here is the code using a Button. This code is in the VM of the Ribbon...when the button is clicked the event goes to OnShowConfiguration. This method load a new View in my GeneralContentRegion and also a new RibbonTab.
private void OnShowConfiguration()
{
loadView(PrismViewsNames.GeneralContentMainView, PrismRegionsNames.ContentRegion);
loadView(PrismViewsNames.GeneralRibbonTab, PrismRegionsNames.RibbonMenuRegion);
}
private void loadView(string viewToShow, string regionWhereToShow)
{
var regionManager = (RegionManager)ServiceLocator.Current.GetInstance<IRegionManager>();
var uri = new Uri(viewToShow, UriKind.Relative);
regionManager.RequestNavigate(regionWhereToShow, uri);
}
Myabe that helps you, Ayyappan Subramanian ;)

Replicating WPF TabControl selected TabItem design time behavior for a custom control

I'm developing a custom control which shows an inline popup window and I would like to use a similar technique as the TabControl employes so that only popup windows that are selected within the designer or more commonly by placing the cursor within the popup declaration in XAML that it is visualized right within the desiger without having to run the application or change any runtime values by hand.
I've started by duplicating the implementation of the TabControl which I have successfully mimicking everything but it is all copied from Reflector output and Stylesnooper. I've renamed all of the control parts and then replaced the default templates so that the main control uses an ItemsPresenter instead of a ContentPresenter to show the individual popup controls within a Grid panel overlayed on top of one another. So far this is working great too. The problem is that somewhere along the line I lost the ability to have the designer follow the item that is selected in the XAML editor.
Either an explanation of how the TabControl's design time behavior functionality actually works to describe the selected TabItem behavior that I described above or just some pointers on how one could achieve what I'm tryign to do would be great.
To solve a similar problem, I had to create design time support for my custom tab control. Here is a link for WPF Designer Extensibility.
Basically, I created a PrimarySelectionAdornerProvider to handle click interaction and a FeatureConnector<> / FeatureProvider pair for selection changes (including selection changes made in the xaml editor).
The feature provider / connector:
[FeatureConnector(typeof(AutoTabPageSelectionFeatureConnector))]
class AutoTabPageSelectionFeatureProvider : FeatureProvider
{
public AutoTabPageSelectionFeatureProvider()
: base()
{
// sole purpose is to register the connector
}
}
class AutoTabPageSelectionFeatureConnector : FeatureConnector<AutoTabPageSelectionFeatureProvider>
{
public AutoTabPageSelectionFeatureConnector(FeatureManager manager)
: base(manager)
{
SelectionOperations.Subscribe(this.Context, SelectionChanged);
}
private void SelectionChanged(Selection selection)
{
if (selection.PrimarySelection != null)
{
// navigate tree to find parent (custom tab page and custom tab control)
for (ModelItem item = selection.PrimarySelection; item != null; item = item.Parent)
{
// once found, select appropriate tab
}
}
}
}
Edit (more info):
This Microsoft link has a number of links to walk-throughs that should help. Here are the basic steps to get started:
Create a new project, MyAssembly.VisualStudio.Design.dll.
The library should compile to the same location as MyAssembly.dll (important).
Add references to Microsoft.Windows.Design.Extensibility and Microsoft.Windows.Design.Interaction.
Add a reference to your control library.
Create a class called Metadata
Code:
internal class Metadata : IProvideAttributeTable
{
// Accessed by the designer to register any design-time metadata.
public AttributeTable AttributeTable
{
get
{
AttributeTableBuilder builder = new AttributeTableBuilder();
// Add the adorner provider to the design-time metadata.
builder.AddCustomAttributes(
typeof(MyControl), // rename to your control's name
new FeatureAttribute(typeof(MyPrimaryAdornerProvider)), // rename to whatever you will call your PrimaryAdornerProvider
new FeatureAttribute(typeof(AutoTabPageSelectionFeatureProvider)) // rename to whatever you will call your SelectionFeatureProvider
);
return builder.CreateTable();
}
}
}
Create a class MyPrimaryAdornerProvider from PrimarySelectionAdornerProvider (rename to whatever you want). See link for good walk-through.
Create the AutoTabPageSelectionFeatureProvider and AutoTabPageSelectionFeatureConnector from the example above.

WPF TabControl / File open needs to locate child WindowsFormsHost

I'm trying to write a text editor in WPF and I have a problem trying to locate the correct instance of an editor within a TabControl in response to a File -> Open action.
Tab items are added programatically and contain a WindowsFormsHost instance which in turn allows each tab to display an editor provided by the ScintillaNet WinForms component.
When a tab is selected and a user selects File -> Open, I need to locate the correct WindowsFormsHost instance based on the tab selection so I can load the file into the correct Scintilla instance.
Previously, I'd done this in WinForms simply by doing:
tabControl.TabPages[tabControl.SelectedIndex].Controls.Find("Scintilla")
How does this work in WPF?
To follow up regarding the solution I've gone with for now: I've decided to subclass the TabItem class and hold an additional property that references the WinForms ScintillaNet control:
public class CustomTabItem : TabItem
{
public Scintilla EditorControl
{
get; set;
}
}
And when I add new tabs, I just make sure that EditorControl is set to the new instance of Scintilla that is created too:
var editor = ScintillaFactory.Create();
var tab = new CustomTabItem()
{
Header = "Untitled",
Content = new WindowsFormsHost() { Name = "WinformsHost", Child = editor },
EditorControl = editor
};
tabControl.Items.Add(tab);
tab.Focus();
Now when an event is raised, I can query the selected tab and as cast to CustomTabItem in order to access the reference to the respective editor:
var editor = (tabControl.Items[tabControl.SelectedIndex] as CustomTabItem).EditorControl
editor.Text = "text here";
Hope that helps someone else.

Cannot show up WPF application when setting MainWindow manually and composing application (MEF)

I got my hands om MEF for a week now and I am trying to build up a WPF application that loads imported controls from MEF.
I created a WPF application project and removed the default window and application start up URI. Then I handled the application startup event to compose the application:
public partial class App : Application, IPartImportsSatisfiedNotification
{
{...}
private void App_Startup(object sender, StartupEventArgs e)
{
this.Compose();
}
public void Compose()
{
try
{
globalCatalog.Catalogs.Add(new DirectoryCatalog(extensionsDirectoryPath));
CompositionContainer container = new CompositionContainer(globalCatalog);
container.ComposeParts(this);
}
catch (Exception ex)
{
// Do something
}
}
{...}
}
Actually, when debugging and watching objects after imports are satisfied, everything has hierarchically composed fine like I wanted. But when I try to show up the MainWindow of the application an exception is thrown on MainWindow.Show() call:
"Specified element is already the logical child of another element. Disconnect it first."
Though my code in OnImportsSatisfied method seems fine as it is working when not using MEF mecanism:
public void OnImportsSatisfied()
{
Window mainWindow = new Window();
mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
}
I insist on the fact that this works perfectly when not importing controls with MEF. What is surprising is that this code does not work too:
Window mainWindow = new Window();
//mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
So I suspect that ComposeParts is doing a bit more than what it says as it is the only member acting on my actual application instance.
Hope someone can help me (Glenn?).
Thanks.
Edit:
I discovered that when I remove the IPartImportsSatisfiedNotification interface from my parts, no exception is thrown and the window shows up. But of course the window is empty as I need this OnImportsSatisfied method to set the DataContext of the window to its associated imported view model.
The sample applications of the WPF Application Framework (WAF) show how to use MEF within a WPF application.
I finally discovered that I was importing my WPF user controls by using the default ImportAttribute constructor, which in fact will make a shared instance of the class if the creation policy is not specified during export. And as many of my controls were implementing the same interface and I was binding them in my views, I was actually trying to add this shared user control instance to different visual elements, which is not permited by WPF (and so the exception).
I marked my imports using the RequiredCreationPolicy set to NonShared and everything got back in order! That was all about learning MEF...

WPF & WinForms Integration and Application Class

I am planning to create a WPF application with a main window which would launch various WinForms. Some of the WinForms use the System.Windows.Forms.Application class (DoEvents, Application.Path, etc). Do you think that there will be a problem in doing this?
Can I still use System.Windows.Forms.Application.DoEvents() from a WinForm that is launched from a WPF application?
The main problem will the ability to instantiate the Windows Forms window and set it's owner to that of the WPF window. The Winforms will want a IWin32Window which a WPF window isn't. To get around this, you need to make a custom class.
I found this code on Mark Rendle's blog (I've copied it here as I had to use the Google Cache to access the page).
LINK - WARNING: May not work
class Shim : IWin32Window
{
public Shim(System.Windows.Window owner)
{
// Create a WindowInteropHelper for the WPF Window
interopHelper = new WindowInteropHelper(owner);
}
private WindowInteropHelper interopHelper;
#region IWin32Window Members
public IntPtr Handle
{
get
{
// Return the surrogate handle
return interopHelper.Handle;
}
}
#endregion
}
and it's method of use:
namespace System.Windows.Forms
{
public static class WPFInteropExtensions
{
public static DialogResult ShowDialog(
this System.Windows.Forms.Form form,
System.Windows.Window owner)
{
Shim shim = new Shim(owner);
return form.ShowDialog(shim);
}
}
}
I haven't tested this code, but reading around the internet, it appears that you can host Winforms windows inside of a WPF app.
I just found this link on MSDN that has a very detailed description of how to interop a Win32 control/window in a WPF application.
Hope these help you out.
I've been doing this sometimes and didn't encounter any problem.
However i don't really recommend it, you should prefer WPF when you are in a WPF Application.
for exemple if you want application path use this :
System.Reflection.Assembly.GetExecutingAssembly().Location

Resources