Call a UserControl method from another UserControl - wpf

I am trying to call a method from a UserControl from a different UserControl. I am not able to trace the UserControl i am trying to call the method from to call the method.
I am trying to call the following method that is in AddDeal.xaml.cs
public void loadDealProducts()
{
InfoBox.Information("loadDealProducts called.", "testing");
}
I am tracing the AddDeal UserControl and trying to call the method loadDealProducts() in file AddDealProducts.xaml.cs using the following method
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
(window as AddDeal).loadDealProducts();
But window is returning null so i can't call the method loadDealProducts.
Instead of getting a Window using GetWindow, is there a way of getting the UserControl? I tried Window.GetUserControl and UserControl.GetUserControl but there is no such method.
sender is the DependencyObject from AddDeal.xaml.cs which i get when i click a button on AddDeal.xaml as following:
<Button Click="BtnAddProducts" CommandParameter="{Binding Path=ProductID}">Add Product</Button>
which calls the following:
private void BtnAddProducts(object sender, RoutedEventArgs e)
{
var button = (Button)sender as DependencyObject;
Window AddProductsDialog = new Window {
Title = "Add Products to Deal",
Content = new AddDealProduct(button, productID, false, 0)
};
AddProductsDialog.ShowDialog();
}
As you can see i am sending button which is a DependencyObject on AddDeal.xaml.cs/xaml
When it opens a new window AddDealProduct, it has AddDealProduct.xaml (UI file) and its .xaml.cs code-behind file. In this file i want to call a function from the calling UserControl(AddDeal).

Ok I solved it.
I am sending a DependencyObject sender that i get from Button Click event from source Window UserControl, to another UserControl class, as a parameter.
Then i use the sender object to resolve a UserControl and call a function in its class from a different UserControl class.
To call the function i do the following:
AddDealUserControl ownerx2 = FindVisualParent<AddDealUserControl>(sender);
ownerx2.loadDealProducts();
FindVisualParent helper class:
public static T FindVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null) return null;
// check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return FindVisualParent<T>(parentObject);
}
}
Hope it helps.

Related

Winforms WebBrowser control without IE popups not appearing [duplicate]

I am trying to implement a simple web browser control in one of my apps. This is to help integrate a web app into a toolset i am creating.
The problem is, this web app absolutly loves popup windows....
When a popup is opened, it opens in an IE window which is not a child of the MDI Container form that my main window is part of.
How can i get any and all popups created by clicking links in my WebBrowser to be a child of my MDI container (similar to setting the MDIParent property of a form)?
Thanks in advance.
The web browser control supports the NewWindow event to get notified about a popup window. The Winforms wrapper however does not let you do much with it, you can only cancel the popup. The native COM wrapper permits passing back a new instance of the web browser, that instance will then be used to display the popup.
Taking advantage of this requires some work. For starters, use Project + Add Reference, Browse tab and select c:\windows\system32\shdocvw.dll. That adds a reference to the native COM interface.
Create a form that acts as the popup form. Drop a WebBrowser on it and make its code look similar to this:
public partial class Form2 : Form {
public Form2() {
InitializeComponent();
}
public WebBrowser Browser {
get { return webBrowser1; }
}
}
The Browser property gives access to the browser that will be used to display the web page in the popup window.
Now back to the main form. Drop a WebBrowser on it and make its code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
webBrowser1.Url = new Uri("http://google.com");
}
SHDocVw.WebBrowser nativeBrowser;
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
nativeBrowser = (SHDocVw.WebBrowser)webBrowser1.ActiveXInstance;
nativeBrowser.NewWindow2 += nativeBrowser_NewWindow2;
}
protected override void OnFormClosing(FormClosingEventArgs e) {
nativeBrowser.NewWindow2 -= nativeBrowser_NewWindow2;
base.OnFormClosing(e);
}
void nativeBrowser_NewWindow2(ref object ppDisp, ref bool Cancel) {
var popup = new Form2();
popup.Show(this);
ppDisp = popup.Browser.ActiveXInstance;
}
}
The OnLoad method obtains a reference to the native COM interface, then subscribes an event handler to the NewWindow2 event. I made sure to unsubscribe that event in the FormClosing event handler, not 100% sure if that's necessary. Better safe then sorry.
The NewWindow2 event handler is the crux, note that the first argument allows passing back an untyped reference. That should be the native browser in the popup window. So I create an instance of Form2 and Show() it. Note the argument to Show(), that ensures that the popup is an owned window. Substitute this as necessary for your app, I assume you'd want to create an MDI child window in your case.
Do beware that this event doesn't fire for the window displayed when Javascript uses alert(). The browser doesn't treat that window as an HTML popup and doesn't use a browser window to display it so you cannot intercept or replace it.
I found that the best way to do this was to implement/sink the NewWindow3 event
Add the reference to c:\windows\system32\shdocvw.dll as mentioned in the other answers here.
Add event handler
SHDocVw.WebBrowser wbCOMmain = (SHDocVw.WebBrowser)webbrowser.ActiveXInstance;
wbCOMmain.NewWindow3 += wbCOMmain_NewWindow3;
Event method
void wbCOMmain_NewWindow3(ref object ppDisp,
ref bool Cancel,
uint dwFlags,
string bstrUrlContext,
string bstrUrl)
{
// bstrUrl is the url being navigated to
Cancel = true; // stop the navigation
// Do whatever else you want to do with that URL
// open in the same browser or new browser, etc.
}
Set "Embed Interop Types" for the "Interop.SHDocVw" assembly to false
Set the "local copy" to true.
Source for that help MSDN Post
Refining Hans answer, you can derive the WebBrowser for accessing the COM without adding the reference. It is by using the unpublished Winforms WebBrowser.AttachInterface and DetachInterface methods.
More elaborated here.
Here is the code:
Usage (change your WebBrowser instance to WebBrowserNewWindow2)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.webBrowser1.NewWindow2 += webBrowser_NewWindow2;
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
webBrowser1.NewWindow2 -= webBrowser_NewWindow2;
base.OnFormClosing(e);
}
void webBrowser_NewWindow2(object sender, WebBrowserNewWindow2EventArgs e)
{
var popup = new Form1();
popup.Show(this);
e.PpDisp = popup.Browser.ActiveXInstance;
}
public WebBrowserNewWindow2 Browser
{
get { return webBrowser1; }
}
}
Code:
using System;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace SHDocVw
{
public delegate void WebBrowserNewWindow2EventHandler(object sender, WebBrowserNewWindow2EventArgs e);
public class WebBrowserNewWindow2EventArgs : EventArgs
{
public WebBrowserNewWindow2EventArgs(object ppDisp, bool cancel)
{
PpDisp = ppDisp;
Cancel = cancel;
}
public object PpDisp { get; set; }
public bool Cancel { get; set; }
}
public class WebBrowserNewWindow2 : WebBrowser
{
private AxHost.ConnectionPointCookie _cookie;
private WebBrowser2EventHelper _helper;
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void CreateSink()
{
base.CreateSink();
_helper = new WebBrowser2EventHelper(this);
_cookie = new AxHost.ConnectionPointCookie(
this.ActiveXInstance, _helper, typeof(DWebBrowserEvents2));
}
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void DetachSink()
{
if (_cookie != null)
{
_cookie.Disconnect();
_cookie = null;
}
base.DetachSink();
}
public event WebBrowserNewWindow2EventHandler NewWindow2;
private class WebBrowser2EventHelper : StandardOleMarshalObject, DWebBrowserEvents2
{
private readonly WebBrowserNewWindow2 _parent;
public WebBrowser2EventHelper(WebBrowserNewWindow2 parent)
{
_parent = parent;
}
public void NewWindow2(ref object pDisp, ref bool cancel)
{
WebBrowserNewWindow2EventArgs arg = new WebBrowserNewWindow2EventArgs(pDisp, cancel);
_parent.NewWindow2(this, arg);
if (pDisp != arg.PpDisp)
pDisp = arg.PpDisp;
if (cancel != arg.Cancel)
cancel = arg.Cancel;
}
}
[ComImport, Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch),
TypeLibType(TypeLibTypeFlags.FHidden)]
public interface DWebBrowserEvents2
{
[DispId(0xfb)]
void NewWindow2(
[In, Out, MarshalAs(UnmanagedType.IDispatch)] ref object ppDisp,
[In, Out] ref bool cancel);
}
}
}
I know the question is very old but I solved it this way: add new reference, in COM choose Microsoft Internet Controls and in the code, before the click that opens a new window add the following:
SHDocVw.WebBrowser_V1 axBrowser = (SHDocVw.WebBrowser_V1)webBrowser1.ActiveXInstance;
axBrowser.NewWindow += axBrowser_NewWindow;
and then add the following method:
void axBrowser_NewWindow(string URL, int Flags, string TargetFrameName, ref object PostData, string Headers, ref bool Processed)
{
Processed = true;
webBrowser1.Navigate(URL);
}

Refresh or Close wpf window

I have a wpf window that shows Bing map whith som bushpin on it,
The window is getting opened by button click in code behind.
Each time i click on the button, new window shows up with current new data.
How can i close the "allready" opened window and open the a new one with the new data, or maybe refresh the current opened window with the new data.
here is my code:
IList<object> rowsToExport = getRows();
BingMapWindow window = new BingMapWindow(rowsToExport);
// somthing like this
if(window.IsOpened)
window.Close;
window.show();
OR
if(window.IsOpened)
window.refresh();
Have you considered storing reference to that window outside method and just checking it for null?
BingMapWindow window;
private void CloseWindow()
{
if(window != null)
{
window.Close();
window = null;
}
}
private void OpenWindow(BingMapWindow window)
{
this.window = window;
this.window.Show();
}
private void ButtonHandler()
{
CloseWindow();
var bingWindow = new BingMapWindow();
OpenWindow(bingWindow);
}

Open other window from window

I have some academic question here. I read this question WPF MVVM Get Parent from VIEW MODEL and concluded that ViewModel should not opens any windows itself. So I use Messenger now to send message to ViewModel's Window and Window opens other window - NewWindow. It works fine, but what if NewWindow does something and get some Result has to be passed in MainWindow for further actions? More detailed:
NewWindow opened by button click in Window (OpenNewWindowCommand) and made some calculations.
After calculations NewWindow got some Result (does't matter what exactly is it) and rise a corresponding event - GotSomeResult, where event arg is Result.
This Result has to be passed in MainWindow to further processing, so I bind event handler to GotSomeResult event.
Below you can see all required code to illustrate this scenario.
MainWindow code-behind:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
Messenger.Default.Register<NewWindowMessage>(this, OpenNewWindow);
}
private void OpenNewWindow(NewWindowMessage message)
{
var newWindow = new NewWindow();
var newWindowViewModel = (NewWindowViewModel) message.Target;
newWindowViewModel.GotSomeResult += ((MetaWindowViewModel)DataContext).ProcessResult;
newWindow.Owner = this;
newWindow.DataContext = newWindowViewModel;
newWindow.ShowDialog();
}
MainWindow ViewModel:
public void OpenNewWindowCommand()
{
Messenger.Default.Send(new NewWindowMessage(this, new NewWindowViewModel("OpenWindow"), String.Empty));
}
public void ProcessResult(Object someResult)
{
// Any actions with result
}
newWindowViewModel.GotSomeResult += ((MetaWindowViewModel)DataContext).ProcessResult; --- this string seems problem for me. Is it correct to get access to public method of ViewModel right in theView? Does it violent MVVM pattern?
Why don't you hook the handler to GotSomeResult at the VM level, ie :
public void OpenNewWindowCommand()
{
var newWindowViewModel = new NewWindowMessage(this, new NewWindowViewModel("OpenWindow"), String.Empty)
newWindowViewModel.GotSomeResult += this.ProcessResult;
Messenger.Default.Send();
}
It removes the references to your ViewModel in your codebehind (which indeed should be avoided):
private void OpenNewWindow(NewWindowMessage message)
{
var newWindow = new NewWindow();
newWindow.Owner = this;
newWindow.DataContext = message.Target;
newWindow.ShowDialog();
}

How to stop a new window to be opened every time?

I have a WPF application in which on a click of a menu item a window is opened. If the same menu item is clicked again when the window is already open, it is opening a new window but I don't want a new window to be opened every time.
What I need is, if the window is already open, the same window should be focused not a new window.
//First we must create a object of type the new window we want the open.
NewWindowClass newWindow;
private void OpenNewWindow() {
//Check if the window wasn't created yet
if (newWindow == null)
{
//Instantiate the object and call the Open() method
newWindow= new NewWindowClass();
newWindow.Show();
//Add a event handler to set null our window object when it will be closed
newWindow.Closed += new EventHandler(newWindow_Closed);
}
//If the window was created and your window isn't active
//we call the method Activate to call the specific window to front
else if (newWindow != null && !newWindow.IsActive)
{
newWindow.Activate();
}
}
void newWindow_Closed(object sender, EventArgs e)
{
newWindow = null;
}
I think this solve your problem.
Att,
If your opened windows is used as simple dialog box you can use following code
window.ShowDialog();
when the dialog will show you cannot press any menu items unit you close this window
A rather brute force approach like this also works:
bool winTest = false;
foreach (Window w in Application.Current.Windows)
{
if (w is testWindow)
{
winTest = true;
w.Activate();
}
}
if (!winTest)
{
testWindow tw = new testWindow();
tw.Show();
}
You can create a field and check if it's set:
private Window _dialogue = null;
private void MaekWindowButton_Click(object sender, RoutedEventArgs e)
{
if (_dialogue == null)
{
Dialogue diag = new Dialogue();
_dialogue = diag;
diag.Closed += (s,_) => _dialogue = null; //Resets the field on close.
diag.Show();
}
else
{
_dialogue.Activate(); //Focuses window if it exists.
}
}

Button Click Event Getting Lost

I have a Menu and Submenu structure in Silverlight, and I want the submenu to disappear when the parent menu item loses focus - standard Menu behavior. I've noticed that the submenu's click events are lost when a submenu item is clicked, because the parent menu item loses focus and the submenu disappears.
It's easier to explain with code:
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? SubMenu.Collapsed : SubMenu.Visible;
};
ParentMenuBtn.LostFocus += delegate
{
SubMenu.Visibility = Visibility.Collapsed;
};
SubMenuBtn.Click += delegate
{
throw new Exception("This will never be thrown.");
};
In my example, when SubMenuBtn is clicked, the first event that triggers is ParentMenuBtn.LostFocus(), which hides the container of SubMenuBtn. Once the container's visibility collapses, the Click event is never triggered.
I'd rather avoid having to hide the sub-menu each time, but I'm a little surprised that the Click event is never triggered as a result...
I can't put any checks inside the LostFocus() event to see if my SubMenuBtn has focus, because it does not gain focus until after the LostFocus() event is called. In other words, SubMenuBtn.IsFocused = false when LostFocus() is triggered.
Anyone have any thoughts about this?
I've found out the solution - albeit, it's not as simple, or elegant as I would have liked. The solution is to use a secondary thread that pauses only for a moment before executing.
ie.
public partial class Navigation : UserControl
{
public Navigation()
{
ParentMenuBtn.Click += delegate
{
SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
};
ParentMenuBtn.LostFocus += delegate(object sender, RoutedEventArgs e)
{
HideSubMenu(SubMenu);
};
SubMenuBtn.Click += delegate
{
//Sub Menu Button actions...
};
private void HideSubMenu(UIElement subMenu)
{
//Get the Main Page
App app = (App)Application.Current;
MainPage mainPage = (MainPage)app.RootVisual;
Thread thread = new Thread(Navigation.HideSubMenu);
thread.Start(new ThreadState(mainPage, subMenu));
}
private static void HideSubMenu(object threadStateObj)
{
ThreadState threadState = (ThreadState)threadStateObj;
//Execute after 5 milliseconds...
System.Threading.Thread.Sleep(5);
threadState.MainPage.Dispatcher.BeginInvoke(delegate() {
threadState.TargetElement.Visibility = Visibility.Collapsed;
});
}
I just use a simple object called ThreadState to handle all the state objects I want to preserve:
public class ThreadState
{
public MainPage MainPage = null;
public UIElement TargetElement = null;
public ThreadState(MainPage mainPage, UIElement targetElement)
{
this.MainPage = mainPage;
this.TargetElement = targetElement;
}
}

Resources