I made a reuseable control for our little team in WPF: a simple Dialog, that we use often.
The handling of the window is done in a private method:
private static void ExecuteInternal(
Window owner,
string windowTitle,
string label,
object operation,
MyDialogSettings settings)
{
MyDialog dialog = new MyDialog(windowTitle, settings);
dialog.Owner = owner;
dialog.Label = label;
ShowDialog();
}
The public call has a System.Windows.Window as parameter (--> WPF Window) and my function sets the Owner to this window.
Now my colleage wants to use this Window from a Windows Forms application.
My first thought was to overload the public function call with a form and then handle it with the WindowInteropHelper internally (see: http://blogs.msdn.com/b/mhendersblog/archive/2005/10/04/476921.aspx)
But I then would have to reference Windows.Forms in every (WPF)-Project that uses my library.
Because I cannot access the window instance from outside the WindowInteropHelper-thing cannot done in the Forms application.
Any ideas?
The problem I see with this approach is that the reusable control is not reusable at all. As you note, the main method uses WPF-only types that cannot be used anywhere else, so reusage on WinForms is not possible as it stands now.
To be able to do so you would need to extract the main logic and visuals of the dialog window into an user control (so it can be hosted in WinForms). This is the first thing I would do:
<UserControl .......>
<Grid>
<!-- Other controls you have on the dialog -->
<Button Content="Accept"/>
</Grid>
</UserControl>
Pretty easy, just move all the content to an user control. Then the window itself becomes trivial by just including this control:
<Window .......>
<controls:MyDialogContent ..../>
</Window>
So far nothing changes, and the method you posted is works exactly the same as before. We'll leave it as being WPF-only and in turn implement an almost-equal method for WinForms.
On the WinForms project, now you have to create the form that will take the place of MyDialog. This form can be as simple as the WPF version, just an ElementHost that will contain the UserControl that you separated previously, with maybe any properties/methods needed for your logic.
The last part is to provide a WinForms method to call it, akin to the WPF one. This could be as simple as something like this:
private static void ExecuteInternal(Form owner,
string windowTitle,
string label,
object operation,
MyDialogSettings settings)
{
MyDialogForm dialog = new MyDialogForm(windowTitle, settings);
dialog.Owner = owner;
dialog.Label = label;
dialog.ShowDialog();
}
Related
i've a WPF openned from a button in the ribbon of Microsoft Outlook, for an addin i'm developing.
private void _HandlerUserSettings(Office.CommandBarButton ctrl, ref bool cancel)
{
var windowUserSettings = new WpfUserSettings();
windowUserSettings.Show();
}
if i close the WPF from its close button, if i click again the button to handle the show of the window, it doesn't work.
i guess the close action does something more than a simple Hide, so my goal is to override the closing action. i tried to work on Closing method *WpfUserSettings_Window_Closing* of my xaml window:
<Window x:Class="TrainingForgeOutlookAddin.View.WpfUserSettings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="700" Title="User Settings"
WindowStartupLocation="CenterScreen" Loaded="WpfUserSettings_OnLoaded"
Closing="WpfUserSettings_Window_Closing">
....
</Window>
do you have any suggestions or howto to read, that i didn't find?
thank you
As far as I know, the close function will destroy the window as you cannot call
window.Show()
multiple times. This means that all the data in the window has been lost. This makes sense, since a Window is just like your normal window that hosts the root of your application. The way to do this, is to simply create a whole new window before calling .Show(). However, this creates the issue that your data is not saved. Once your open your new window, it will be blank again. While this is not quite part of your question, I think some might find this useful.
For this you will need to make the two Windows talk to each other in order to save data. The parent will hold onto the child's data and then give it to the newly created window to repopulate.
Some people prefer delegates, I prefer to use interfaces for this.
Define a simple interface. This sets up the relationship between the child and the parent window and allows the child to inform it's closing and give it's data back for safe keeping.
public interface IParentWindow
{
void ChildClosed(SomeData data);
}
this will inform the parent window that the child has been closed and now the window can hold on to the data until the window is opened again.
Assign this to the parent window:
public partial class MainWindow : Window, IParentWindow
this will force you to implement the ChildClosed function. Before we do this, you will notice that I have defined SomeData. This can be anything you like as long as both windows use this to send data back and forth.
For me, the implementation of SomeData looks like this:
public struct SomeData
{
public string name;
public string email;
}
We can debate on why I made this a struct, but that is besides the point. You can make this a class if you would like.
Next, implement the child closed function. For this, create a SomeData object in your parent class scope:
SomeData lastWindowData;
And this function can how tell the parent to hold on to it's data
public void ChildClosed(SomeData data)
{
lastWindowData.email = data.email;
lastWindowData.name = data.name;
}
Ok, before we move onto the child window, we have to implement the opening of the window. Since, as I have found (please tell me there is another way around this?), that the Window cannot be reopened once closed. This means that we have to make a whole new window. Put this in your event, whichever opens the window:
childWindow = new ChildWindow(lastWindowData, this);
childWindow.Show();
as you will notice, the window takes the data and "this", which is our parent window. It does this so that it is able to call back to the parent window before being closed and allow the parent window to save the data.
Lets move onto the child window.
Modify the child window's constructor to accept a SomeData and an IParentWindow object. Looking back, the IParentWindow is the window which is opening the child.
public ChildWindow(SomeData data, IParentWindow parent)
Now we get to save the data passed from the parent!
Define a data holder for your window which is the some SomeData object (inside the child window class)
SomeData myData;
and in the constructor
myData.email = data.email;
myData.name = data.name;
so we can repopulate everything. You can figure out the re population part for yourselves. This way, the window has now received the data of the previously closed child window and we can restore it as though it's the previous window!
When you do something in the window (could be anything, anywhere), you can mutate your data. Say I assign a name and email:
myData.email = "gordon#freeman.com";
myData.name = "Gordon Freeman";
Finally but not least, once we close the child window, we can inform the parent window that the child is being closed and we can give it the newly updated data to save once again!
protected override void OnClosed(EventArgs e)
{
parent.ChildClosed(myData);
base.OnClosed(e);
}
rinse and repeat!
NOTE; For the first open, before you have any saved data, you can pass in an object with null variables. You can then, in the child window, figure out whether you need to update the UI or not based on this.
I have the following C# code in a WPF project:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsWindow = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsWindow);
helper.Owner = new IntPtr(ownerHandle);
}
settingsWindow.ShowDialog();
}
The SettingsWindow isn't properly modal to the owner window (i.e. I can focus on, interact with, and even close the owner window while the SettingsWindow is still open). What am I doing wrong?
For context, this code is part of a screen saver program, and the owner window is the Control Panel screen saver selection window (which passes in the handle to use as owner via command line parameter). I know the IF statement is evaluating true and correctly parsing the handle.
I have also tried using the SetWindowLongPtr method from user32.dll (compiling for x64, hence not using SetWindowLong) which is briefly described here and shown in use here. This method works in WinForms, but doesn't seem to work here in WPF. Help me Obi-Wan Kenobi, you're my only hope.
It turns out that using WindowInteropHelper to set the native window as owner of the WPF Window does work, it just doesn't do the whole job. When set this way, the WPF Window will remain visible on top of the native window, even if the native window has focus. However, that is the only effect obtained. The WPF Window does not prevent interaction with the native Window, and the native window can even be closed, without the WPF Window closing or being affected.
In order to get the rest of the desired behaviour, we need to use the EnableWindow function in user32.dll to disable the native Window before calling ShowDialog on the WPF Window, and again to re-enable it once the WPF Window closes.
The modified code looks like this:
private static void RunConfig(string owner)
{
long ownerHandle;
var settingsForm = new SettingsWindow();
if (long.TryParse(owner, out ownerHandle))
{
WindowInteropHelper helper = new WindowInteropHelper(settingsForm);
helper.Owner = new IntPtr(ownerHandle);
NativeMethods.EnableWindow(helper.Owner, false);
settingsForm.ShowDialog();
NativeMethods.EnableWindow(helper.Owner, true);
}
else
{
settingsForm.ShowDialog();
}
}
(Note: The above code is correct in general, but incomplete in the case of screen savers, which is what this code is actually being used for. In the case that this code is being used for the config window of a screen saver, the string passed in for the owner handle is not the handle of the Control Panel window to be used as owner, but rather a handle for a control that is a child of the Control Panel window. The extra step in this case is to get the handle of the parent of that control. We can do this by calling GetParent, also in user32.dll, on the passed-in handle. This will return the real handle we want to use for the owner and EnableWindow calls.)
If anyone from Microsoft ever finds this, maybe consider modifying WindowInteropHelper to properly set all of this up when Owner is assigned and ShowDialog used, since this is the proper complete behavior for modal windows.
Firstly, I have a project with a Windows Form that references another project with WPF forms. The windows form has an elementhost which child is one of the WPF documents in the other project.
Now, on this WPF document I want to have a button that upon a click can open another wpf form. Either as a new standalone WPF form, as a modal or whatever.
I cannot, on the button click event, say
WPFform2 WPFform2=new WPFform2();<br>
WPFform2.Show();
... as many other threads on the net suggest, since the show method does not exist.
My solution does not allow some sort of call that changes the main FormĀ“s elementhost, so that is not an option for me.
All my WPF forms derives from UserControl:
public partial class WPFform1: UserControl
The form must be derived from Window to have the Show() method.
Just create a new window that contains only the form you want to show and call Show on it. Or change the control's base class to Window (you will have to rewrite it both in XAML and in the code behind), nothing should really change, Window supports most of UserControl's features.
I really had no idea what to title this question.
Assume I have a windows form application. The GUI is complex enough to require two custom user controls, "LeftSide" and "Rightside" which each are composed from various buttons, labels, and maybe even another custom user control.
My question:
I am in in the scope of the "Rightside" control. How would I call a method from the "Leftside" control?
I am using Visual Studio 2008.
The simplest solution is to make a property on the RightSide control of type LeftSide, then set it to the LeftSide instance in the form designer.
You can then call public methods on the property.
However, this is poor design.
Each usercontrol should be a self-contained block that doesn't need to directly interact with other usercontrols.
You should consider restructuring your form.
Exact equivalent with standard WF controls: how to keep the text of one text box in sync with another:
private void textBox1_TextChanged(object sender, EventArgs e) {
textBox2.Text = textBox1.Text;
}
Necessary ingredients: an event on your user control that is fired when something interesting happens. And public properties.
I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).
In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)
frmForm fm=new frmForm();
fm.tabMain.Selected=tabVitamins;
I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.
Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.
Please advice.
Thanks,
Karthick
When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.
To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.
For example:
frmForm myForm = FindForm() as frmForm;
if(myForm != null)
myForm.tabMain.SelectedTab = myForm.tabVitamins;
If the control is on some other form, this code won't do anything.
By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.
SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.
It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.
However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.
In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.
Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();
Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.
If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.
Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :
private frmForm ParentForm;
public frmForm UCParentForm
{
set { ParentForm = value; }
}
So then in your main form's code, perhaps in the Load event like this :
private void frmForm_Load(object sender, EventArgs e)
{
TheUserControl.UCParentForm = this;
}
or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().
In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :
private TabControl mainFormTabControl;
public TabControl MainFormTabControl
{
set { mainFormTabControl = value; }
}
imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.
good luck !
You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.
In your user control constructor try something like this.
private frmForm fm;
public YourUserControl(frmForm fm)
{
this.fm = fm;
}
Then you could use.
fm.tabMain.Selected=tabVitamins;
Does that help?