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.
Related
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.
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.
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.
I am using a Queue for a property, and it shows it as a collection in the propertygrid. The only issue, is I can't edit anything inside of it, it shows them as just "Objects" and everything is read only. I know Vector2 works fine in a propertygrid because I have a few of those by themselves. I've been googling, and found something about making a ContainerEditor, but not sure how to make one for a Queue, since it isn't a custom class.
Am I reading this right, or searching for the wrong things?
You need to write TypeConverter and UITypeEditor for converting and editing Queue object. See this and this article for how to go about it. Both articles are bit dated but should be relevant - if there is something simpler has been come with later version of .NET then I am aware of it (you can try googling).
Now, for above to work, you need to decorate the class/type with attributes which is not possible if you use Queue. I will suggest that you inherit a dummy/wrapper class from Queue and apply attributes it. You can even choose specific T in your wrapper class if it make sense (e.g. class MyQueue : Queue<string> { }). Change your property type to use your wrapper class.
I've started using this alot to link elements of my UI to their data backing class (whatever that might be). What are some of the common uses you put the Tag property to use for?
Indeed, do you use it at all? I know I didn't for a very long time.
Just as you describe, the most frequent use of the Tag property I have come across and use in both WinForms, WPF and Silverlight is to indicate the real data that the control relates to. This is especially useful on ListViewItem instances or auto-generated user interface where you want to use the same event handler for multiple objects where only the target data is different (i.e. the action to be performed remains the same).
However, I have also used the Tag to store an enumeration value (though you should avoid value types as it would cause boxing when assigning the value to the Tag property) or a string that is then used to determine the action that needs to be performed instead of the data on which to perform it, and in one particular usage, I stored a delegate so that I could auto-generate some buttons and embed their handlers in the Tag (the handler information was supplied in a data driven manner).
I am sure there are many other ways to use Tag and many other ways to replace the uses of Tag with something more strongly typed, but that's how I've used it.
The Tag property is an ancient (in programming language terms) hold over for controls. To my knowledge, it's been used in everything from visual basic, delphi, and pretty much any other gui based language.
It is simply an extra property that allows you to add a numeric value for any reason you want to the control.
I've seen it used for everything from a counter to holding a record id that the control is tied to.
It is a bit of a kludge. It is often used in for instance a TreeView to link a Node to a data element.
But I would not over-use it, since it is very public and not very flexible. Note that you can almost always use a Dictionary< Control, ValueType> instead, and have a lot more control that way.
I use it all the time with ListViews and TreeViews. It makes trying to find the underlying data much easier. In fact, I'm not sure how you'd make a readable Winforms application without it.
I also use it a lot when creating ContextMenus at run-time. I stuff an object into the Tag member of each ToolStripMenuItem and then I can point each menu item's click handler at the same method. It results in a lot less code.
I just wish it didn't require so much casting.