| Denis's profileDenis Vuyka .Net Playgro...BlogListsNetwork | Help |
|
February 08 Blog is movingLadies and gentlemen, The blog has been moved to Wordpress (http://denisvuyka.wordpress.com). Please update your bookmarks in order to follow new posts and get new information. Thanks for your interest to my blog. August 05 Online Mind Mapping SoftwareFor those who are fond of mind mapping and corresponding tools the following project might be of great interest: http://mindomo.com/ July 03 Dynamic Geometry with Silverlight 2.0While preparing an updated article related to diagramming with Silverlight 2.0 I came across an awesome project at CodePlex named "Dynamic Geometry" made with Silverlight 2 Beta 2. So I've decided to postpone my article for a while and mention this project worth looking at. June 05 Your own WPF PropertyGrid control in a couple of hoursAbout a week ago I found a very interesting implementation of a simple WPF PropertyGrid control at CodePlex. It inspired me writing my own vision of PropertyGrid that could help me within my projects. Special thanks to Robby Ingebretsen who gave me permissions using Kaxaml controls styles and a couple of classes within this article. First thing you should always keep in mind is that with WPF the GDI age is over. You don't need drawing and rendering everything from code. This brings out that something like PropertyGrid control is now nothing more but a collection of brushes, data templates and the most simple steps to introduce the desired layout. It might take about 5 or 6 hours implementing the same control from scratch assuming you already have styles/themes for the controls. I won't describe the code line by line and would rather prefer concentrating on key aspects of the implementation. Complete sources and demo application can be found at my SkyDrive at the end of this article. Final results. Demo application. The Property grid control is implemented in a full isolation inside a separate custom control library. This means that all control styles and effects are fully encapsulated within the control assembly and so do not impact the final application design. All you need is to add a reference to the property grid assembly and declare something like the following [MARKUP CUT] <GridSplitter Width="2" Background="Black" /> <pg:PropertyGrid xmlns:pg="clr-namespace:DenisVuyka.Controls.PropertyGrid;assembly=DenisVuyka.Controls.PropertyGrid" Grid.Column="1" SelectedObject="{Binding ElementName=button}" ShowAttachedProperties="False" ShowReadOnlyProperties="False" ExpandCategories="False"/> The PropertyGrid is presented by a customized System.Windows.Controls.Control class and alongside the native properties defines only four property grid related properties for the moment. They are: 1. SelectedObject (object): Same as for the canonical Windows Forms PropertyGrid. It is an instance of an object the properties are to be changed for. 2. ShowAttachedProperties (true/false): Defines whether to show attached properties. Though this version is not capable of maintaining attached properties properly (still working on it) the control is capable of displaying them visually. The default is "False". 3. ShowReadOnlyProperties (true/false): Defines whether to show dependency properties that are readonly so having only getters. You won't be able to edit them but simply to see the values. For experimental purposes you can switch it on to see that readonly checkboxes will be properly reacting on such readonly properties like the "IsMouseOver". The default is "False". 4. ExpandCategories (true/false): Defines whether categories should be expanded upon control load. I personally like it to be initially collapsed but nevertheless set the default value to "True". So by default all the categories will be expanded. Usually I guess the only "SelectedObject" definition/binding will be enough for everyday usage. You can omit the rest of the properties and rely on the default values. UI features 1. PropertyGrid control Supports only one "Black" color scheme for the the moment. After having estimated the time required to duplicate the Blend or Kaxaml color scheme I've decided to reuse the existing wonderful implementation and got Kaxaml permission for the styles, component templates and a couple of classes that I needed to change a bit according to my needs. Though everything I reused was reviewed, refactored and changed I still consider Kaxaml set to be the most awesome set of the control templates that can be found today. The Control can be stretched within your panels without having any hardcoded width or height. For demo application I've used the common Grid layout with a vertical splitter between property grid and playground content so to see how it can be stretched in case property names are too long to be fully displayed. 2. Categories Categories are smooth and animated :) It cannot be demonstrated by screenshots so you'll see that only after launching the demo application. Category templates are based on restyled Expander elements and so have their own Collaped/Expanded states unlike the Kaxaml approach within the TabControl where only one panel could be opened at a time. 3. UI Editors UI Editors are the heart of any PropertyGrid control. They have to be efficient and convenient enough for the user to change the values. All editors in this control are based on DataTemplates for CLR types, inherited/base types or user configured properties. This will be described further on. All editors can be represented either by common controls or by user created ones. PropertyGrid performs re-templating and re-styling for the following set of common controls: 1. CheckBox 2. ComboBox 3. Expander 4. ScrollBar 5. ScrollViewer 6. Slider 7. TextBox 8. ToggleButton alongside with a couple of user defined custom editors for demo purposes (will be described further on). But these controls above take part in most of the editor templates. As I've mentioned earlier the property grid has the capability of choosing this or that editor according to the type of the property being edited. The key aspects and knowledge references will be given in the second half on the article. Now I'd like to dwell on the following features of the editor selection: Default editor Among all the scope of properties and their editors there will always be situation when there's no UI editor defined. In this case the fallback UI is needed to display the property in any way and rely on Type Converters that might still define the property value converted from a string at some kind of TextBox element. So the default fallback editor here might possibly be a TextBox control. Thus I've tried covering different types of properties by relying on the default editor, like Width, Height, etc All property value assignments are proxied by TypeConverters so in this case I can still have a valid property editor by means of the most basic Validation applied (I'm still working on more powerful validation and error notifications) As for the existing editors the property grid control prioritize template selection based on CLR type. There's a set of already predefined ones but it is quite an easy thing importing them during runtime or extend the template selection factory (actually I'm also working on providing a service bus support with IServiceProvider/IServiceContainer but it's a point of future releases). "Boolean" editor Boolean editors are represented by CheckBox elements. Among the rest I think this is the less error-prone approach possible. This editor reflects the readonly/writable state of the property properly so you won't be able defining getter-only property values. "Enum" editor Represented by a ComboBox element containing all the possible value for the corresponding enum-based property reflecting the values of the Enum. Like the CheckBox editor this one neither allows user define error-prone values as the proper values are limited to a list. This editor is applied to all property types that are based on "System.Enum" type. Alongside there are customized templates that provide more rich capabilities of editing property values: ComboBox editor There are many properties that are not based on Enums but nevertheless have a fixed (or nearly fixed) set of options to be chosen by the end-user. Let's take Font Families for example. It can be regarded as a fixed set as the user defining the Font Family value will be obliged keeping the existing list of possible values rather that typing anything he wants. So in this case Font Family is not an Enum-based value but is a perfect candidate to be displayed as a ComboBox selector. The same goes for example for FontStyle FontWeight, FontStretch, etc properties. Meanwhile there might be, let's say, dual-oriented editors that support both either existing values or manual typing property value definition. The perfect sample for such a case might be a "Brush"-based properties. There have always been a set of predefined or named colors developers could use and the possibilities for defining/constructing any color based on it's ARGB members. This means that "Brush" editor should support either named colors or manual definitions: "Brush" editor The "Brush" editor is presented by an editable ComboBox that has a list of all Named colors (dynamically loaded) and yet is capable of converting user typed values from strings to appropriate brush instances where possible, the BrushConverter is used for this purpose. Also the combo is styled so that each Named color can be previewed before selection. The "Brushes" that don't belong to Named color family can be still displayed in the property grid in their native view. "Thickness" editor The "Thickness" editor was the first attempt to demonstrate the user defined templates and how the Point or Thickness based property can be edited in a different way. So I've created a separate editor control having two editing possibilities: 1. Common default-based text control. It can take only one value so that it will be set to all four members. 2. Expandable area for editing each value member separately: This is just one of the samples to get the idea that you can provide any UI you want to edit a common property. "Opacity" editor Another feature of this PropertyGrid control is editor (template) selection based on named properties. I'm still working on a more efficient way of properties definition for that purpose but for present moment you don't need changing anything to the object type you are editing within the property grid. All required is to define an editor template with a name of the property that don't have any build in predefined editor. "Opacity" property is a Double-based one. For all the doubles it comes out that the "Default" editor with an appropriate TypeConverter is quite enough to define a Double value. But there are many cases when the user can be restricted in the values he type. For example the "Opacity" takes a range between 0.0 and 1.0 as a valid value. So it is a perfect candidate for having a tuned editor for example looking like a Slider: So you can get the most of a slider features like defining the Minimum/Maximum properties, TickFrequency, AutoToolTipPrecision, etc. and make this property editor less error-prone for the end-user as soon as you restrict the value setting possibilities only for a range of valid ones. Also I've implemented a custom "Point" editor providing possibilities of editing parts of the Point structure similar to the "Thickness" editor and based on it another customization for "RenderTransformOrigin" property. "RenderTransformOrigin" editor RenderTransformOrigin property thus based on a Point structure also has a valid range for each member of the structure. Same as the opacity both X and Y expect the range between 0.0 and 1.0 doubles so this property can also be tuned in order to be more safe and restrictive. I've again used the Slider approach here for demo purposes: This is a composite editor that is presented by a Default one to define the values manually and having Two sliders for each member of the Point structure. The editable part is automatically updated upon changing the slider values, hints and tooltips are also provided while dragging the slider. That's all as for the key UI editor aspects. More details you will get from the sources and demo project. Again, I have to mention that you can implement any sort of UI required to edit this or that value type and I find this very appealing for future enhancements. I'm still working on more complex editors and their dynamic integrations outside the property grid control.
Interopability and Windows Forms Until now I was mainly concentrated on Dependency Objects and editing Dependency Properties. It is quite easy to extend the control to provide Native properties support in future so that it can be used within any object instance. Demo application solution contains a Windows Forms project for interop capabilities demonstration where the PropertyGrid holds another instance of PropertyGrid control as a SelectedObject. private void Form1_Load(object sender, EventArgs e) { this.elementHost1.Child = new DenisVuyka.Controls.PropertyGrid.PropertyGrid() { SelectedObject = new DenisVuyka.Controls.PropertyGrid.PropertyGrid() }; } This will look like the following: So as I've said the Windows Forms support is on the way but not a difficult task to accomplish.
What are the key points and aspects you should get acquainted with in order to implement your own vision? Step 1. From property descriptor to business object. The main task of the property grid is displaying object properties and giving possibilities of changing their values in convenient way. So collecting properties is the first step do be thought over and performed. Each object to be displayed in a property grid has a set of public properties and each property has a special descriptor giving possibilities analyzing them from code. So you will have to implement a wrapper around each property encapsulating the process of value changing and ready to be bound to the UI elements as some business object. Each property can provide the following minimum set of properties to the UI elements: 1. Name (mandatory). Each property has a name and this name should be displayed as a Label right before the value editor control. 2. Category (optional). The properties can be grouped by different categories so that user can easily manipulate a set of properties belonging to some logical family. 3. Type (mandatory). Property type describes the type of the value that is to be assigned for the instance of the object. This is the only way to choose the correct UI editor for each property. 4. Value (mandatory). This is a what to be set by UI editors and to be passed to underlying editable object instance. 5. IsReadOnly (optional). The property might occur to be read only (having only a getter) and so UI should react on that in a proper manner. So you might work out something similar to the following diagram: Note that here and later on I'll be dealing with Dependency Properties only. I've reserved the Native properties and their maintenance for future enhancements and posts. Property Category has only the "Name" mandatory property and serve a container for property items. It might look like given below: I've added additional optional boolean property called "IsExpanded" just for visualization purposes. The property grid will have an option defining whether all the property categories should be collapsed or expanded by default. Here's the whole diagram for properties and related infrastructure So upon setting the object to be edited your property grid control has to enumerate all the dependency properties and wrap them into some entities called here "DependencyPropertyItem" that will be bound to UI elements for end users. You can get enumerate all property descriptors using the following snippet: PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(component, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues | PropertyFilterOptions.UnsetValues | PropertyFilterOptions.Valid) }); Next thing you can get is extracting a DependencyPropertyDescriptor from a common PropertyDescriptor in the following way: DependencyPropertyDescriptor dpDescriptor = DependencyPropertyDescriptor.FromProperty(descriptor); It will give you possibilities getting the Dependency Property only related metadata like determining whether it is an Attached Property, adding the value change and coerce callbacks, getting default values, etc. Step 2. From business object to data template Now as you already have some sort of business object it is time to start thinking of possible ways to display it in a proper way. Visually property grid displays a two-column grid where first column displays labels or text blocks identifying property names and second column displays different types of UI editors for setting values in a convenient way. This control will be using a plain custom Control where the whole layout and control template will be declared within XAML. To get the basic idea of data templates used within the property grid I'd recommend you referring to the following articles. Guess this will be more efficient than I will be copy-pasting information that is already clarified everywhere: WPF DataBinding, Styling and DataTemplates How can I display time information more graphically in WPF Here it will be used a Control-based class templated in a way to expose ItemsControl. Something like the following: <ItemsControl ItemsSource="{TemplateBinding local:PropertyGrid.Properties}" Background="<Some color>" /> DataTemplate for each dependency property wrapper may look like the following snippet: <DataTemplate DataType="{x:Type data:DependencyPropertyItem}"> <Grid Margin="4"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Margin="0,0,8,0" TextAlignment="Right" VerticalAlignment="Center" Text="{Binding Mode=OneTime, Path=Name}" ToolTip="{Binding Mode=OneTime, Path=Name}"/> <ContentControl Grid.Column="1" VerticalAlignment="Center" Content="{Binding Mode=OneWay}" ContentTemplateSelector="{StaticResource propertyTemplateSelector}" /> </Grid> </DataTemplate> Snippet above presents one common row in your property grid control. It is layouted as a two-column grid where column 1 will contain a TextBlock element bound to the "Name" property of the underlying business object. Column 2 will be presented as a dynamic ContentControl or "Value Editor". According to the property type it will be "replaced" to the proper UI element. To get more information on ContentControl and it's usage details you can refer to the following articles: WPF Control Templates - An Overview Why WPF Rock... (The content model) Choosing the proper UI editor I assume that at this moment you are already aware of templating the ItemsControl and providing basic data templates for the items. The next question that might arise from your side is how exactly the ContentControl is turned into the proper UI editor and how to create different types of editors for different property types. This is where you might need nested templating or data templates inside the data templates and this is when you need details on ContentTemplateSelector property of a ContentControl or custom DataTemplateSelector implementations. I've also prepared a couple of links to start digging from in case you hear these words for the first time: "Routed Template Selection" by Josh Smith "How do I display items in an ItemsControl using different templates?" by Beatriz Costa As you've seen from the snippets above the DependencyPropertyItem data template declares a ContentControl that has a PropertyTemplateSelector assigned for the ContentTemplateSelector property. It is a custom implementation of a DataTemplateSelector and it's job will be to get an appropriate DataTemplate for the UI editor according to the ItemsControl item being templated. Step 3. UI Editors As soon as you can style ItemsControl and aware of DataTemplating the next task you'll have to perform is to implement as much UI editors as you want and give them any desirable view. You can think over having UI editor templates outside the control so having possibilities extending your external collections later on. For a set of sample predefined editors and control templates please refer to the "/Themes/PropertyEditors.xaml" resource dictionary within the property grid control. This will give you all the required material and ideas needed to implement you own vision of value editors.
Conclusion: Of course, there are still plenty of tasks for me to finalize in order to produce a fully-fledged property grid control and there's a lot of stuff for anyone else to change or study. But with the appearance of WPF I think we have reached the point in time where we can actually implement all the requirements rather than thinking over how to do that. As I've mentioned several times across the article I will continue working on this project so you may expect to hear more news and get more updates here. So thanks for your attention and keep on the line... Source code and demo application for the article Updated information: The PropertyGrid control described here moved to the CodePlex. All new information and updates can be found there. April 26 Immediate Window. Generating and executing lambdas across application domains.First I'd like to say thank you words to Igor Moochnick. His article CodeDom extensions and dynamic LINQ (string/script to LINQ emitting) gave me a lot of pleasure extending it a bit and creating shorter code in a Linq manner flow. Refer to his article for more details on CodeDom and extension methods he introduced. Introduction Due to some purposes I needed creating a quick implementation of some kind of "Immediate Window" that could allow me executing methods and setting properties for object instances at live running application. I tried several approaches. Among them were "Dynamic Methods", extending "Linq Dynamic API" and parsing commands and property paths to be able using them against reflection. But at last moved to code generation as the most easiest and quickest way of accomplishing the task. Imagine you have a small TextBox based pad in the running WPF application similar to "Immediate Window" pad in Visual Studio and you are willing to execute the following snippet against the main application window: this.Title = "NewTitle" (this.FindName("btnExecute") as Button).SetValue(Button.ContentProperty, "NewContent") (this.FindName("stackPanel") as StackPanel).Children.Add(new Button() { Content="xxx" }) So these lines should be transformed into the code from their string representation and executed in runtime against the running Window as the code is supposed to perform some actions against the Window instance. What I've decided to do was similar to the following ideas: 1. We should have the dynamically generated method that will have our text line as it's body (though with some possible transformations) 2. Method should accept our Window instance as parameter and perform defined text line against parameter. 3. It should return something if we are requesting the property value. So we need here something similar to the code we could write down manually in the application wrapped into a method, for example this.Title = "NewTitle" can be imagined as following: public static void SetTitle(Window obj, string value) { obj.Title = value; } So upon processing the command our environment has to generate some method called for example "SetTitle", provide the execution context (in this case a Window) and pass the command typed by the user. During that it should redirect all "this" aliases to the execution context detected (in this case all "this" should be translated into "obj"). Regard this as you are typing the body of some method without body declaration and are sure that all the missing stuff will be automatically generated. Implementation Code generation for this case will require dealing with separate application domains due to assembly management issues and possible memory leaks. The steps will be as following: 1. Transform and wrap user defined method/function body with appropriate method, class, namespace, etc. declarations using the CodeDom (so to get the ready to compile sources either into memory or physical file) 2. Compile the resulting Dom into the assembly (I'll be using physical files) 3. Create a separate application domain and load compiled assembly into it. 4. Extract the generated method from the loaded assembly and invoke it against the execution context (in this case it will be a Window) 5. Free resources, unload second application domain and perform a temporary files cleanup. This is similar to implementing an Add-In or Plug-In support for your application where your external algorithms may be added to main application, executed against some provided execution context and finally destroyed without any impact on main application. It is obvious that you might don't want passing whole your window across application domain boundaries (i.e. .net Remoting scenario) and you don't want marking anything as Serializable to pass into another domain. So all we need here is getting some delegate or lambda somewhere outside for applying it against our Window locally. Void execution Under Void execution I mean some operation that is invoked against your execution context and doesn't return any results. For example setting the property value. So what happens when user types this.Title = "New Title" and presses Enter or some "Execute" button 1. "this" alias is converted into "context" 2. Lambda action is generated that performs your logic against some "context" parameter: Action<T> action = context => { context.Title = "New Title"; }; 3. Wrapper method and class container is generated that will help you getting lambda from another domain: namespace GraphSquare.AutoScript { using System; using System.Windows; public class Script { public static Action<T> CreateLambda<T>() where T : Window { Action<T> action = context => { context.Title = "New Title"; }; return action; } } } As you can see this method will be valid for all the types based on Window, so you'll have possibilities running the resulting lambda against your different custom windows and dialogs that inherit Window type. Note that this rules are introduced for clarification purposes and <T> constraint may differ. According to this constraint we ensure that the method will be compiled successfully as each Window object has a Title property. CodeDom for this method is generated using the Igor's CodeDom extensions I mentioned at the beginning of the article: public static CodeDom GenerateActionDom<T>(string actionBody) { string body = string.Format( "Action<T> action = context => {{ {0}; }}; return action;", actionBody.Replace("this", "context")); CodeDom c = new CodeDom(); c.AddReference(DefaultReferences) .AddNamespace(DefaultNamespace) .Imports(DefaultImports) .AddClass( c.Class(DefaultClassName) .AddMethod(c.LambdaAction("CreateLambda", typeof(T).Name, null, body)) ); return c; } I won't dwell on extension methods implementation and my contribution to it as it is more understandable when looking through the sources. Please refer to the source code link at the end of this article. In two words you are creating the source for the class mentioned earlier and specifying the <T> constraint passing the generic type provided for the method. Also "this" alias is converted into "context" and passed to CodeDom as a body of newly generated method. Function execution If you want invoking the command that user had typed and expect some results to be returned and displayed you might want in this case getting Func<T,K> lambda, running it against some T parameter and present the K result back to user. For simplicity purposes I've decided to reuse native .net "return" keyword and so being close to method body declaration in a common way it is used. So what happens when user types return this.Title and presses Enter or some "Execute" button: 1. "this" is converted into "context" 2. Lambda function is generated that performs some logic against your "context" and returns results Func<T, object> func = context => { return context.Title; }; 3. Wrapper function is generated for getting lambda from another domain, constraints and types are defined according to the calling application namespace GraphSquare.AutoScript { using System; using System.Windows; public class Script { public static Func<T, object> CreateLambda<T>() where T : Window { Func<T, object> func = context => { return context.Title; }; return func; } } } Here's how CodeDom is generated to get such a class: public static CodeDom GenerateFunctionDom<T>(string fucntionBody) { string body = string.Format( "Func<T,object> func = context => {{ {0}; }}; return func;", fucntionBody.Replace("this", "context")); CodeDom c = new CodeDom(); c.AddReference(DefaultReferences) .AddNamespace(DefaultNamespace) .Imports(DefaultImports) .AddClass( c.Class(DefaultClassName) .AddMethod(c.LambdaFunction("CreateLambda", typeof(T).Name, null, body)) ); return c; } Compilation and assembly loading I've provided additional method for CodeDom class providing capabilities compiling sources to file without keeping the strong reference to Assembly file at the end so providing possibilities of removing the assembly later on when it is no more in use. You'll be calling CodeDom.CompileAssembly method that gets string parameter for full final assembly path and returns CompilerResults for results processing in order compilation exceptions. According to common approach patterns for loading assemblies inside separate application domains you need implementing a proxy Loader class to serve also as a contract and bridge between your two domains. So exactly the loader being instantiated in the second application domain will load compiled script assembly, extract the desired lambda and execute it against your context or simply return you the lambda so you can execute it inside the main application domain whenever required. Assembly Loader using System; using System.IO; using System.Reflection; namespace GraphSquare.Scripting.CodeDom { [Serializable] public class AssemblyLoader { Assembly assembly; public AssemblyLoader() { assembly = null; } // Loads the content of a file to a byte array to // prevent file locking, so user can remove or modify assembly // while using it loaded into memory static byte[] LoadFile(string filename) { FileStream fs = new FileStream(filename, FileMode.Open); byte[] buffer = new byte[(int)fs.Length]; fs.Read(buffer, 0, buffer.Length); fs.Close(); return buffer; } public void Load(string assemblyPath) { byte[] rawAssembly = LoadFile(assemblyPath); this.assembly = Assembly.Load(rawAssembly); } } } This is a basic implementation providing only assembly loading facilities. It will be extended later on. Note that it is situated in the separate assembly in order to serve a contract between different domains. To create additional application domain our main application will have the following method: private static AppDomain CreateNewDomain() { // Create a simple application domain return AppDomain.CreateDomain( typeof(AssemblyLoader).Name, AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation); } This is the most basic implementation of second domain that takes Evidence and Setup Information from the main one and so loads all the references that the main domain has. In our case it will also load the contract library with the AssemblyLoader so we could establish communication between domains easily. To create a valid loader in the second domain and loading external assembly file into it we'll be using the following method: private static AssemblyLoader CreateLoader(AppDomain domain, string assemblyPath) { // Create a new instance of AssemblyLoader in the separate domain AssemblyLoader loader = domain.CreateInstanceAndUnwrap( "GraphSquare.Scripting", typeof(AssemblyLoader).FullName) as AssemblyLoader; // Load compiled assembly into the loader loader.Load(assemblyPath); return loader; } Extracting compiled lambdas Fine now we have two possible members of the generated wrapper class needed to be discovered and loaded (action and function) to be used in lambda generation. As we have a separate domain with compiled assembly loaded into it and know exactly the namespace, type and name of the wrapper class and even the name of the method to generate lambda - "CreateLambda" (as we did it's generation actually) the process of it's loading and invokation becomes quite trivial. Using Assembly.GetType(string...) method it becomes easy loading types and activating objects. For each type you can call GetMethod(...) to get the necessary method info to be used in invokaction. Thus discovering and invoking generic methods are a bit different. Here's how can extract our generated "CreateLambda" method from AssemblyLoader: public Action<T> GetLambdaAction<T>(string type, string action) { // Extract type method info from assembly MethodInfo methodInfo = assembly.GetType(type).GetMethod( action, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // Create generic arguments for generic method Type[] genericArguments = new Type[] { typeof(T) }; // Create generic function info for invokaction MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments); // Invoke generic function and get return value as Action<T> Action<T> returnValue = (Action<T>)genericMethodInfo.Invoke(null, null); // return results return returnValue; } As compiled assembly is loaded into the second application domain where the AssemblyLoader is already present so loader is capable of discovering our generated type to work with further. When executed this method returns us ready to use lambda with the body we defined earlier. In most of the cases guess you'll have to get compiled lambda, execute it against some context and dispose it. So we can introduce two more members for the AssemblyLoader for invoking actions and functions directly from AssemblyLoader. public bool InvokeLambdaAction<T>(string type, string action, T argument) { // Extract type method info from assembly MethodInfo methodInfo = assembly.GetType(type).GetMethod( action, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // Create generic arguments for generic method Type[] genericArguments = new Type[] { typeof(T) }; // Create generic function info for invokaction MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments); // Invoke generic function and get return value as Action<T> Action<T> returnValue = (Action<T>)genericMethodInfo.Invoke(null, null); if (returnValue != null) { // Execute received action against argument provided returnValue(argument); return true; } return false; } public object InvokeLambdaFunc<T>(string type, string function, T argument) { // Extract type method info from assembly MethodInfo methodInfo = assembly.GetType(type).GetMethod( function, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); // Create generic arguments for generic method Type[] genericArguments = new Type[] { typeof(T) }; // Create generic function info for invokaction MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments); // Invoke generic function and get return value as Func<T,K> Func<T, object> func = (Func<T, object>)genericMethodInfo.Invoke(null, null); //return execution result of func against the argument provided return func(argument); } As you can see both methods are not so different. Both of them require full type name of the class holding the method, method or function name to be searched and argument of your needed type to be processed as an execution context. So the process of executing user typed command that doesn't require results can be as following (at main application side): // Generate CodeDom CodeDom c = CodeDomSmith.GenerateActionDom<T>(methodBody); string assemblyPath = GenerateAssemblyPath(); // Compile sources into assembly CompilerResults results = c.CompileAssembly(assemblyPath); // Process compilation errors if (results.Errors.Count > 0) return false; // Create separate application domain AppDomain domain = CreateNewDomain(); // Create loader proxy based on application domain created // and load compiled assembly to it AssemblyLoader proxy = CreateLoader(domain, assemblyPath); try { // Extract and execute lambda against the context object proxy.InvokeLambdaAction<T>( CodeDomSmith.DefaultClassFullname, "CreateLambda", context); } catch (Exception ex) { return false; } if (domain != null) { // Unload and dispose second application domain AppDomain.Unload(domain); domain = null; } if (File.Exists(assemblyPath)) // Remove used assembly File.Delete(assemblyPath); The only difficulty that arise is determining what type of lambda to generate according to the user input. I mean how to decide whether he wants executing a Void or getting some results. I've accomplished that again as I've mentioned earlier by reusing the "return" keyword. So due to this sample when the user starts the command from "return" keyword we are generating the Func<T,K> otherwise Action<T> Samples: Here's the sample environment I've prepared for the article: You'll have the Window that will be assigned as an execution context for all upcoming commands typed in the bottom TextBox control. "Execute" button compiles and executes the command putting all the output to the "Debug window" TextBox control. You will also be able to see the content of the class generated and executed for development purposes. "Help" button shows a list of dummy samples that can be copy-pasted and tested against the window. "Execution Result" TextBox shows the results of the command execution. This can be either "void()" if method was compiled and executed or some value. Here's the possible result of execution of the command: this.Title = "GraphSquare Scripting" Note that execution result is "void()" as we didn't try to return anything and the Title of the window has changed from "Window1" to "GraphSquare Scripting" Let's try running the application once again and executing the following command: return this.Title == "Window1" As you can see command was successfully evaluated and returned "True" value that is a valid result as the Window's Title property value is "Window1" Now let's try typing a command that cannot be valid and compiled by default, something like this.ExecuteMissingCommand() If you remember the execution context for our commands is being set to Window. This means that as a common Window class does not contain the method typed the resulting code won't be compiled and cannot be executed against a window. This brings to a compilation exception: The exception message will be the following: error CS1061: 'T' does not contain a definition for 'ExecuteMissingCommand' and no extension method 'ExecuteMissingCommand' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?) This is mainly for your debugging purposes. The real life implementation can hide the developer oriented details and present more user-friendly message. Also as you might have noticed all lambda generation methods are generic ones, for example public static bool InvokeContextMethod<T>(T context, string methodBody, ...) this means that you can specify different execution contexts or even change them on the fly. As for the suggestions I think there could be some contracts to serve as execution context instead of applications or windows. Remember not providing the end-user more power that he is expected to have. As you will see from the samples provided within the "Help" section of this tool you can simply add or remove new elements to the window, rename buttons, etc. Hope this helps someone enriching his tools with runtime command support or Debug features. The source code for the article can be found here April 23 Providing simple collaboration support for your diagramming tools with WCF services.Notes: This article assumes you are already familiar with WCF services or studying them. Several months ago I've posted simple draft project for a small diagramming tool. Though I didn't intend it to be a tool or starter kit it is still the most visited post among all other more interesting things ;) As I've been playing with WCF and wanted some simple environment for testing some ideas I remembered about old CTP project and found it was quite a good playground for my experiments. So this article will be using the reviewed and edited version of the project mentioned in the old article. I'd also recommend you visiting the Code Project to see the wonderful samples by Marcus. I've dared to take his toolbox and tool item implementation for my article samples :) What is the key changes made since the previous sample: 1. Simple MVC pattern implementation sample The classes introducing the pattern are: DiagramView: WPF canvas hosting the shapes and connection lines DiagramController: Simply the controller DiagramModel: Data model hosting the business objects (entities) that are not related to presentation layer. As my intention was processing business objects rather that shapes and controls I've decided to perform a dummy bridging between raw "DiagramEntity" classes and their UI representation. I've also simplified event system between Model, Controller and View based on Action<DiagramEntity> delegates for clarity purposes. Model is capable only of storing the entities, adding them and removing. In this implementation the View is also responsible for instantiating the Collaboration Client that will be described later on. 2. Unity Application Block I've reduced the influence of the service bus for many parts of the application in favor to Unity Application Block. That made my life much easier. 3. Toolbox As I moved on for using a simple toolbox I've eliminated the shape tools and shape creators that were rudimentary parts of Factory design pattern. But left the connection creator tools with a simple Strategy pattern implementation just for educational purposes. 4. Extensions and IExtensibleObject<T> interface In this article I've described some ideas for extending Canvas panels by means of IExtensibleObject<T> interface taken from WCF. I've decided it would be fair enough reusing this stuff to show that it's working fine. The Canvas (View) is extended with Selection and Drag functionality that can be simple switched off if required. 5. Aero theme by default and many minor improvements... Here's how it looks like
Basic collaboration support using WCF service My intention was giving some sketches for how it could be implemented and some raw material for starting to play with. So I'll be concentrating on it omitting thread safeties, error checking and the stuff like that for clarity purposes. Guess it would be much easier extending the sources based raw skeleton implementation. Business objects As I've mentioned the entities my service will be operating on are not related to presentation layer so it won't be aware of WPF and the stuff like that. It will be the View responsibility turning them into the visuals and applying different styles and templates. Meanwhile we need at least some basic UI related information be passed like positioning coordinates so this is what UIEntity class serves for. I'll be playing with three entities named "DataEntity", "SystemEntity" and "UserEntity" that will be turned later on to the appropriate shapes at client side. This provides nice presentation flexibility allowing different clients using specific visuals and styles though following one remote model. Remote Model and Remote Callbacks I've chosen the Duplex WCF service for the sample that requires implementing a callback contract also. For more details on duplex services better refer to MSDN library as it might take long time for me describing theory that is already perfectly presented ;) This is how the remote model might look like: using System.ServiceModel; namespace Collaboration.Contracts { [ServiceContract( Name = "RemoteModel", Namespace = "http://dvuyka.spaces.live.com/diagramming/types", CallbackContract = typeof(IRemoteModelCallback), SessionMode = SessionMode.Required)] [ServiceKnownType(typeof(UserEntity))] [ServiceKnownType(typeof(DataEntity))] [ServiceKnownType(typeof(SystemEntity))] public interface IRemoteModel { [OperationContract(IsOneWay = true)] void AddEntity(DiagramEntity entity); [OperationContract(IsOneWay = true)] void RemoveEntity(DiagramEntity entity); [OperationContract(IsOneWay = true)] void Connect(); } } Due to serialization purposes our model should be aware of all the inheritances that might take part in the collaboration process. I've declared them as "known" to the service being described by this interface. Our remote model will also provide Adding and Removing capabilities like the "local" tool's model and subscription to service via the Connect method. The callback contract might look like the following: using System.ServiceModel; namespace Collaboration.Contracts { public interface IRemoteModelCallback { [OperationContract(IsOneWay = true)] void OnEntityAdded(DiagramEntity entity); [OperationContract(IsOneWay = true)] void OnEntityRemoved(DiagramEntity entity); } } It means that remote model (or simply service) will be raising "OnEntityAdded" and "OnEntityRemoved" events for all the subscribers providing them with the entities to be immediately added to the local model and so presented with the View. Wiring up the "local" model At the client side we are creating a simple wrapper delegating remote model events to the local model like the following using Collaboration.Contracts; namespace HomeDiagramming.Collaboration { public sealed class RemoteModelCallback : IRemoteModelCallback { public DiagramModel LocalModel { get; private set; } public RemoteModelCallback(DiagramModel localModel) { this.LocalModel = localModel; } #region IRemoteModelCallback Members public void OnEntityAdded(DiagramEntity entity) { this.LocalModel.AddEntity(entity); } public void OnEntityRemoved(DiagramEntity entity) { this.LocalModel.RemoveEntity(entity); } #endregion } } So each time the service will be publishing "OnEntityAdded" or "OnEntityRemoved" events to it's subscribers the event execution will be delegated to the underlying local model by calling it's appropriate members. The client connecting our remote model will be as simple as possible: using System.Diagnostics; using System.ServiceModel; using System.ServiceModel.Channels; using Collaboration.Contracts; namespace HomeDiagramming.Collaboration { [DebuggerStepThrough] public class RemoteModelClient : DuplexClientBase<IRemoteModel>, IRemoteModel { public RemoteModelClient(InstanceContext callbackInstance) : base(callbackInstance) { } public RemoteModelClient(InstanceContext callbackInstance, Binding binding, EndpointAddress address) : base(callbackInstance, binding, address) { } #region IRemoteModel Members public void AddEntity(DiagramEntity entity) { base.Channel.AddEntity(entity); } public void RemoveEntity(DiagramEntity entity) { base.Channel.RemoveEntity(entity); } public void Connect() { base.Channel.Connect(); } #endregion } } During creation of the View that instantiates the Controller we can initialize our remote controller in the following way InstanceContext context = new InstanceContext(new RemoteModelCallback(this.Controller.Model)); Remote model skeleton And finally here comes the raw skeleton for the remote model using System; using System.Collections.Generic; using System.ServiceModel; using Collaboration.Contracts; namespace Collaboration.Service { [ServiceBehavior( InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)] public class RemoteModel : IRemoteModel { // TODO: should be the thread-safe collections private List<DiagramEntity> entities = new List<DiagramEntity>(); private List<IRemoteModelCallback> callbacks = new List<IRemoteModelCallback>(); #region IRemoteModel Members public void AddEntity(DiagramEntity entity) { // TODO: should be performed in a thread-safe manner and fully async Console.WriteLine("Adding entity :" + entity.Name); lock (entities) { this.entities.Add(entity); } Console.WriteLine("Entity added. Total count: " + entities.Count); IRemoteModelCallback callback = GetCallback(); foreach (IRemoteModelCallback c in callbacks) if (c != callback) c.OnEntityAdded(entity); } public void RemoveEntity(DiagramEntity entity) { // TODO: should be performed in a thread-safe manner } public void Connect() { IRemoteModelCallback callback = GetCallback(); if (!callbacks.Contains(callback)) { callbacks.Add(callback); Console.WriteLine("User connected. Total count: " + callbacks.Count); } } private IRemoteModelCallback GetCallback() { return OperationContext.Current.GetCallbackChannel<IRemoteModelCallback>(); } #endregion } } As you can see it is a Singleton service with Multiple ConcurrencyMode. Each connected client will be registered within the subscribers collection (callbacks). Each model change causes all the subscribers to be notified. In my implementation each client immediately applies changes to the Model and View. Guess the rest of functionality might depend on your ideas. The source code for the article can be found here: Possible issues: If you encounter exceptions related to connection try changing the ports for your endpoints. On some machines localhost:8080 may be not allowed. Change it to localhost:8000 ("App.config" for server and "DiagramView.cs" for client) or whatever port is available free and doesn't violate your firewall policy. Notes: In order to build solution you'll need the release version of Unity Application Block. Solution is configured to run both service and demo application at a time. Open the binaries folder for demo application and run several instances of application in order to ensure all clients receive notifications and create shapes at a time. April 04 Injecting Xaml with Unity Application Block using Markup Extensions. Part 4.Delegating Routed Events (Event Handlers). This is a continued research dedicated to Unity Application Block. For more details please refer to the following posts Part 1. Basic injection of custom user controls. Part 2. Injection of standard WPF controls. Setting properties for injected controls. Part 3. Attached dependency properties. Binding delegation. Code behind support. To be honest I didn't expect myself splitting the topic across 4 posts. And I didn't plan writing the 4rth one until I collect a batch of features to be presented. But after yesterdays playing with injections I decided that I have to implement and share with one more helpful feature as event delegation. This will be a good ending for the series until I collect feedbacks and ideas to be able to continue it. What's the purpose injecting WPF (or 3rd party) controls without specifying event handlers from my current page like for the common controls? This was the question I wanted to answer myself yesterday. As I've started the way injection extension behaved itself in a maximum intuitive manner I definitely wanted to set event handlers in the common manner too (meaning intellisense, generation of new handler within the current page, etc). I tried to follow the "Setters" approach for properties and make the standard "EventSetter" class suite my needs but encountered some difficulties with it's implementation. So the easiest and quickest solution was following the "PropertySetter" approach and introduce another placeholder for the event. Again I've decided to change the naming :) Everything being wrapped by the custom classes were suffixed with the "Injector". PropertySetter became PropertyInjector, BindingSetter became BindingInjector and so new event setter became EventInjector accordingly. This was mainly done to follow one scheme and not to affect existing extensions' names. So here's what I wanted to have as a result Unity Control extension provides additional collection for storing ready to delegation event handlers. You have the possibility of specifying appropriate routed event and assign or generate event handler for it like you would do for the common Button control. EventInjector itself is represented by the following holder: public class EventInjector { public RoutedEvent Event { get; set; } public event RoutedEventHandler Handler; public bool IsValid { get { return Event != null && Handler != null; } } public void AssignTo(FrameworkElement element) { if (IsValid) element.AddHandler(Event, Handler); } } Unity Control extension keeps the list of assigned EventInjector objects until the underlying UI control is resolved within the Unity Container. After that all event handlers are assigned for the control. Rather simple approach that might require enhancements in your implementations. Anyway does it's job :) You can specify a chain of "Button.Click" events or specify a set of different routed events, everything will be delegated to the underlying control until control has the routed event being assigned. Guess this single and small extension finishes the basic feature set that is required to move your controls to another abstraction level and provide possibilities for hot changing/patching of your UI elements via the application configuration file. April 02 Injecting Xaml with Unity Application Block using Markup Extensions. Part 3.Attached dependency properties. Bindings. Code behind support. This is a continued research dedicated to Unity Application Block. For more details please refer to the following posts Part 1. Basic injection of custom user controls. Part 2. Injection of standard WPF controls. Setting properties for injected controls. Introduction After having played by myself with injector markup extension implementation described in the previous article I found out a lot of limitations that could be easily introduced. I used "PropertySetter" wrapper for delegating properties to the injected elements based on dependency property descriptors taken from string representation of property names. This restricted me either from setting common non-dependency properties or supporting Attached Properties. Though good for panel layouts injected controls for example couldn't be properly positioned for the Canvas. Another thing I wanted to have is ability for delegating dependency property bindings to injected control. This could be also a very interesting task as I found no ways of declaring Binding markup extension within the Xaml that are having deferred processing/execution. Third feature I was looking for is correct referencing injected controls from the code behind. I wanted to be fully transparent delegating the names of UnityControl extensions to the injected controls so having possibilities of addressing them via .FindName(...) method As the mission was successfully accomplished I'm putting the most interesting details here... Prerequisites As we are going to deal with Bindings we need getting one level upper in the type mapping as UIElement doesn't support bindings. We will be using FrameworkElement for binding purposes. This requirement slightly impacts the Application Configuration File we are using for Container initialization. Instead of UIElement contract we have to declare FrameworkElement alias and map all the controls to it. Here's the sample: <typeAlias alias="FrameworkElement" type="System.Windows.FrameworkElement, PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> The rest of the control aliases remain the same. The container types are mapped to this alias accordingly like the following: <types> <type type="FrameworkElement" mapTo="Button" name="UnityButton"/> <type type="FrameworkElement" mapTo="TabControl" name="UnityTabControl"/> <type type="FrameworkElement" mapTo="TextBox" name="UnityTextBox"/> <type type="FrameworkElement" mapTo="ListBox" name="UnityListBox"/> </types> (Attached) Dependency Properties Support While it is more convenient maintaining dependency properties using DependencyPropertyDescriptor, common "plain" properties are maintained using Reflection - PropertyInfo. I've decided to decouple these two worlds to have capability of deciding which approach is more appropriate in this or that situation. I'm using the standard Setter class for targeting (attached)dependency properties as this class may seem more intuitive than custom control implementation. Setter class also deals with DependencyProperty object and so provides native way of controlling the values defined. This is how our injection declaration looks like: <this:UnityControl Name="btn1" Container="defaultProvider" Dependency="UnityButton"> <this:UnityControl.Setters> <Setter Property="FrameworkElement.Height" Value="50"/> <Setter Property="Canvas.Left" Value="100"/> <Setter Property="Canvas.Top" Value="100"/> </this:UnityControl.Setters> <this:UnityControl.Properties> <this:PropertySetter Property="Content" Value="{StaticResource resourceString}"/> </this:UnityControl.Properties> </this:UnityControl> As you can see UnityControl extension now contains a collection property "Setters" accepting objects of "Setter" type. "Setters" collection is processed using dependency property descriptors only. "Properties" collection property accepts objects of "PropertySetter" type and is processed using PropertyInfo. This means that PropertySetter can be also defined for a common dependency property, but it doesn't support attached dependency properties. So as you can see this modification provides backwards compatibility for the samples from the previous article. Main purpose of using standard Setter class as I've said earlier is restricting users from defining plain strings for property names. For the markup be valid each setter must have any valid dependency property defined. Thus PropertySetter accepts string based values and is more error prone. Each approach has it's advantages and disadvantages so it's up to you deciding which and at what time to use. Dependency Property Binding Delegation As you can understand the markup extensions' inner world from the previous articles it's quite difficult to control standard Binding markup extension. And guess it's nearly impossible (at least for current version of WPF) subclassing it or providing deferred initialization/execution mechanisms. This means we cannot use default Binding extension for delegation purposes as we cannot control it's life cycle. The most easiest way was to follow the "PropertySetter" approach and introducing "BindingSetter" class for wrapping most basic functionality for establishing a binding. Here's the source code for such a wrapper: public sealed class BindingSetter { public DependencyProperty Property { get; set; } public string ElementName { get; set; } public DependencyProperty Path { get; set; } public BindingMode Mode { get; set; } public IValueConverter Converter { get; set; } public object ConverterParameter { get; set; } public object FallbackValue { get; set; } } So the main task for UnityControl extension will be enumerating the setters defined and constructing appropriate Bindings for the resolved FrameworkElement. Let's take a Canvas panel and inject the TextBox based control by "UnityTextBox" name like the following: <this:UnityControl Name="txtBox" Container="defaultProvider" Dependency="UnityTextBox" Content="Default"> <this:UnityControl.Setters> <Setter Property="FrameworkElement.Width" Value="100"/> </this:UnityControl.Setters> </this:UnityControl> Next let's inject a Button based control named "UnityButton": <this:UnityControl Name="btn1" Container="defaultProvider" Dependency="UnityButton"> <this:UnityControl.Setters> <Setter Property="FrameworkElement.Height" Value="50"/> <Setter Property="Canvas.Left" Value="100"/> <Setter Property="Canvas.Top" Value="100"/> </this:UnityControl.Setters> <this:UnityControl.Bindings> <this:BindingSetter Property="ContentControl.Content" ElementName="txtBox" Path="TextBox.Text" Mode="OneWay" Converter="{StaticResource testConverter}" ConverterParameter="[{0}]"/> </this:UnityControl.Bindings> </this:UnityControl> Button will be have a Height value of 100 and will be positioned at (100,100) point at the Canvas. Also we define a one way binding between Button.Content and TextBox.Text. Also for demonstration purposes I've configured simple value converter and converter parameter. Converter is formatting the input string according to parameter template. UnityControl markup extension takes the element resolved via Unity Container and configures the Binding manually. Of course it is not fully functional and there's no MultiBinding support (guess this will require another enhancement for future articles) but demonstrates that it is possible to do.
Code Behind Support It is obvious that you won't get all the required functionality by declaring xaml only. It logically comes out that next thing you might require is accessing the injected elements from code behind. This is also required for Binding establishment. You might have already noticed the "Name" attribute definition in the samples above. <this:UnityControl Name="txtBox" Container="defaultProvider" Dependency="UnityTextBox" Content="Default"> <this:UnityControl.Setters> <Setter Property="FrameworkElement.Width" Value="100"/> </this:UnityControl.Setters> </this:UnityControl> Note that my implementation of Name property is not the same as x:Name markup extension. Name property is used in this case for delegating x:Name for the control being injected using the Unity Container. It is not mandatory so you can omit it if not required. Upon injection UnityControl extension takes the Namescope of the parent container and registers the newly resolved element within it's scope using the Name property if it was defined. This brings out that the following Window.Loaded event handler be fully functional: void Window1_Loaded(object sender, RoutedEventArgs e) { // Get the control injected with the UnityControl extension TextBox txtBox = canvas.FindName("txtBox") as TextBox; } So this means that declaring injection in the Xaml you still have access to the element being injected and not taking care about the injector markup itself. The source code for the article can be found here. April 01 Injecting Xaml with Unity Application Block using Markup Extensions. Part 2.Notes: This article assumes that you are familiar or started playing with a Unity Application Block. I won't dwell on describing it's architecture or manuals so please refer to the link mentioned for more details. IntroductionIn the previous article I've described how user custom controls can be injected into Xaml markup without code behind operations using custom Markup Extensions. I've mentioned that there might be more easy way of introducing common WPF controls without any need for subclassing. So my second article will cover this very situation. Aliases and Type MappingSimilar to declaring contract interfaces and mapping all the required types to single content you can have no predefined contract. Dealing with UI controls common UIElement class may serve perfectly well as a contract. Especially if you noticed that my UnityReference markup extension was dealing with UIElement objects mainly. So UIElement alias declaration within our "App.config" file can be the following <typeAlias alias="UIElement" type="System.Windows.UIElement, PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> Similar to the line above you are able to declare aliases for any set of controls required for your use in the Unity Container. Here's what I'll be playing with later on: <typeAlias alias="Button" type="System.Windows.Controls.Button, PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> As you can see I've just declared four standard controls. Container definition is extremely simple <container name="containerOne"> <types> <type type="UIElement" mapTo="Button" name="UnityButton"/> <type type="UIElement" mapTo="TabControl" name="UnityTabControl"/> <type type="UIElement" mapTo="TextBox" name="UnityTextBox"/> <type type="UIElement" mapTo="ListBox" name="UnityListBox"/> </types> </container> All the controls are mapped under base UIElement type. Each control name is prefixed with a "Unity*" to bring more clearance in the markup (this is just for testing purposes and you can provide any name you want). Container Provider and Configuration Markup ExtensionBoth Container Provider and Configuration extension remains unchanged so you can refer to the previous post or source code for more detailed information. UnityControl Markup ExtensionHere comes the most important part. UnityControl markup extension is based on the previous "UnityReference" one though extended with a set of more efficient facilities. Support for property setters As this extension is being transformed into UIElement it is obvious that we might want defining some properties not covered with initialization phase or whatever. For this very purpose I've introduced dedicated collection of my custom "PropertySetter" classes. PropertySetter contains only two properties: "Property(string)" and "Value(object)" Here's the basic example what I'm talking about <this:UnityControl Container="defaultProvider" Dependency="UnityListBox"> <this:UnityControl.Properties> <this:PropertySetter Property="Background" Value="Wheat"/> <this:PropertySetter Property="Height" Value="100"/> <this:PropertySetter Property="ItemsSource"> <this:PropertySetter.Value> <x:Array Type="sys:String"> <sys:String>Hello</sys:String> <sys:String>World</sys:String> </x:Array> </this:PropertySetter.Value> </this:PropertySetter> </this:UnityControl.Properties> </this:UnityControl> Here I've injected some UI control named "UnityListBox", specified Background and Height. Also I've provided a list of strings to be applied as an ItemsSource. <this:UnityControl Container="defaultProvider" Dependency="UnityTabControl"> <this:UnityControl.Properties> <this:PropertySetter Property="Height" Value="100"/> <this:PropertySetter Property="ItemsSource"> <this:PropertySetter.Value> <x:Array Type="TabItem"> <TabItem Header="Page1"/> <TabItem Header="Page2"/> </x:Array> </this:PropertySetter.Value> </this:PropertySetter> </this:UnityControl.Properties> </this:UnityControl> Here I've injected a "UnityTabControl" element and predefined two pages of TabItem type. The property/value conversion procedure is rather dummy and the subject to change but you are able configuring some properties and getting the basic idea. Note that I've supported only Dependency Properties. Default Content Property Support Next thing I wanted a lot is specifying the Content of UIElement in the common WPF manner without declaring any additional tags. Most of the standard controls have some properties marked with the ContentPropertyAttribute pointing to the property that will be defined when specific declaration is omitted. For this case I've also implemented additional property "Content" for my markup extension and delegated it's value to the control being injected with the container. Here's the sample: <this:UnityControl Container="defaultProvider" Dependency="UnityButton"> <this:UnityControl.Properties> <this:PropertySetter Property="Height" Value="50"/> </this:UnityControl.Properties> <Image Source="Images/data.png" Width="32" Height="32" Stretch="Uniform"/> </this:UnityControl> In this sample I'm injecting "UnityButton" element defining it's Height property value. Also pay attention to the Image control declaration as a default content. This will be delegated to the button's content after injection. Omitting the properties you can use the following declaration either: <this:UnityControl Container="defaultProvider" Dependency="UnityButton"> <Image Source="Images/data.png" Width="32" Height="32" Stretch="Uniform"/> </this:UnityControl> The content can be also defined using the following markup <this:UnityControl Container="defaultProvider" Dependency="UnityTextBox" Content="Default text"/> In this sample I'm injecting the "UnityTextBox" control providing the Content as "Default text" string. As TextBox defines "TextProperty" dependency property as a default content I've defined the TextBox text here. ConclusionThis approach may help you dynamically configuring your UI without rebuilding main application. Changing the type mapping or introducing new aliases you bring more power and extensibility. Any time any control can be patched or changed to completely new implementation. Note: I've rebuild Unity targeting .net 3.5 framework. Binaries can be found in the "binaries" folder of the project. March 31 Injecting Xaml with Unity Application Block using Markup ExtensionsNotes: This article assumes that you are familiar or started playing with a Unity Application Block. I won't dwell on describing it's architecture or manuals so please refer to the link mentioned for more details. Today I've got another drop of Unity Application Block and decided to play with it in the scope of WPF to see what ideas might appear and how flexible Unity may be dealing with Xaml. For testing purposes I've decided to get Unity Container being referenced totally within the markup without code behind usage. Additionally I wanted to get some dummy UI elements built up by the container and injected into the page. It took me approximately 2 hours of playing and I must say I was quite satisfied with the results. I'll be giving just the pure idea to be elaborated without any fancy visual stuff. That's up to you guys to get anything worth usable from it. Common Unity Container can be configured in two ways. It is either manual configuration and initialization at code behind or using configuration file ("App.config" or "Web.config"). Of course for keeping experiment clear I've chosen the latter one - application configuration. Here's a simple snippet that can be found in the documentation file <?xml version="1.0" encoding="utf-8" ?> So we declare common Unity section called "unity" and one container called "containerOne". Type aliases and container types will be described later on. Next step is bringing a container to the upper level from code behind to markup. We need something declared within the page that can be referenced to from the other elements. It can be achieved either by declaring a static resource or introducing some bridge control wrapping your Container. I've chosen the latter one because it might give me possibilities easy accessing my container either from xaml or code behind depending on situation and complexity of requirements. Unity Container Provider Simple initialization of Unity Container from code behind looks like the following IUnityContainer container = new UnityContainer(); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); section.Containers["containerOne"].Configure(container); So we need to know the section name and container name to create and configure properly our container. This means that theoretically we could have some element on the page like following: <ContainerProvider Name="defaultProvider" Section="unity" Container="containerOne"/> Such control doesn't require any UI representation and can serve like data provider similar to Windows Forms Designer age ;) To save your time for future enhancements there's quite a good reason providing additional facility that will populate any control with a Unity Container (if appropriate property is supported by control) and so giving you possibility integrating the logic in more complex layouts and implementations. This is where Markup Extensions come. Unity Configuration Extension (UnityConfiguration.cs) using System; using System.Configuration; using System.Windows.Markup; using Microsoft.Practices.Unity; using Microsoft.Practices.Unity.Configuration; namespace UnityTesting1 { [MarkupExtensionReturnType(typeof(IUnityContainer))] public class UnityConfiguration : MarkupExtension { #region Properties [ConstructorArgument("section")] public string Section { get; set; } [ConstructorArgument("container")] public string Container { get; set; } #endregion public override object ProvideValue(IServiceProvider serviceProvider) { IUnityContainer container = new UnityContainer(); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection(Section); section.Containers[Container].Configure(container); return container; } #region ctors public UnityConfiguration() { } public UnityConfiguration(string section, string container) { this.Section = section; this.Container = container; } #endregion } } Here I've introduced a very simple markup extension that will provide our elements with a Unity container taken from application configuration. It has two properties: "Section" for section name and "Container" for referencing some required container within the configuration section. For getting more really good details on markup extensions you may refer to Rob Relyea's blog So assuming our container provider has a dedicated property for storing a container it can now be declared like the following: <ContainerProvider Guess it's a more convenient way using markup extension in this case because as you've might understood that it can be applied to any object that holds IUnityContainer child as a property. Using extension above our container holder source becomes extremely simple: Container Provider Contract (IContainerProvider.cs) public interface IContainerProvider This is mainly for future extensibility and flexibility. Container Provider (ContanerProvider.cs) public class ContainerProvider : FrameworkElement, IContainerProvider { public IUnityContainer Container { get; set; } } As you can see we are having more lookless stub I think than fully featured control. You can get rid of it or introduce new features at any time. Just to ensure injection methods works fine and to have a stab for future implementations I've introduced a dummy service called "ContentResolver" that it to be used as a singleton and take part in the entities buildup process. It won't do a lot but demonstrate the possible usage for flexible initialization of our UI elements. Content Resolver Contract (IContractResolver.cs) public interface IContentResolver { string GetName(); } Content Resolver (ContentResolver.cs) public class ContentResolver : IContentResolver { #region IPersonResolver Members public string GetName() { //return Guid.NewGuid().ToString("B"); return DateTime.Now.ToLongTimeString(); } #endregion } Our following controls will be taking the data via this service. Again reminding that this is absolutely optional and can be skipped. Next step is providing some UI elements we will build up via our Unity container. I'll give two samples: Image and ListBox controls just to catch the main idea. Our sample controls will be exposing IContent contract for flexibility purposes Content Contract (IContent.cs) public interface IContent { } I will use it mainly for identification and generalization purposes so the contract doesn't have any members. Image Content (ImageContent.cs) public class ImageContent : Control, IContent { public string ImagePath { get; set; } public string Text { get; set; } static ImageContent() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageContent), new FrameworkPropertyMetadata(typeof(ImageContent))); } [InjectionMethod] public void Initialize(IContentResolver resolver) { this.Text = resolver.GetName(); this.ImagePath = "Images/gear.png"; } } Our simple control has two properties "ImagePath" and "Text". Normally they should be Dependency Properties but I've simplified it to have a compact view of general info ;) It has a customized template (see attached sources for more details). Also you can see the injected method that provides basic initialization of a control. Here you normally insert more efficient stuff required for controls and taken from external singleton based (in my implementation) service. ListBox Content (ListBoxContent.cs) public class ListBoxContent : ListBox, IContent { public ListBoxContent() { this.Items.Add("One"); this.Items.Add("Two"); this.Items.Add("Three"); this.Items.Add("Four"); this.Items.Add("Five"); } } Next control represents a common ListBox. It doesn't require any initialization injection and has no overridden templates. Actually you can avoid creating wrapper classes for standard controls and this is what normally you will do in a real-life development. As this will require using Activator class and dynamic Type instantiation I've decided to move the simple way to keep the code and configuration file more understandable. But keep in mind that it's absolutely possible and there's no problems implementing that for you in future. Configuration file (App.config) Here's how our final configuration file that we'll be using across the sample application: <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </configSections> <unity> <typeAliases> <typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" /> <typeAlias alias="IContentResolver" type="UnityTesting1.IContentResolver, UnityTesting1"/> <typeAlias alias="ContentResolver" type="UnityTesting1.ContentResolver, UnityTesting1"/> <typeAlias alias="IContent" type="UnityTesting1.IContent, UnityTesting1"/> <typeAlias alias="ImageContent" type="UnityTesting1.ImageContent, UnityTesting1"/> <typeAlias alias="ListBoxContent" type="UnityTesting1.ListBoxContent, UnityTesting1"/> </typeAliases> <containers> <container name="containerOne"> <types> <type type="IContentResolver" mapTo="ContentResolver"> <lifetime type="singleton" /> </type> <type type="IContent" mapTo="ImageContent" name="UnityImage"/> <type type="IContent" mapTo="ListBoxContent" name="UnityList"/> </types> </container> </containers> </unity> </configuration> I've created all required aliases and filled container with appropriate types. Our Image and ListBox content will be accessed using "UnityImage" and "UnityList" names. So if we were using the code behind approach we would resolve our "UnityList" using the following line: IContent content = container.Resolve<IContent>("UnityList"); This returns as an object of ListBoxContent type that is actually a subclass of ListBox and so UIElement that can be easily added to the visual tree of a panel, canvas or whatever. Analyzing content creation you can see that generally for creating our control we need initialized Unity container and name of the element to be resolved. This can be theoretically put to xaml in the following way: <SomeElement Container="defaultProvider" Dependency="UnityList"/> We have already discussed container provider element so how exactly we can reference it in the code and what else should be created that can cover all our content controls? We definitely need another markup extension that will give us all the required possibilities... Unity Reference Markup Extension (UnityReference.cs) [MarkupExtensionReturnType(typeof(UIElement))] public class UnityReference : MarkupExtension { #region Properties [ConstructorArgument("container")] public string Container { get; set; } [ConstructorArgument("dependency")] public string Dependency { get; set; } #endregion public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget ipvt = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); FrameworkElement fe = ipvt.TargetObject as FrameworkElement; FrameworkContentElement fce = ipvt.TargetObject as FrameworkContentElement; if (fe != null) { IContainerProvider containerProvider = fe.FindName(Container) as IContainerProvider; if (containerProvider == null) throw new Exception("IContainerProvider not found!"); return containerProvider.Container.Resolve<IContent>(this.Dependency); } else if (fce != null) { IContainerProvider containerProvider = fce.FindName(Container) as IContainerProvider; if (containerProvider == null) throw new Exception("IContainerProvider not found!"); return containerProvider.Container.Resolve<IContent>(this.Dependency); } else throw new Exception("Container " + Container + " cannot be resolved!"); } #region ctors public UnityReference() { } public UnityReference(string container, string dependency) { this.Container = container; this.Dependency = dependency; } #endregion } Here we've introduced a unified approach for presenting our Unity based controls using a simple (hope so) markup extension. Next it becomes possible using the following declaration in a xaml: <UnityReference Container="defaultProvider" Dependency="UnityList"/> As markup extension turns into the UIElement resolved within the "defaultProvider" container holder we get a fully functional ListBoxContent control at the end of processing. Finally here comes the sample application that can be constructed of such blocks <Window x:Class="UnityTesting1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:this="clr-namespace:UnityTesting1" Title="Window1" Height="600" Width="800"> <DockPanel LastChildFill="True"> <this:ContainerProvider x:Name="defaultProvider" Container="{this:UnityConfiguration Section=unity, Container=containerOne}"/> <StackPanel DockPanel.Dock="Left" Width="200" Background="AliceBlue"> <this:UnityReference Container="defaultProvider" Dependency="UnityList"/> </StackPanel> <Canvas x:Name="canvas"> <Button Width="100" Content="{this:UnityReference Container=defaultProvider, Dependency=UnityImage}"/> </Canvas> </DockPanel> </Window> The source code for the article can be found here March 17 Silverlight Diagramming. Part 1.
Notes: The article and samples are based on Silverlight 2 Beta 1. For more details refer to Microsoft Silverlight site. Sample project requires "Microsoft Silverlight Tools Beta 1 for Visual Studio 2008". Details for installing the tools you can find here. Introduction When I've downloaded and installed the first cooked beta for Silverlight 2 I was really enraptured from what I've seen there. I didn't expect such a huge jump ahead in such a small term (comparing with previous versions of Silverlight). At last my dream is started to come true. I've been working for several years with the early versions of Ajax especially the Michael Schwarz xmlhttp implementation which I personally considered to be the best among the variety of existing projects. Later on with appearance of Atlas I've also devoted a lot of time digging and elaborating. But WPF appearance dramatically changed my attitude towards the technologies and products I want spend my working and spare time with. Being rather tired of ASP.NET and JavaScript "spaghetti coding" I didn't touch a lot first Silverlight versions. I was clear enough for me that further versions of Silverlight have to provide the developers much more power in the future releases and we all should only wait patiently when WPF will get another Renderer rather then emulation for IE. This is when comes Silverlight 2.0. Even the first beta can impress anyone being really in love with .net 3.5/WPF. Xaml, Dependency objects/properties, C# and a great number of other candies. You should really take a loot at that! I appreciate the work done by MS guys to deliver it to the community and realize how much work is to be performed for us to get the real power Silverlight is supposed to bring. Diagramming sample Actually my intention was to test how much time could take me to cook anything working with Silverlight 2 beta 1. As we are facing something like WPF port I've decided to test how the common basic project could be ported to browser. First of all I was interested in problems rather than successful results. Anyway I was pleased much with both. As it is the first beta and serve for demonstration purposes my hands were quite tied in doing some efficient code behind. Even having nearly everything as "sealed" I must admit it is quite possible implementing a lot :) It took me only one evening (approximately 3 or 4 hours of pure time) porting basics of my previous diagramming samples to Silverlight 2. Later on I'll continue enhancing the sample to eliminate difference between my two implementations. SilverlightDiagramming.Core I've implemented a separate Silverlight Library for placing some common stuff there and also testing the Library project itself. I didn't manage to find any helper class for walking visual and logical trees so I've implemented simple LogicalTreeHelper for my own needs, mainly hit testing the canvas and recursive parent search. Shapes As I've stuck a bit with styling and templating (I've played a bit with generics.xaml and it's compilation as a Resource within the Library projects but guess this part is an early beta so I couldn't get most of it I wanted) and downgraded to introducing simply separate User Controls for each of the shape. Also I managed to do the basic class for all the shapes to suite as an example later on (according to forums there's a lot of troubles with inheritance at Beta 1 especially with dependency objects, but we can live with it up to next betas I think). DiagramCanvas Basic subclass of Canvas panel. Generic approach for adding children and input delegation to the underlying BasicTool for drawing straight line connections. Drag and drop I didn't introduce anything here as there's already cooked drag and drop samples that can be found in the Silverlight 2 documentation. Of course later on it can be replaced with some more efficient managers or whatever but as for now the MS sample is really good. Connections and bindings I've decided not to implement fancy bindings/multibindings for the first version as again I was interested in time more than additional facilities. I came across some issues with code behind bindings and quickly switched to INotifyPropertyChanged interface implementation. The only code behind binding sample between ToggleButton and StraightLineTool can be found in the Page.xaml.cs. Line positioning was left to owner driven layout updates on drag operations.
Conclusion I must admit that it was a real pleasure preparing this sample. I'll definitely continue enhancing the sample described alongside with preparing some more detailed overviews of issues I came across and walkarounds performed. The source code for the article can be found at my SkyDrive January 15 Make your WPF controls fully extensible using WCF IExtensibleObject pattern (.net 3.0)
Two days ago while reading MSDN documentation details on WCF's NetDataContractSerializer I found a reference to the IExtensibleObject interface from "System.ServiceModel.dll". There were no details concerning what for the sample serializable object was exposing it as it was out of the sample scope. So I tried to figure out the purpose of this interface and how it can be reused in my applications when not dealing with Communication Foundation. Of course as you might have noticed the 2008 MSDN library contains almost no developer-readable information and the situation gets worse and worse ;), the Googling also gave me nothing except various annoying reprints of MSDN articles. It took me two days of investigations to figure out how much power this pattern can bring to common applications. IExtensibleObject<T> Exposing this interface "Enable an object to participate in custom behavior, such as registering for events, or watching state transitions [...] For example, if you are implementing a TreeNode class and want to allow external code to add annotations and methods to some instances of TreeNode, you can have TreeNode implement IExtensibleObject<TreeNode>. This enables code that uses TreeNode to add objects that implement IExtensibleObject<TreeNode> to the Extensions collection." (MSDN). And four confusing samples from Windows Communication foundation... ;) So starting implementation of our extensible class... public class Person: IExtensibleObject<Person> public IExtensionCollection<Person> Extensions { get; private set; } #endregion
}
Actually I like those Persons and Dogs samples all over the MSDN so continued the tradition :) IExtensibleObject<Person> requires you implementing only the strong-typed "Extensions" collection. We don't require any extreme logic so using the default out-of-box ExtensionCollection. For testing and debugging purposes I also define the name of our person to something meaningful :) IExtensionCollection<T> Won't be rewriting the obvious information, guess you should better take a look the original article. It is supposed to hold the extensions for the extensible object. Default ExtensionCollection class provides you the possibility of fetching the result sets from it entries directly according the type you are acquiring. For more details see Find<E> and FindAll<E> details for the collection. IExtension<T> This is what your "Extensions" collection expects you to provide ;) Each your extension should expose this interface having the same <T> as the object being extended. Guess it might be more easy to get the idea from the sample below public class PersonExtension : IExtension<Person> { #region IExtension<Person> Members public void Attach(Person owner) { Console.WriteLine("Attaching to owner called: " + owner.Name); } public void Detach(Person owner) { Console.WriteLine("Detaching from owner called: " + owner.Name); } #endregion } IExtension<T> exposing needs two methods to be implemented by the author of the extension class: Attach and Detach. Very easy and the main things needed to extend the owner. Before each extension is added to the Extensions collection the Attach method is called to provide extension with possibility to initialize itself (cache the parent instance, attach events, change properties, whatever you need). Each time the extension is removed from the collection the Detach method is called so the extension class finalize itself, release resources, detach event handlers, etc. Each time for both steps extension gets it's owner in order. In this sample I won't be doing anything except sending a notification with a current owner name just to be sure the code is correct. Putting it all together Person p = new Person() { Name = "Denis Vuyka" }; p.Extensions.Add(new PersonExtension()); You create an instance of the Person class, set the name property and add a new instance of the PersonExtension to it. Upon adding the extension you will see the "Attaching to owner..." message in your VS Output window. So the code is working.
Extending WPF UI elements Another good sample would be showing some UI element being extended. I've chosen a Canvas but you are free to choose anything you need actually. public class ExtensibleCanvas : Canvas, IExtensibleObject<ExtensibleCanvas> { public ExtensibleCanvas() : base() { this.Background = Brushes.Transparent; this.Extensions = new ExtensionCollection<ExtensibleCanvas>(this); } #region IExtensibleObject<ExtensibleCanvas> Members public IExtensionCollection<ExtensibleCanvas> Extensions { get; private set; } #endregion } Again as in the previous sample we create our custom class inheriting IExtensibleObject and a Canvas control. As I will be playing with Mouse events I set the "Background" property of the canvas to "Transparent" to have a valid mouse responses. Also again we initialize our Extensions collection providing ourselves as an owner of the collection. As I want just simply react to the MouseLeftButtonDown event of the ExtensibleCanvas the code sample for the extension will be the following public class CanvasExtension : IExtension<ExtensibleCanvas> { #region IExtension<ExtensibleCanvas> Members public void Attach(ExtensibleCanvas owner) { owner.MouseLeftButtonDown += new MouseButtonEventHandler(owner_MouseLeftButtonDown); } void owner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("Extension report: MouseLeftButtonDown event hooked!"); } public void Detach(ExtensibleCanvas owner) { owner.MouseLeftButtonDown -= owner_MouseLeftButtonDown; } #endregion } During attach phase we wire up the MouseLeftButtonDown event with our own extension handler and release it during the detach phase. Each time the left button of the mouse if down we simply display a message box. Putting it all together Here's the source for our sample window <Window x:Class="CanvasExtensions.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ext="clr-namespace:CanvasExtensions" Title="Window1" Height="300" Width="300"> <ext:ExtensibleCanvas x:Name="myExtensibleCanvas"> </ext:ExtensibleCanvas> </Window> Here we just simply registered our local namespace and declared the ExtensibleCanvas instance called "myExtensibleCanvas". Here's the source code for our sample window using System; using System.Windows; namespace CanvasExtensions { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); // Testing extending the UI (Canvas extensions) myExtensibleCanvas.Extensions.Add(new CanvasExtension()); } } }Just add a new instance of Canvas extension, run application and click somewhere on the canvas. You'll get the message box with some text in it.
Extending dependency objects As I immediately started to fill the power of this pattern (thanks to Microsoft guys who implemented it :) ) I've decided to gain more control over the dependency objects that they can give me within their public properties and events. I've decided that there's a cases when "wise" extension could have the need integrating deeper into the property system of the owner object thus I won't have the possibility or time extending the owner to support "OnChanged" events. So this example will show you how to extend the dependency objects (so read "all the UI controls present in WPF also" :) ) that don't want to tell anyone of their properties changes. Let's extend our Person class to be dependency object having "Name" property as a dependency one: public class DoPerson : DependencyObject, IExtensibleObject<DoPerson> { public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } // Using a DependencyProperty as the backing store for Name. This enables animation, styling, binding, etc... public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(DoPerson), new UIPropertyMetadata("Default Name")); public DoPerson() { Extensions = new ExtensionCollection<DoPerson>(this); } #region IExtensibleObject<DoPerson> Members public IExtensionCollection<DoPerson> Extensions { get; private set; } #endregion } Now we have DependencyObject Person or DoPerson for short. Let's imagine someone else had written this class and I have no possibility of tracking whether the name of DoPerson object has changed indeed. Thanks for WPF dependency system this is also possible via the "DependencyPropertyDescriptor". Let's imagine your extension wants to know when the Name property of it's owner is changed and want to slightly modify resulting value according to some internal rules. Here's how will look like the updated version of extension called DoPersonExtension. For educational purposes and code clearance I didn't implement any checks and safe casting, hope you'll understand that right public class DoPersonExtension : IExtension<DoPerson> { DependencyPropertyDescriptor dpDes; bool NamePropertyLock = false; #region IExtension<DoPerson> Members public void Attach(DoPerson owner) { dpDes = DependencyPropertyDescriptor.FromProperty(DoPerson.NameProperty, typeof(DoPerson)); dpDes.AddValueChanged(owner, HookOwnerNameChanged); } public void Detach(DoPerson owner) { dpDes.RemoveValueChanged(owner, HookOwnerNameChanged); } #endregion private void HookOwnerNameChanged(object sender, EventArgs e) { // avoid entering endless loop here if (NamePropertyLock) return; NamePropertyLock = true; ((DoPerson)sender).Name += "(hooked by extension)"; NamePropertyLock = false; } } What a hell the extension is doing here... :) It extracts the DependencyPropertyDescriptor from the DoPersonClass for his dependency property Name and attaches it's own handler for value changed event that is raised regardless the DoPerson notifies about or not ;) As now both the extension and owner classes are dealing with the same property it is obvious that some locking should be implemented not to bring application into endless loop when changing the Name and so avoiding overflow exceptions. Upon detachment from the owner extensions collections DoPersonExtension clears it's own garbage by removing the event handler for the property descriptor. The testing lines for the sample above could be similar to following // Testing extending of DependencyObject hooking the change of properties // when no change events are supported withing the object DoPerson doPerson = new DoPerson(); doPerson.Extensions.Add(new DoPersonExtension()); Console.WriteLine("Setting name: Denis Vuyka"); doPerson.Name = "Denis Vuyka"; Console.WriteLine("Current name: " + doPerson.Name); I create a DoPerson instance and add the appropriate extension to it. Then I set the value of the "Name" property to my own name :) and instantly put it in the Output window. As our extension hooked the Name setter we have the following message as a result: "Current name: Denis Vuyka (hooked by extension)" This is the simplest sample and the rest depends on your imagination. Finalizing my article I'd like to admit that this pattern perfectly fits into MVC pattern as you can simply change the control flow vice versa. The extended object itself can iterate through the extension collection and call appropriate methods and do something he needs to do. So it means the Controller can also get the power unleashed :) Have a nice coding...
January 10 Upcoming WPF Diagramming framework
As Francois Vanderseypen has disclosed some info in his post Mindmapping layout in WPF I've also decided to make a small announcement towards that :) We joined our forces on the way implementing full "WPF replacement" for Netron 3 due to a small mindmapping application that resides on the top of the it. For the last couple of months it takes all my spare time but I must admit it really brings a lot of pleasure working with WPF and .net 3.5 in this way :) We try to get the most of MVC and MS service buses approaches to perform scalable and highly extensible diagramming framework. While you can see the layout details and screenshots here I'll single out some additional features that might be of interest to developer audience. Stress on lightweight MS service bus architecture close to MS designer re-hosting approaches so to be very intuitive for those guys familiar with Windows Forms and Workflow designers re-hosting. Some common services like unlimited Undo/Redo, Zooming/Panning, Drag/Drag to scroll, Single and Multiselection, Movement/Resizing, exporting content to different formats including images and XPS. Variety of Line connectors and different types of Shapes. Stress on maximizing the extensibility of the core designer functionality. Nearly each part of the functionality can be plugged in/changed/disabled by the single developer according to the needs. Flexible service model. Different drawing surfaces for diagram types like trees, mindmaps, flowcharts etc. Even writing this very small list of features requires me a lot of investigations towards the native WPF features and possibilities of implementation this or that feature. Due to the lack of documentation and good samples for WPF (especially in diagramming and drawing) I have to surf with Reflector and google a lot :) This also gives me some additional and rare material worth placing into separate blog posts concerning WPF itself. I'll try to find some free hours in the nearest future for that :) Of course we are only on the way preparing some world-ready app demos but the progress is very stable ;) Meanwhile I would advice the people really interested in diagramming and WPF solutions stay on the line for other news within "The Orbifold" and my blog. Thanks for your attention. December 15 WPF. Simplify your life with Linq extension methods, Canvas and Visual Tree helpers.This article dwells on .net 3.5 and Visual C# 2008 Express Edition. Up to current moment I've been using the Canvas element very much with the diagramming stuff that I described earlier. As you implement something similar you definitely come across inconvenience using the standard Children collection property. There's very little changes that you will be using only UIElement objects. For example you have some shapes exposing different interfaces, something like ISelectable, IDraggable or anything else. Additionally you have a variety of visual connections and drawings inheriting Paths, Geometry or Figures. So each time you come across type casting and code longer that it could be ;)
1. Extending base classes When you need three or more core functions for manipulating canvas children and don't have the possibility of inheriting/implementing additional stuff for your own classes there's a good opportunity of extending the Canvas class itself using a couple of Linq extension methods for that. using System.Windows; using System.Windows.Controls; namespace UsefulExtensions.Extensions { public static class CanvasExtension { public static void AddChild<T>(this Canvas canvas, T element) { UIElement uiElement = element as UIElement; if (uiElement != null && !canvas.Children.Contains(uiElement)) canvas.Children.Add(uiElement); } public static void RemoveChild<T>(this Canvas canvas, T element) { UIElement uiElement = element as UIElement; if (uiElement != null && canvas.Children.Contains(uiElement)) canvas.Children.Remove(uiElement); } public static void InsertChild<T>(this Canvas canvas, int index, T element) { UIElement uiElement = element as UIElement; if (uiElement != null && !canvas.Children.Contains(uiElement)) canvas.Children.Insert(index, uiElement); } } } Of course you should carefully read the documentation towards the extension methods at least at your local VS MSDN library. There's a good explanation how it works and where and when to use these features. Similar to "System.Linq" whenever you declare the namespace "UserfulExtensions.Extensions" (or your own namespace name) you get the additional methods for all the Canvas based classes.
The best thing I like using generics is the transparency of T provided. That's a really great feature having the possibility to omit the T declaration within the generic method. Of course sometimes it may bring to small problems but in most cases helps you a lot. Having shapes exposing for example IDraggable and ISelectable interfaces you don't need to explicitly define the type as T when calling generic methods. The method call will look like following: myCanvas.AddChild(myShape1); myCanvas.AddChild(myShape2) The T will be automatically resolved by the type/interface of the parameter provided. You can actually pass anything you to the method but that's not the fact the element will be added to the Canvas. As you have seen earlier the extension methods require the element be at least of UIElement type to perform an action, so the next thing will also compile but won't be executed at runtime. myCanvas.AddChild(0) myCanvas.AddChild(new Point(10,10)) As I've already said I like the generics feature of resolving the <T> and I think it's really useful.
2. Implementing helper classes 2.1. Canvas helper Helper classes is the thing each developer implement very often. When you use the canvas child maintenance using the methods above you come to conclusion that the standard "Canvas.GetLeft" or "Canvas.GetTop" are not enough flexible for you as they also deal with UIElement objects while you require some generics approach for that. It's very easy to implement a static helper class to correct that. Here's a quick snippet I use often CanvasHelper.cs using System; using System.Windows; using System.Windows.Controls; namespace UsefulExtensions.Helpers { public static class CanvasHelper { public static double GetLeft<T>(T element) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); return (double)uiElement.GetValue(Canvas.LeftProperty); } public static double GetTop<T>(T element) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); return (double)uiElement.GetValue(Canvas.TopProperty); } public static Point GetPosition<T>(T element) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); return new Point( (double)uiElement.GetValue(Canvas.LeftProperty), (double)uiElement.GetValue(Canvas.TopProperty)); } public static void SetLeft<T>(T element, double length) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); uiElement.SetValue(Canvas.LeftProperty, length); } public static void SetTop<T>(T element, double length) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); uiElement.SetValue(Canvas.TopProperty, length); } public static void SetPosition<T>(T element, Point value) { UIElement uiElement = element as UIElement; if (uiElement == null) throw new ArgumentNullException("element"); uiElement.SetValue(Canvas.LeftProperty, value.X); uiElement.SetValue(Canvas.TopProperty, value.Y); } } } I didn't provide any supplementary information for the sources above because I hope it's quite self-describing. As static Canvas members "GetLeft" and "GetTop" deal with the attached dependency properties of the UIElement provided there's quite easy to extend the functionality a bit for supporting the generics approach. Of course this can also be placed as extension to the Canvas class but my intention was to expose the helper classes that can be implemented at any stage of the project life cycle. Also I think these methods deal with attached properties of UIElement the extension method for Canvas won't be the right place of storing our generics stuff.
2.2. Visual Tree Helper Standard VisualTreeHelper class contains a lot of important and useful stuff helping developers a lot. Usually I use if for hit testing and it helped me much with rubberband selection tool implementation. The only thing it lacks very much is the more complicated Parent processing functionality. According to the MSDN forums a lot of people came across one problem related to the control templates and templating stuff. When dealing with hit testing it's rather difficult from the first steps provide hit testing features for the elements totally based on Control Templates because hit testing will work for each element within the template separately. Assuming that you might have some sort of shape containing a text box, an image element and something like text block I clearly understand the troubles you might experience when trying to hit-test it or detect clicks. The only good solution can be found across forums is navigating with the visual tree to get the parent of the Control Template and so find the element required instead of controls within the template. Simple recursive function is quite enough but as I prefer working with generics I've implemented another look of the function. VisualTreeHelperEx.cs using System.Windows; using System.Windows.Media; namespace UsefulExtensions.Helpers { /// <summary> /// Provides additional functionality alongside standard VisualTreeHelper. /// </summary> public static class VisualTreeHelperEx { /// <summary> /// Returns current object or ony parent object that expose the type provided. /// Very useful when accessing the main container for the ControlTemplate or for any elements within the ControlTemplate. /// </summary> /// <typeparam name="T">The type of the object to be searched.</typeparam> /// <param name="obj">Starting object to search for parents or itself.</param> /// <returns>Returns current object if it exposes the required type or any parent from the upper levels or null.</returns> public static DependencyObject GetParent<T>(DependencyObject obj) { if (obj is T) return obj; DependencyObject parent = VisualTreeHelper.GetParent(obj); if (parent == null) return null; else if (!(parent is T)) return GetParent<T>(parent); return parent; } } } As for hit testing itself you will find a lot of information across the MSDN library so I won't dwell on it. This method helps getting exactly the element you are looking for when performing the test. Assume you are implementing the rubberband/multi-selection tool some sort of diagramming application and you definitely know that each of your shape expose something like ISelectable interface. Of course you need to hit-test all the ISelectable based elements on the Canvas. But each you shape also contains a very rich content template making each shape look very fancy. In this case you will simply move to your helper methods defining the shapes regardless their content templates:
private List<ISelectable> hits = new List<ISelectable>(); private List<ISelectable> GetSelectablesHit(Geometry region) { hits.Clear(); GeometryHitTestParameters parameters = new GeometryHitTestParameters(region); HitTestResultCallback callback = new HitTestResultCallback(HitTestCallback); VisualTreeHelper.HitTest((Visual)DrawingSurface, null, callback, parameters); return hits; } private HitTestResultBehavior HitTestCallback(HitTestResult result) { GeometryHitTestResult geometryResult = (GeometryHitTestResult)result; // Get the object regardless ControlTemplate content ISelectable visual = VisualTreeHelperEx.GetParent<ISelectable>(result.VisualHit) as ISelectable; if (visual != null && !hits.Contains(visual) && geometryResult.IntersectionDetail == IntersectionDetail.FullyInside) hits.Add(visual); return HitTestResultBehavior.Continue; } You influence the performance a lot because the function doesn't navigate the element tree each time. if (obj is T) return obj; The first line of the function ensures the actual element is what you are querying and stops the recursion. In the rest cases it will go upper level until it founds the required <T> or return null if nothing was found.
3. Extending interfaces. As I've been using IServiceProvider interface for organizing the basic service bus for my diagramming sample application I again got tired of type casting stuff when querying some service host for the services by their interfaces. So I've decided to extend it without any additional inheritance and changes to interface. Extension methods features helped me very much with it. Here's a quick sample: IServiceProviderExtension.cs using System; namespace UsefulExtensions.Extensions { public static class IServiceProviderExtension { public static T GetService<T>(this IServiceProvider provider) { return (T)provider.GetService(typeof(T)); } public static bool Contains<T>(this IServiceProvider provider) { return (provider.GetService(typeof(T)) != null); } } } What does it give you? In all classes exposing IServiceProvider or interfaces inheriting it you get additional methods GetSetvice and Contains This will help you get rid of common type casting and long strings like provider.GetService(typeof(ISelectionManager)) as ISelectionManager by simple provider.GetService<ISelectionManager>() Additionally it provides an extension helping you determine whether the service exists within the provider collection. Of course the "Contains" method is just a sample of implementing the extension method. The best thing as I've mentioned above is that you get this extensions to each inheritance case
The only thing you should remember is adding the namespace of your extension classes to the source files where you deal with IServiceProvider or interfaces exposing it. Hope this will help anyone to increase the coding performance. December 03 WPF Diagramming. Saving you canvas to image, XPS document or raw Xaml.Guess saving visuals is one of the most spoken topics today. Main formats I wanted to implement within my application were png, xps and pure xaml so this is what I'll dwell on below. Exporting canvas to PNG image public void ExportToPng(Uri path, Canvas surface) { if (path == null) return; // Save current canvas transform Transform transform = surface.LayoutTransform; // reset current transform (in case it is scaled or rotated) surface.LayoutTransform = null; // Get the size of canvas Size size = new Size(surface.Width, surface.Height); // Measure and arrange the surface // VERY IMPORTANT surface.Measure(size); surface.Arrange(new Rect(size)); // Create a render bitmap and push the surface to it RenderTargetBitmap renderBitmap = new RenderTargetBitmap( (int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Pbgra32); renderBitmap.Render(surface); // Create a file stream for saving image using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create)) { // Use png encoder for our data PngBitmapEncoder encoder = new PngBitmapEncoder(); // push the rendered bitmap to it encoder.Frames.Add(BitmapFrame.Create(renderBitmap)); // save the data to the stream encoder.Save(outStream); } // Restore previously saved layout surface.LayoutTransform = transform; } I've again tried to make the code self-explaining. You can try to use different encoders for saving the data so it's up to you to look through what ones are available. The main trouble many guys come across is blank images as the canvas output or saving canvas programmatically without visualizing it. The key is measuring and arranging the surface before pushing it to render bitmap. As render bitmap does nothing with measuring and arranging elements in this case, this should be regarded mandatory for you. And of course you should remember the zooming/scaling and rotation matrices ;) the layout transformations will also be saved to the image so you should take care of that manually. In my case I cache the LatoutTransform and reset the original value right before measuring canvas. Paying more attention you can see that bitmap encoder accepts a Visual so canvas is not the only element that can be passed ;) Going down the inheritance tree it is convenient to use the FrameworkElement or pure Visual. FrameworkElement contains Width and Height properties used by canvas. Default set references provided by WPF application template is quite enough to implement this method. Of course you'll have to resolve "using" section ;) Exporting canvas to XPS document Actually this was the first I've started implementing for my application. I like XPS format very much because it is easy to maintain and integrate. Here's the quick snippet of exporting your canvas to XPS public void Export(Uri path, Canvas surface) { if (path == null) return; // Save current canvas transorm Transform transform = surface.LayoutTransform; // Temporarily reset the layout transform before saving surface.LayoutTransform = null; // Get the size of the canvas Size size = new Size(surface.Width, surface.Height); // Measure and arrange elements surface.Measure(size); surface.Arrange(new Rect(size)); // Open new package Package package = Package.Open(path.LocalPath, FileMode.Create); // Create new xps document based on the package opened XpsDocument doc = new XpsDocument(package); // Create an instance of XpsDocumentWriter for the document XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); // Write the canvas (as Visual) to the document writer.Write(surface); // Close document doc.Close(); // Close package package.Close(); // Restore previously saved layout surface.LayoutTransform = transform; } You will need two additional assemblies to be referenced from the GAC: "ReachFramework.dll" and "System.Printing.dll". Again I'd advice you to backup the original layout transformation regardless it's state before processing canvas. You should definitely read more details towards System.IO.Packaging and XPS documents. For those who doesn't know yet the XPS document is presented by zip container for some xml, fonts and binary data like images etc. You can freely rename XPS to ZIP, unpack it and look inside for better understanding when you'll be looking though MSDN articles towards it. I was really pleased to find out that 10 or 20 of my duplicated elements having the same image with content template loaded from the separate assembly wasn't cloned. I had only one image in the XPS for each distinct type of element so the archive size was extremely small for my diagram. Exporting canvas to the XAML I'll give you the most stupid sample that can be found everywhere public void Export(Uri path, Canvas surface) { if (path == null) return; if (surface == null) return; string xaml = XamlWriter.Save(surface); File.WriteAllText(path.LocalPath, xaml); } You get the xaml presentation of your canvas that can be later loaded with XamlReader. Be prepared to cuss MS guys a lot because it is rather difficult to adopt any application load anything except simple xamls into your canvas :) The worst thing is that it doesn't support bindings/multibindings so you'll have to resolve them somehow on document loading. The same is for events and there's a list of things not supported that can be easily found at MSDN shipped with Visual Studio. I've looked through the Internet to find any good solution but there's nothing I liked. Guess if it becomes a hot topic (and I'm sure it will become soon) a lot of good overrides will appear. I'm doing my own implementation but it's in the early stage and cannot be brought to public. Anyway there's a lot of cases when the pure xaml will be quite enough for you. Have a nice coding and investigations. November 24 WPF Diagramming. Undo/Redo service and simple commands.Undo/Redo functionality can be regarded as one of the most important parts of application and not because of it's complexity. This feature can influence the architecture of your application very much (of course if you didn't implement the commands pattern from the beginning) and can make you spend a lot of time for refactoring. Each action in the application can be regarded as some single command. The command knows how to execute an action and so it should know how to undo or redo it easily. In order to provide the list of user-friendly names of commands to be undone the it can also contain some title to be distinguished in the UI. So the basic interface for it can be as following public interface IDiagramCommand { /// <summary> /// Executes the actual action /// </summary> void Execute(); /// <summary> /// Executes an action corresponding to an undo /// </summary> void Undo(); /// <summary> /// Executes an action corresponding to redo in case it has been undone. /// </summary> void Redo(); /// <summary> /// Title of the command. /// </summary> /// <remarks>Typically to be used in undo list or undo stack description.</remarks> string Title { get; set; } } Assume you are adding a shape to the canvas and this action should be undone on demand. The action command should be common for all your elements based on UIElement and know the surface it is operating. In my case extended canvas will be the command surface. This is the most common information needed the first simple command. I've implemented some additional helper methods for the canvas and pass my common interface over the commands. public interface IDrawingSurface: IInputElement { /// <summary> /// Get/Set the value indicating whether elements dragging is allowed for the canvas. /// </summary> bool IsDragEnabled { get; set; } /// <summary> /// Add generic element to the surface /// </summary> /// <typeparam name="T">UIElement type</typeparam> /// <param name="element">Element to be added to the surface</param> void AddElement<T>(T element); /// <summary> /// Insert generic element to the surface at a specified index position. /// </summary> /// <typeparam name="T">UIElement type</typeparam> /// <param name="index">The position of element to be placed</param> /// <param name="element">Element to be inserted to the surface</param> void InsertElement<T>(int index, T element); /// <summary> /// Remove generic element from the surface /// </summary> /// <typeparam name="T">UIElement type</typeparam> /// <param name="element">Element to be removed from the surface</param> void RemoveElement<T>(T element); }
Using generics in this case help me very much eliminating the type casting and the stuff like that. As canvas deals with UIElement based elements it becomes rather difficult maintaining the collection because I definitely sure you won't deal with UIElement objects all over you application. It becomes more convenient to wrap the basic child processing logic into some sort of generic helpers. The simple pieces of code for the canvas exposing such an interface can be as following /// <summary> /// Add generic element to the surface /// </summary> /// <typeparam name="T">UIElement type</typeparam> /// <param name="element">Element to be added to the surface</param> public void AddElement<T>(T element) { UIElement uiElement = element as UIElement; if (uiElement != null && !Children.Contains(uiElement)) Children.Add(uiElement); }
The remove or insert methods are too obvious to be mentioned here. You can implement you own subset of functionality extending the canvas using your own demands. As I've mentioned earlier the simple add element command that can satisfy the undo/redo service should have the possibility of adding some kind element to the drawing surface (command execution), remove the element from the surface (undo operation) and rollback the undo operation so add the element again to the surface (redo operation). public class AddElementCommand : IDiagramCommand { public const string DefaultCommandTitle = "Add Element"; #region Properties /// <summary> /// Gets or sets the surface. /// </summary> /// <value>The surface.</value> public IDrawingSurface Surface { get; set; } /// <summary> /// Gets or sets the element. /// </summary> /// <value>The element.</value> public UIElement Element { get; set; } /// <summary> /// Title of the command. /// </summary> /// <value></value> /// <remarks>Typically to be used in undo list or undo stack description.</remarks> public string Title { get; set; } #endregion #region ctor /// <summary> /// Initializes a new instance of the <see cref="AddElementCommand"/> class. /// </summary> /// <param name="surface">The surface.</param> /// <param name="element">The element.</param> public AddElementCommand(IDrawingSurface surface, UIElement element) : this(surface, element, null) { } /// <summary> /// Initializes a new instance of the <see cref="AddElementCommand"/> class. /// </summary> /// <param name="surface">The surface.</param> /// <param name="element">The element.</param> /// <param name="title">The title.</param> public AddElementCommand(IDrawingSurface surface, UIElement element, string title) { if (surface == null) throw new ArgumentNullException("surface"); if (element == null) throw new ArgumentNullException("element"); Surface = surface; Element = element; Title = (!string.IsNullOrEmpty(title)) ? title : DefaultCommandTitle; } #endregion #region IDiagramCommand Members /// <summary> /// Executes the actual action /// </summary> public void Execute() { Surface.AddElement(Element); } /// <summary> /// Executes an action corresponding to an undo /// </summary> public void Undo() { Surface.RemoveElement(Element); } /// <summary> /// Executes an action corresponding to redo in case it has been undone. /// </summary> public void Redo() { Surface.AddElement(Element); } #endregion } The command itself doesn't create the UI element because it is not responsible for that. It even doesn't distinguish your objects being passed. It only assumes the objects belong to UIElement type and that's all. As far as you can understand the undo/redo service is also blind to the content of the commands it deals with. It will process the IDiagramCommand objects and will attach it's undo/redo functionality to each command. Here's the what could be the simple interface for basic undo/redo service public interface IUndoService { /// <summary> /// Get the undo commands history. /// </summary> Stack<IDiagramCommand> UndoCommands { get; } /// <summary> /// Get the redo commands history. /// </summary> Stack<IDiagramCommand> RedoCommands { get; } /// <summary> /// Get the undo commands titles. /// </summary> ObservableCollection<string> UndoTitles { get; } /// <summary> /// Get the redo commands titles. /// </summary> ObservableCollection<string> RedoTitles { get; } /// <summary> /// Gets a value indicating whether there is anything that can be undone. /// </summary> bool CanUndo { get; } /// <summary> /// Gets a value indicating whether there is anything that can be rolled forward. /// </summary> bool CanRedo { get; } /// <summary> /// This method puts the command to the Undo stack and then executes it. /// </summary> /// <param name="command">The command to be executed.</param> void Execute(IDiagramCommand command); /// <summary> /// Rollback the last command. /// </summary> void Undo(); /// <summary> /// Rollback the last undone command. /// </summary> void Redo(); /// <summary> /// Clear the undo history. /// </summary> void ClearUndoHistory(); /// <summary> /// Clear the redo history. /// </summary> void ClearRedoHistory(); /// <summary> /// Clear all the undo and redo history. /// </summary> void ClearHistory(); } Hope the code is self explaining. We introduce two stacks for commands according to undo and redo implementation. Also we have two observable collections giving the titles. Also we provide the obvious Execute/Undo/Redo methods alongside with a set of helper methods and properties as clearing the history or returning a boolean value determining whether this or that functionality as possible at execution time. Here's the Undo/Redo service that expose the interface mentioned above public class UndoService : IUndoService { #region ctor public UndoService() { UndoCommands = new Stack<IDiagramCommand>(); UndoTitles = new ObservableCollection<string>(); RedoCommands = new Stack<IDiagramCommand>(); RedoTitles = new ObservableCollection<string>(); } #endregion #region Routed events bindng support public void OnExecuteUndo(object sender, ExecutedRoutedEventArgs e) { Undo(); } public void OnCanExecuteUndo(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = CanUndo; } public void OnExecuteRedo(object sender, ExecutedRoutedEventArgs e) { Redo(); } public void OnCanExecuteRedo(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = CanRedo; } #endregion #region IUndoService Members /// <summary> /// Get the undo commands history. /// </summary> public Stack<IDiagramCommand> UndoCommands { get; protected set; } /// <summary> /// Get the redo commands history. /// </summary> public Stack<IDiagramCommand> RedoCommands { get; protected set; } /// <summary> /// Get the undo commands titles. /// </summary> public ObservableCollection<string> UndoTitles { get; protected set; } /// <summary> /// Get the redo commands titles. /// </summary> public ObservableCollection<string> RedoTitles { get; protected set; } /// <summary> /// Gets a value indicating whether there is anything that can be undone. /// </summary> public bool CanUndo { get { return UndoCommands.Count > 0; } } /// <summary> /// Gets a value indicating whether there is anything that can be rolled forward. /// </summary> public bool CanRedo { get { return RedoCommands.Count > 0; } } /// <summary> /// This method puts the command to the Undo stack and then executes it. /// </summary> /// <param name="command">The command to be executed.</param> public void Execute(IDiagramCommand command) { if (command == null) return; // Execute command command.Execute(); // Push command to undo history if (command is IDiagramCommand) { UndoCommands.Push(command); UndoTitles.Insert(0, command.Title); // Clear the redo history upon adding new undo entry. This is a typical logic for most applications RedoCommands.Clear(); RedoTitles.Clear(); } } /// <summary> /// Rollback the last command. /// </summary> public void Undo() { if (CanUndo) { IDiagramCommand command = UndoCommands.Pop(); command.Undo(); UndoTitles.RemoveAt(0); RedoCommands.Push(command); RedoTitles.Insert(0, command.Title); } } /// <summary> /// Rollback the last undone command. /// </summary> public void Redo() { if (CanRedo) { IDiagramCommand command = RedoCommands.Pop(); RedoTitles.RemoveAt(0); //Execute(command); if (command != null) { command.Execute(); if (command is IDiagramCommand) { UndoCommands.Push(command); UndoTitles.Insert(0, command.Title); } } } } /// <summary> /// Clear the undo history. /// </summary> public void ClearUndoHistory() { UndoCommands.Clear(); UndoTitles.Clear(); } /// <summary> /// Clear the redo history. /// </summary> public void ClearRedoHistory() { RedoCommands.Clear(); RedoTitles.Clear(); } /// <summary> /// Clear all the undo and redo history. /// </summary> public void ClearHistory() { ClearRedoHistory(); ClearUndoHistory(); } #endregion } The code above mostly doesn't require long supplementary information. On each command execution the redo stack is cleared to reflect the most common behavior in all applications. The undo and redo methods mainly do the manipulations with theirs stacks and corresponding title collections. The main entry point for the undo service is Execute method. It executes the command and adds it to the undo stack (great thanks to François for that idea) The only part that can be a point of detailed interest is the region "Routed events binding support". These four methods provide an easy and convenient possibility of binding the undo/redo commands to the UI. Usually you'll have two buttons and maybe each button will contain the list of operation titles for undoing of redoing. In my sample projects I usually implement two buttons bound to the standard application commands. <Button Margin="0,3" Name="cmdUndo" Command="ApplicationCommands.Undo"> <StackPanel> <Image Source="Images/undo.png" Width="32" Height="32"/> <TextBlock>Undo</TextBlock> </StackPanel> </Button> <Button Margin="0,3" Name="cmdRedo" Command="ApplicationCommands.Redo"> <StackPanel> <Image Source="Images/redo.png" Width="32" Height="32"/> <TextBlock>Redo</TextBlock> </StackPanel> </Button> Take a look at the "Command" property of each button. "ApplicationCommands" is a standard class so if interested you look through MSDN to get more information and use cases. Upon the window loading I attach my undo/redo logic to the list of command bindings for the window so that buttons accessing "ApplicationCommands.Undo" and "ApplicationCommands.Redo" automatically go to my UndoService. Something like the following #region Configure command bindings UndoService undoService = designer.GetService<IUndoService>() as UndoService; if (undoService != null) { this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo, undoService.OnExecuteUndo, undoService.OnCanExecuteUndo)); this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo, undoService.OnExecuteRedo, undoService.OnCanExecuteRedo)); } #endregion OnCanExecuteUndo and OnCanExecuteRedo methods are returning the CanUndo and CanRedo properties of the underlying Undo/Redo service. The last two mentioned are bound to the corresponding stacks and return true if the stack contains any command. This gives you a very interesting feature. Your buttons will themselves control their state and will become enabled/disabled according to the contents of undo/redo stacks. This is a very basic pattern to get the idea of implementing undo/redo functionality and commands. It's up to you what commands will be executed and what will they do. The undo/redo service is usually implemented in the way it doesn't take care of that. It deals with interface part of commands and that's all. Thanks for your attention. The sources can be found at my skydrive under "Diagramming" folder or in the previous article. November 13 WPF Diagramming. Simple diagramming CTP project for process designing.While preparing material and samples for my next 5 or 6 articles towards the diagramming I've come to conclusion that it would be perfect to implement some kind of generic UI for testing everything connected to diagramming. I've spent the weekend preparing the draft project that could be close to my vision and needs. Again I must mention the Unfold project that inspired me very much. This is still a best free implementation and starter kit that can be found all over the Internet for present moment and the sources are worth looking at. I really appreciate the work that was done to perform it. When I started developing my own vision of diagramming approach I promised myself 2 things to remember: not to use the Unfold sources and not to mix my actual job sources and approaches so keeping solution in a "home project" mode as long as possible. As I'm reading and reviewing the Unfold sources almost every day to get some new ideas how this or that part could be done in the way I would prefer some parts may seem familiar to you, for example IMouseListener, IShapeCreator, Undo/Redo functionality. This was done from the scratch because I hate copy-pasting approaches and I've left the names and some pieces for convenience purposes. Later all the parts will be definitely changed to expose the architecture I'm implementing actually. Here's what you can download and play with I made a strict decision for using a service-oriented approach exposing IServiceProvider for my further samples towards diagramming. This give me everything I need for the further framework extensions and the most easiest ways of maintaining the blocks. I won't dwell on the blocks I've used for this project in this article as I'm going to give more details explanations in the later posts. The project is a very very rough and draft implementation. If you become interested in it I advice you to stay on the line with the blog I'll definitely provide a lot of new information towards diagramming ;) Any feedback and questions are appreciated so don't hesitate The key stuff I concentrated on: 1. Selection Service Very simple service for shape selection on the drawing surface providing a basic circle adorner. Each selectable shape can contain own adorners and expose Select/Unselect calls managed by the service. I'm going to provide additional sample for selection service that can replace existing one and that implements it's own way of selection without shapes. 2. Undo/Redo Service Basic implementation of undo/redo functionality. Now supports only shape addition to canvas and deletion. It still requires a lot of refactoring that I'm going to finish it as soon as possible. 3. Tool Service I tried to turn everything into tools. The connectors are not yet finished but the idea is the same. Tool service fully maintain the tools and later I'm going to move them to external assemblies to provide the possibilities of separate development and loading. The same is for shapes. 4. Sample connector tools For this project I've provided three sample connectors: plain line, arrow line and bezier line (taken from Microsoft VPL designer). I liked the idea of shape creators implemented in Unfold project so I've left the same naming for now but guess I would better call them "Materials" at later implementations. Each tool can have different materials (this can be seen from the tool registration). Also there's some simple binding converters for automatic line positioning according to drag operations. 5. Main design host Main abstraction layer for the designer that is used for hosting and maintaining all the services registered.
And a lot of other interesting blocks that can be easily changed or extended. At least I try to keep this way. Still continue to work hard on the project... November 03 Getting Aero theme with WPF on Windows XPFor those guys who like Aero very much but cannot migrate their projects at a moment there's quite a good news :) You don't have to buy third party controls styled like Aero or Luna or Royale theme today because there's some dirty tricks to enable that stuff in your application. When you install .net 3.0 framework you automatically get some candies into your GAC that might help you styling your applications. Assuming you have the most common system path try to navigate the "C:\Windows\assembly\GAC_MSIL" folder. There you can find several folders starting from "PresentationFramework" plus the name of the theme. At my laptop I now see following: PresentationFramework.Aero PresentationFramework.Classic PresentationFramework.Luna PresentationFramework.Royale Let's take Aero folder... :) Inside you can find the following folder 3.0.0.0__31bf3856ad364e35 (Note that the name of the folder can be different from mentioned) It contains the assembly called "PresentationFramework.Aero.dll" Fine, that's enough information for us to bind the theme to our application. How should we do that? Open your "App.xaml" and move to the resources section. All you need to do is to add one single line defining the resource dictionary like in the sample below <Application x:Class="Vista.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources> <ResourceDictionary Source="/PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=MSIL; component/themes/aero.normalcolor.xaml" /> </Application.Resources> </Application> So what do we set here... PresentationFramework.Aero is the source for resources. Version is taken by you from the folder "3.0.0.0__31bf3856ad364e35" in my case it is "3.0.0.0". Culture is neutral. Public key token is also taken from the folder "3.0.0.0__31bf3856ad364e35" and has the value "31bf3856ad364e35". The rest should be typed as it is given in the sample. Now place some controls on the canvas and you can immediately see the changes in the Visual Studio WPF designer (I used VS 2008 TS beta 2 for preparing the sample). This is also a perfect feature of Visual Studio :). Of course you won't get the glass effect for the whole window, but at least all your controls are styled well. I've prepared one small screenshot for you to get the idea. And don't forget that you are running Windows XP ;) Guess I must leave you playing with this stuff for yourselves. There are also other themes mentioned so you may play with them a bit. Have a nice coding. November 01 WPF Diagramming. The WPF way of binding line endpoints to shapes.In the previous articles of this series I was presenting some "Windows Forms" idea of drawing line connectors and updating their endpoints according to the positions of shapes being dragged. We've handled the "DragDelta" event for each Thumb based shape moved the shape and processed all the corresponding starting and ending lines (their positions). Any simple connector can be represented as some kind of "Data Model - Presentation View" structure. You have the geometry with UI for graphical presentation and have some data assigned to it to handle the underlying business objects and other stuff. So it is obvious that you might want to separate the drawing and business logic. When I've learned a bit of new data binding features I've started to dig to find out whether MS guys at last gave us some new property binding sugar. Yesterday evening I was again surfing my Visual Studio MSDN library and came across the MultiBinding class . I've started testing that stuff and after getting the idea of the power provided by multiple dependency properties binding I've started implementing that immediately to my diagramming sample. Today in the morning I've found some similar solutions with Google but the sample is already prepared and I've started writing the article... :) I assume you already know how the binding is performed with xaml markup and reviewed some samples towards this. MS guys give you the possibility of binding one dependency property simultaneously to several other ones. This means that you can get rid of all "always the same" calculations moving the code somewhere outside. Trying to connect one property to multiple other ones you nevertheless need to get one value at the end and assign it to your line endpoint. That's why you need some converter to resolve the incoming values and return processed result to be applied to the property at the other end. In our case we'll try to connect each endpoint property of the line to four properties of the corresponding shape. Again we have to get "Canvas.Left", "Canvas.Top", "ActualWidth" and "ActualHeight" to center the line endpoint properly. Fist we do is implementing the appropriate binding converter to get all this values and return one Point for the center of the shape. ConnectorBindingConverter.cs using System; using System.Globalization; using System.Windows; using System.Windows.Data; namespace HomeDiagramming.Connectors.Converters { public class ConnectorBindingConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { double left = System.Convert.ToDouble(values[0]); double top = System.Convert.ToDouble(values[1]); double actualWidth = System.Convert.ToDouble(values[2]); double actualHeight = System.Convert.ToDouble(values[3]); return new Point(left + actualWidth / 2, top + actualHeight / 2); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #endregion } } The code doesn't require commenting because the logic is clear I think. Converter receives an array of collected values and returns a point based on some calculations performed according to them. Next we implement a helper method for generating a MultiBinding collection according to the shape provided private MultiBinding CreateConnectorBinding(IConnectable connectable) { // Create a multibinding collection and assign an appropriate converter to it MultiBinding multiBinding = new MultiBinding(); multiBinding.Converter = new ConnectorBindingConverter(); // Create binging #1 to IConnectable to handle Left Binding binding = new Binding(); binding.Source = connectable; binding.Path = new PropertyPath(Canvas.LeftProperty); multiBinding.Bindings.Add(binding); // Create binging #2 to IConnectable to handle Top binding = new Binding(); binding.Source = connectable; binding.Path = new PropertyPath(Canvas.TopProperty); multiBinding.Bindings.Add(binding); // Create binging #3 to IConnectable to handle ActualWidth binding = new Binding(); binding.Source = connectable; binding.Path = new PropertyPath(FrameworkElement.ActualWidthProperty); multiBinding.Bindings.Add(binding); // Create binging #4 to IConnectable to handle ActualHeight binding = new Binding(); binding.Source = connectable; binding.Path = new PropertyPath(FrameworkElement.ActualHeightProperty); multiBinding.Bindings.Add(binding); return multiBinding; } Here IConnectable is my custom interface all the shapes are expose. Check out the source code for the article to get a better understanding of it. Now we need another helper method that will give us the opportunity of connecting to IConnectable objects and so binding two endpoints of the line to the provided shapes. The method is very simple in this case public void AddConnection(IConnectable source, IConnectable target) { ShapeConnectorBase conn = new ShapeConnectorBase(); conn.SetBinding(ShapeConnectorBase.StartPointProperty, CreateConnectorBinding(source)); conn.SetBinding(ShapeConnectorBase.EndPointProperty, CreateConnectorBinding(target)); this.DiagramView.Children.Insert(0, conn); }
Here's the complete source for ShapeConnectorBase.cs using System.Windows; using System.Windows.Media; using System.Windows.Shapes; namespace HomeDiagramming.Connectors { public class ShapeConnectorBase : Shape, IShapeConnector { LineGeometry linegeo; public static readonly DependencyProperty StartPointProperty = DependencyProperty.Register("StartPoint", typeof(Point), typeof(ShapeConnectorBase), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsMeasure)); public static readonly DependencyProperty EndPointProperty = DependencyProperty.Register("EndPoint", typeof(Point), typeof(ShapeConnectorBase), new FrameworkPropertyMetadata(new Point(0, 0), FrameworkPropertyMetadataOptions.AffectsMeasure)); public Point StartPoint { get { return (Point)GetValue(StartPointProperty); } set { SetValue(StartPointProperty, value); } } public Point EndPoint { get { return (Point)GetValue(EndPointProperty); } set { SetValue(EndPointProperty, value); } } public ShapeConnectorBase() { linegeo = new LineGeometry(); this.Stroke = Brushes.Black; this.StrokeThickness = 1; } protected override Geometry DefiningGeometry { get { linegeo.StartPoint = StartPoint; linegeo.EndPoint = EndPoint; return linegeo; } } } } Finally at your main code you can do something like the following to link two shapes together // Setup connections for predefined thumbs
designer.AddConnection(myThumb1, myThumb2);
designer.AddConnection(myThumb2, myThumb3);
designer.AddConnection(myThumb3, myThumb4);
designer.AddConnection(myThumb4, myThumb1);
You have no need of handling the line position on the shape movement as it will be handled automatically by the binding object. So from now you may concentrate on business logic better and not on UI part and geometry repositioning. Have a good coding... Note: You may find a lot of other interesting things in the source code provided for the article. As I'm preparing to present some really complex samples in the nearest future I just don't have time to clear the solution ;) Again building the sample will require you to have Visual Studio 2005 beta 2. October 28 WPF Diagramming. Lines pointing at the center of the element. Calculating angles for render transforms.All my previous posts from "WPF Diagramming" series were presenting simple lines connected to the center of each shape object. For the first steps it is very convenient not to care about complex maths and just bind endpoints of the line to the centers of objects being connected. The rest will be triggered by WPF. One day you will decide to implement the arrowheads for your connectors because it's a mandatory requirement for any diagramming project. But how can you draw the arrowhead for the line that is pointing to the center of the shape? You won't see that arrowhead in any way because your shape will overlap your connector endpoint. There are at least three ways of implementing this requirement to your project. Changing layers You simply change the ZIndex of your geometry to bring the connectors one level upper than your shapes. This will make the arrowheads to be seen but this also make the whole diagramming layout so ugly that I no more dwell on this approach. Sticking to shape border Guess this can be regarded as the most common approach when developing diagramming tools or controls as I've looked though a variety of implementations. You always know the size of the shape at runtime so you always can get all the border sizes and positions of it. When the connection between two shapes is being established the start point of the line is bound to the bottom border of source shape and the end point is simply bound to the upper border of the target shape. Upon performing a drag operations for both shape objects the line coordinates are recalculated to apply the scheme described. Of course this also influence the layers ordering because the connectors are to be placed one layer upper than the shapes are. As for me I don't like this approach at all. I've spent several days to perform the third approach that is more close to what I wanted for my article and for my home project. Pointing at the center of the element This means that whenever you drag your target shape source shape connector will always point to the center of it. It's up to the layout design where to place the start point of the line (shape center or some border) but the end point with arrowhead will point at the center of the target shape thus not touching the shape visually. This is what I'm going to describe later on.
When the line is pointing at the center of some object it may seem visually, that all you need is to bind line endpoint to the center of the shape and then make it shorter a bit. But when you try to turn your "shortened" line into coordinates you encounter one simple question "What will be the endpoint coordinates and how I can calculate them dynamically?". Upon Googling you then find some answers than make you feel rather unhappy and willing to use the "Sticking to shape border" approach I mentioned before :) The answer is that you have to use angles calculation, trigonometry, vector algebra, geometry and a lot of stuff do understand. I've used Google heavily for a last week to get small and easy solution to be implement to my project and not to write this article at all :) There were some interesting pieces of information on DirectX and XNA forums that were nearly close to what I wanted but not exactly the perfect and easy ones. By that time I've already understood what I needed to implement. Some frightening theory... First you have to get the angle between you source shape and the target shape. Then you imagine and abstract circle that is drawn around your source shape. After that you get the point on the circle by the angle known already by that time. This point will be exactly the required coordinate of your line endpoint. As I've mentioned at the beginning of the article it took me several days to get the smallest code for the angles calculations possible for performing the process. I could simply give you the sample code to use but when I remembered my own feeling when looking on dozens of code implementations without any supplementary info I decided to spend some more time preparing the drawings and description of the whole process. This can be extremely useful when you'll decide to customize the process or adopt it to your own needs. For the sample we will be using a simple button element placed somewhere on the form. According to mouse position it will be rotated to face the cursor. Also one line will be drawn from the cursor position with arrowhead pointing to the center of the button.
Getting angle between two vectors From the Vector Algebra books you can get all the required information on how the vectors are used and how the angles between them are calculated. Microsoft did a really huge piece of work towards this. Presentation Framework (WindowsBase assembly to be exact) contains all vector procedures you need to feel happy implementing this stuff. All you need is two vectors to be compared. You have vector a (0,100) and vector b (100, 100). Using the static method Vector.AngleBetween(b, a) you will get the exact angle between them in degrees. In our case vector a is based on our button location and vector be is created using the current mouse position on the canvas.
Turn element positions into vectors Of course to get the angle between two vectors we need to have some vectors to be calculated. In a real life we have only the coordinates of the button element and can freely get the position of mouse cursor according to the canvas at runtime. Mouse can be placed anywhere like the mouse, so we don't have much to do with zero coordinates and the image above. How this all can be converted to vectors? Assume you have a button positioned with it's center to point (100, 150). Just to note somewhere: we can get the center of element on the canvas by the following template Point p1 = new Point( Canvas.GetLeft(button1) + button1.ActualWidth / 2, Canvas.GetTop(button1) + button1.ActualHeight / 2); So let's imagine again that our button center is at (100, 150) and the mouse cursor position (think of dragging the shape in this case or simply some another static shape position) is actually at point (200, 150). These two points are quite enough for us to get the rectangle covering our working area. We are taking the center of the button as a starting point of our coordinate system. From the picture given you can easily get the idea. You get the line a X(100, 150) - Y(100, 50) by the button element "Left" position and mouse "Top" position. You get the line b X(100, 150) - Y(200, 150) by center of the button and the mouse cursor position. So we have to lines looking like the vectors we exactly need from the first picture and we know the working area bounds as rectangle X1(100, 150) - Y1(100, 50) - X2(200, 50) - Y2(200, 150). Again to keep simplicity we have a 45 degree angle between them and both lines are of 100 length. To get the two vectors needed we simply get the differences between left and right side coordinates of our rectangle by Y axis. Next issue we come across is that Microsoft uses left top orientation of coordinates while we require left bottom for your calculations. So additionally we have to reverse our rectangle positions upside down to meet the requirement. Finally as we want our button center to be the starting point for our coordinate system we have to align the rectangle to zero point removing the left button canvas offset from all the four points according to X axis. So what we have after all is Vector A = (100, 150) - (100, 50) by Y axis = (100, 100) Vector B = (200, 150) - (200, 50) by Y axis = (200, 100) and aligned to zero based positioning by 100 Vector A = (0, 100) Vector B = (100, 100) Looks like we have got our first picture describing vectors angle calculation ;) Next issue you will get when you move the mouse to another three quarters of coordinate system drawn in the picture. According to the approach described you will have to deal with negative coordinates and so have something like on the picture Your internal coordinate system seems to be divided into 4 parts each receiving 0 to 90 or 0 to -90 degrees (see the picture). Additionally when you will move the mouse to the X or Y axis you will get the 0 degrees instead of 0, 90, 180 and 360 degrees. What is happening? Well this is quite expected behavior as upon mouse cursor moving to to negative coordinates our rectangle and so the line a endpoint goes to negative position either line b. To fix the behavior we need functionality similar to common clock. Line a should always be static: being positive and laying on the same place according to Y axis. Math.Abs() method will help you much in doing the calculations ;) I intended to write the smallest code possible for the calculations needed. The quickest approach I guess is splitting our coordinate system into two parts. Our 360 degrees rotation is simply performed by doing 180 and -180 degrees. There's no visual difference but code behind checks become extremely small. So placing line a statically according to Y axis gives us the opportunity of getting +/- 180 degrees. The only issue will arise is 90 degrees positioning. As two Y points of our line b and so vector lay on the same axis their subtraction will give you a zero value and the further angles calculation will give you 0 degrees respectively. We have to provide additional check for this case and restrict subtraction.
Calculating angle using C# Here's the source code for getting the angle between button on the canvas and mouse pointer. I've optimized the rectangle points usage and vector creation additionally. Also I've implemented the simple Y axis check. Point p1 = new Point( And that's all :) so much talking and so small to do. According to two points you have the angle in degrees between button1 element and mouse pointer position. You can use this code freely anywhere you need getting angles between two elements on the canvas :) To rotate the button element to face the mouse pointer you can use the following sample button1.RenderTransformOrigin = new Point(0.5, 0.5); button1.RenderTransform = new RotateTransform(angle);
Positioning the line to point at the center of the element As we have already calculated the angle between the button element and mouse pointer we can get the right position the line with arrowhead should be bound to. First we must have an abstract circle around which our arrowhead will be moving in case of angle changed. The radius of circle should be large enough not to allow the arrowhead touch the element at any angle. This is up to you what values to provide, I've simply used the button's "radiuses" according to X and Y axis double radius = button1.ActualWidth/2 + button1.ActualHeight / 2;
according to the school course of geometry we know already how to find a point on a circle knowing the radius and angle double x = radius * Math.Cos(angle); double y = radius * Math.Sin(angle); But here we also come across two issues. First is that .net Math.Cos and Math.Sin get the values in radians though Vector.AngleBetween returns a value in degrees. The second is that this well known formula for point evaluation on circle does calculations in counter-clockwise mode when we require exactly the clockwise result. To fix these two issues we transform the angle to the radians and invert the calculation formula to clockwise mode: double x = radius * Math.Sin(angle * Math.PI / 180); double y = radius * Math.Cos(angle * Math.PI / 180); Note the inverted usage of Sin and Cos according to the traditional usage. Also you can increase the calculation performance by predefining the value of (PI/180) but this is up to you ;) That's all. You can use this coordinates to assign the value of the line endpoint with arrowhead.
Getting all together Notes: For the line with arrowhead I used the sample by Charles Petzold. For a detailed information you really should refer to his own blog. Window1.xaml <Window x:Class="Rotation.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" Loaded="Window_Loaded"> <Canvas Name="myCanvas"> <Button Canvas.Left="100" Canvas.Top="150" Height="20" Name="button1" Width="40">Button</Button> <CheckBox Canvas.Left="10" Canvas.Top="10" Panel.ZIndex="1" Height="15.554" Name="chButtonRotate" Width="119.988">Rotate button</CheckBox> </Canvas> </Window> Window1.xaml.cs using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using Petzold.Media2D; namespace Rotation { public partial class Window1 : Window { public Window1() { InitializeComponent(); } ArrowLine directionLine; private void Window_Loaded(object sender, RoutedEventArgs e) { this.MouseMove += new MouseEventHandler(Window1_MouseMove); button1.RenderTransformOrigin = new Point(0.5, 0.5); directionLine = new ArrowLine(); directionLine.Stroke = Brushes.Red; directionLine.StrokeThickness = 2; myCanvas.Children.Add(directionLine); button1.RenderTransformOrigin = new Point(0.5, 0.5); } void Window1_MouseMove(object sender, MouseEventArgs e) { Point p1 = new Point( Canvas.GetLeft(button1) + button1.ActualWidth / 2, Canvas.GetTop(button1) + button1.ActualHeight / 2); Point p2 = e.GetPosition(this); Vector a = new Vector(0, Math.Abs((p1.Y != p2.Y) ? p1.Y - p2.Y : p2.Y)); Vector b = new Vector(p2.X - p1.X, p1.Y - p2.Y); double angle = Vector.AngleBetween(b, a); this.Title = angle.ToString(); if (chButtonRotate.IsChecked == true) button1.RenderTransformOrigin = new Point(0.5, 0.5); button1.RenderTransform = new RotateTransform(angle); double radius = button1.ActualWidth/2 + button1.ActualHeight / 2; double x = radius * Math.Sin(angle * Math.PI / 180); double y = radius * Math.Cos(angle * Math.PI / 180); Point touchPoint = new Point(p1.X + x, p1.Y - y); directionLine.X1 = p2.X; directionLine.Y1 = p2.Y; directionLine.X2 = touchPoint.X; directionLine.Y2 = touchPoint.Y; } } } What you get on the screen |
|
|