Reference WPF button to Winform .dll - c#

I have a Winform .dll referenced inside my WPF project. Main method of .dll has a parameter for passing a Control object. I need to pass reference of WPF Button control inside that method parameter, but I receieve error : "Value of type 'Button' cannot be converted to 'Control'".
As far as I've seen I can't do that because WPF and Winform controls are entirely different things, but Is there anything I can do ?
P.S.: Main .dll method opens WinForm window next to specified control (WPF control in this case), that is why I need reference of WPF control.

Honestly, the best, easiest, cleanest solution is to make another version of the Main method which accepts System.Windows.Controls.Control instead of System.Windows.Forms.Control. They can even be overloaded (share the same name). The changes to this new version would probably be very few and simple.
However, assuming Main is some huge, complicated method that would be a nightmare to even look at- or you don't have the source code anymore- and you really have to use the existing method, there is one other potential option:
Make a class that inherits from System.Windows.Forms.Control which will act as a wrapper around a System.Windows.Controls.Control. You can use new and/or override keywords to replace the existing properties in System.Windows.Forms.Control and instead expose the equivalent properties of System.Windows.Controls.Control.
To give a rushed example:
class WinformsControlWrapper : System.Windows.Forms.Control
{
protected System.Windows.Controls.Control WrappedControl;
public WinformsControlWrapper(System.Windows.Controls.Control wrappedControl)
{
WrappedControl = wrappedControl;
}
public new int Width
{
get { return (int)WrappedControl.Width; }
set { WrappedControl.Width = value; }
}
}
Of course there are numerous problems even with the above example: the fact that WPF uses double and winforms uses int, and the fact that WPF has both Width and ActualWidth to name a couple. But you should probably be able to write your class to work the way you need it to.
Again, making another version of Main is a much simpler, cleaner and probably easier solution. But if you really can't the above tactic should work.

My solution was to change Main .dll method. Instead of passing Control object I changed parameter to accept Point object - which Is what I need in the end from a control, to get their X and Y coordinates... However WPF and Winform uses different Point library references (System.Drawing.Point and System.Point) which are different types so when calling .dll in WPF I had to convert WPF Point object into type that Winform System.Drawing.Point accepts. That was easiest I could come with.

Related

How to create a DLL library with a standard form layout to use on several forms?

I searched a lot of places but with no answer. I want to create a standard window form with a predefined layout on it like: borderless window, a panel with some code to be able to move the window around, close and minimize buttons, etc.
I imagine that this need to be an DLL so any other project that I create on the future could import and use this DLL. Even if I update the layout on DLL all my projects that use it automatically change instead of I having to manually change all windows forms on every project.
If someone could please explain to me step-by-step on how to do this, create, import and use the DLL, I would be very thankfull.
Edit: Sorry, it's WinForm. I want to be able to use this standard WinForm layout outside my solution, on other projects. The main solution will only have the standard WinForm layout I created, nothing else. The other projects should be able to insert their components, like buttons, panels, inside the standard WinForm layout. I think I might need to use NuGet package instead of DLL.
There are a lot of ways to do what you need. But first of all you need to do some analysis and design. Find out what layouts and controls are needed, what will change and in what ways. Identify possible modes or patterns to implement by the form. Some drawings on paper will help. Start with a single project to keep things very simple, and when you got it working, go on with a DLL or NuGet package. I'll try to explain a very quick and dirty example with a lot of assumptions.
In short, we will create a form that implements three behaviors or modes. Each mode is composed of two factors:
GUI. Controls are added, hidden, changed or moved around its
containers as needed.
Code. Event handlers, private methods and all the code to implement what the mode should do.
Every mode should have a single method performing all the needed changes in GUI and code.
An Enumeration should be created (at namespace level) with all needed mode names.
public enum EditFormMode { Default, CreateClient, EditClient }
The form Constructor should receive one argument of the enumeration type. And a switch will dispatch execution to the method.
{
public FormEditConfig(EditFormMode mode)
{
InitializeComponent();
switch (mode)
{
case EditFormMode.Default:
CreateDefaultMode();
break;
case EditFormMode.CreateClient:
CreateClientMode();
break;
case EditFormMode.EditClient:
CreateEditClientMode();
break;
}
}
private void CreateDefaultMode()
{ // do default stuff }
private void CreateClientMode()
{ // do create client stuff }
private void CreateEditClientMode()
{ // do edit client stuff }
}
And the code to create a form:
private FormEditConfig _theForm ;
_theForm = new FormEditConfig(EditFormMode.CreateClient);
That's the basic mechanism. How to retrieve data from the form to the calling code is very dependent on the concrete implementation.
Once all modes are implemented and tested, go on and create a new project for a class library, move files, add project references, build the solution and test again.

how "intercept" messagebox & modify its content, and back to normal flow?

I have a bunch of messageboxes in an existing app, both simple informations to user and also questions.
I would like to "intercept" them (for sure not the correct IT wording), change automatically its content, and then normally display it to user.
The "OK" or other standard return should be returned/forwarded to the initial messagebox.
The modification function is a kind of translation, but for the purpose of demonstration, lets say that this special function does += " AAA" to the content and += " BBB" to the top header.
Note1: while searching, I have seen several custom message boxes, but
these are additional controls, mainly for changing the button captions
or style, not to "intercept". Please correct.
Note2: fully agree that a better & cleaner MVVM structure would have avoided the
trick needed above, but this big app started some time ago, with a
very small and different aim
As far as I know this isn't possible. You cannot have a reference to a MessageBox, so you cannot access it in any way once it is open.
According to the documentation:
You cannot create a new instance of the MessageBox class. To display a message box, call the static method MessageBox.Show.
This means that you cannot do like the following:
var box = new MessageBox([stuff]);
MS deliberately made the constructor or constructors of that class private (or protected), to make you use the factory method instead (MessageBox.Show();). Note that since they are explicitly defined, just not accessible, this means that no implicit constructor is generated either.
Doing this also won't work:
var box = MessageBox.Show([stuff]);
The Show method doesn't return a reference to the open box itself, but to the DialogResult object after it closes.
As for your situation, the only ways I can think of to solve your problem would be to either go through the program and change the strings, or create a new custom control and ditch the MessageBox entirely. You may be able to find another way, however "intercepting" the MessageBox instances isn't possible.
Assuming that the code uses System.Windows.MessageBox.Show calls using text and caption arguments, you can try defining a public static MessageBox class in a common namespace of your application providing a similar Show method that updates the arguments and calls the original MessageBox.Show method, e.g.:
public static class MessageBox
{
public static void Show(string text, string caption)
{
text += "AAA";
caption += "BBB";
System.Windows.MessageBox.Show(text, caption);
}
}
Note: this will only work if you are able to rebuild the solution from source code, as it requires adding a new source code file (the custom MessageBox class), and then rebuilding the solution.

Visualizer for WPF elements

Trying to create visualizers for some WPF elements including DrawingImage and UIElement etc. While creating a visualizer was trivial, my visualizers always throw exception that the target object types (DrawingImage and UIElement that is) are not marked as serializable.
Further reading revealed that I need to implement VisualizerObjectSource to provide custom serialization. This class is specified as one of the arguments in DebuggerVisualizer attribute. I followed these steps and now my custom serializer gets called, but I don't really know what to do in there. Here is the relevant function that gets called:
public override void GetData(object target, Stream outgoingData)
{
var writer = new StreamWriter(outgoingData);
writer.WriteLine(/*???*/);
writer.Flush();
}
Don't understand exactly what it is expecting from me (a binary-serialized version of the UIElement?) and exactly how do I write a UIElement or a DrawingImage to the outgoing stream. Anyone has done this before?
Finally managed my way through it. It is much simpler than I had thought. For anyone else trying to find their way, here is how it works:
Firstly, GetData() override (read the question) is to be managed by YOU. You have to decide what you want to send in to the visualizer. Send in enough information so that you're able to construct the object back in the Show() call.
For WPF elements, serialization proved to be FAR simpler than what I was thinking. There are built-in XamlReader and XamlWriter classes that you can use to perform serialization/deserialization of WPF objects.
Once you have reconstructed the object in Show(), it is simply a matter of showing it in a Form. Note that Visual Studio supports old-school Form and Control classes only (WinForms that is), not WPF Windows, but you can workaround this issue by placing an ElementHost in your form or control and then assigning reconstructed WPF object as the child of this ElementHost.
You may want to add a ViewBox layer in between your ElementHost and the reconstructed object to fit it elegantly in the available space.
I have uploaded the WPFVisualizers project on GitHub in case anyone is interested. Currently it contains two visualizers, for DrawingImage and UIElement types. Together these cover most of the visual elements of WPF world, but you're free to add more types in case you need some. The project contains VisualizerBase class that contains all the visualizer serialization/communication logic. This makes creating new WPF visualizers as simple as writing 1 line of code, like this:
public class GeometryDrawingVisualizer : VisualizerBase<GeometryDrawing, GeometryDrawingControl>
{
}
That's it. You have created a new visualizer for GeometryDrawing type. The second generic parameter (GeometryDrawingControl in the above example) is the WinForms Control (or Form if you wish) that will constitute the UI of your visualizer. Place an ElementHost in your control and then put in whatever your type needs to render.

Multiple Opening Paths

I have an application that can launch/new up a form(lets call it QuickNoteForm) from many different actions. It can launch the form from many different tabs and mostly are launched thru buttons all over my application.
I basically want to track where it was launched from, ie I need to track its Launch Path.
What would be a good approach to implement this. I was thinking to enclose this as a property that gets set via the constructor of the QuickNoteForm. I want to track from which action this form got launched from.
This is a windows forms application and not a asp.net app.
thanks.
Create an enum that would list all possible paths (or a static class with constants if you worry about maintainability, enums don't work well when compiled and then modified). Add a custom constructor to your form that would accept this enum as a parameter. When you instantiate a form, use that constructor. Basically replace all occurrences of New QuickNoteForm() with New QuickNoteForm(yourEnumValue). For compatibility, add an Unknown = 0 value to the enum, this way calling form's default constructor will work too, just not as useful.
If this approach is not practical (please provide more details on your application), you can also provide a context Control as parameter in your form's constructor. Then have code like If typeOf ctl Is Button AndAlso DirectCast(ctl, Button).Text = "Something" Then and all sorts of crazy stuff. This promotes separation of concerns, i.e. the calling code does not need to know how to call and only pass itself as a parameter, but also makes your code harder to maintain, because you may end up with one giant know-it-all method which would connect all pieces together.

SelectedIndexChange event not firing when using through reflection

I have a windows form with a listview control. I have a selectedIndex changed event where i am performing some action. Through reflection I am trying to set the value of the list view.
But the event is not getting fired. Any help will be helpfull.
Edit
The event looks like
private void LV1_SelectedIndexChanged(object sender, EventArgs e)
{
if (LV1.SelectedItems.Count>0)
{
if (LV1.SelectedItems[0].Text.ToString() == "testing")
{
// Do some work.
}
}
}
I am using relection in another project and changing the selected item as follows
Assembly a = Assembly.LoadFrom(exePath);
Type formType = a.GetType(formName);
testForm = (Form)a.CreateInstance(formType.FullName);
if (testForm != null)
{
Type t1 = testForm.GetType();
FieldInfo fi = t1.GetField(controlName, flags);
object ctrl = fi.GetValue(testForm);
ListView l1 = (ListView)ctrl;
l1.Items[0].Selected = true;
}
Automating another application is fun howver not a trivial task. There's a few options but I guess most of them is out of scope for you. One would be to programatically take over the mouse and keyboard and trough those channels manage the program. Another way would be to manipulate memory, As I said neither are trivial to implement and very easily broken if the aplpication is updated.
I would suggest instead of trying to automate the application to look for infliction points. Are there any service endpoints you could automate and achieve the same result? any API or dll's used by the application you could use instead?
If you really have to automate the application there do exist several frameworks for doing that (usually build for testing purposes). The only one I can think off right now is made by Assima as is ment for training purposes.
I think your problem is here:
testForm = (Form)a.CreateInstance(formType.FullName);
You are creating a new instance of the form. I'm assuming your main project is an exe that runs an shows the form. Your second project is then another exe that runs and wants to change the selected item. By creating a new instance of the form you will be changing the selected item on the new form, not the old one.
What you need to do is somehow pass the form object over to the secondary project. possibly some static method that gets a singleton instance of the form maybe.
I'm still not entirely sure why you are using reflection, you could just give the second project a reference to the first.
The first question I'd ask is: why are you using reflection here? Just set the value through the public API. If you are messing underneath the public API, then yes: it is entirely possible that some events won't get fired.
Perhaps if you could show us exactly how you are doing this?

Categories

Resources