- Build Enterprise Information Systems
- Complexity shows up almost immediately–get the tools to manage it
- Different types of bugs
- Enterprise architect information
- Fashion Gist and Modernity
- GistMDriven
- Main concepts definition
- MDriven Turnkey Architecture
- Model Driven
- Philosophy–where lies the value
- Reality and the theoretical best model
- Database OR Mapping
- User interface technologies
- Barcode - on Android - with Xamarin and MDriven
- Catching more information in your model
- Creating CustomControl that Shows Data in a Gantt Chart
- Custom controls in ViewModel aided Views
- Delayed Fetch
- Derivation expressions
- Derived settable attributes
- Diagrams in Diagrams
- Ecomdl
- Enumerations
- Enumerations in Modlr
- Exception starting
- GuardConstraints
- Making sense of legacy data–DB Reverse
- Material Design Icons
- MDriven Designer and Modlr extensions–exporting data
- MDriven Designer–a new Rename function to save you time
- MDriven In Xamarin
- MDrivenDesignerPropertyInspectorHints
- NugetProject
- Object Identity
- PersistenceMapperWEBAPIClient
- Plugins in Modlr
- PopUp action
- PreserveLegacyFilenames
- Property inspector
- Simulate login
- Styling WPF Applications and ViewModels
- Text formatting
- UI First–or information first
- UIOverride
- Updates to AutoForm and Debugger after latest feedback
- Using WECPOF in runtime
- WECPOF
- WECPOF Goodies
- WPF Debugger
- Xmi
- Angular Ext Component
- autocomplete
- autofocus
- BlobDownloadLink
- ClientSortable
- DataIsHtml
- DataIsImageUrl
- DataIsLink
- BlobType
- FixColSize
- FormatAttr
- HideHeading
- MaxFetch
- MaxFileSizeInBytes
- Placeholder
- Resizable
- SeekerSortable
- SkipStyleLogic
- StringFormat
- StringFormatAngular
- StringFormatRazor
- Switch
- Autofocus
- BlobDownloadLink
- Texttype
- MaxFetch
- A few words on linq
- Acting on object changes
- Case sensitive or not
- Char to int and back
- ClassFromString
- Collection of strings
- Comparing strings
- Convert string to bytes
- Creating numeric types
- Custom types and custom operations in OCL
- Deepclone
- Default String Representation and asString
- Encoding
- Escape codes
- Examples on collection operators
- Methods
- Number conversions
- Ocl hex to dec
- Regular expressions
- Set vs bag
- Split
- Split time string to value
- State value as a string
- Understanding OCL with reference to SQL
- Unique constraints on 1 to 1 links
- VerbalizePatterns
- Api documentation
- Code generation
- Codegen Issues .net standard
- Constructor in generated code
- Debug CodeDress
- DisplayQueue
- Evolve db with code
- Unity 3D and MDriven
- Using different WCF Bindings with Enterprise Core Objects – ECO – MDriven framework
- ViewInView
- WinForms MDrivenFramework
- Working with legacy Handles
- AppWideAngularScriptIncludes
- AssetsTK
- Calling your own c - sharp .net things from Turnkey–server side
- Connecting javascript SinglePageApplications to Turnkey (SPA)
- DisplayWithVariables
- Geolocation
- Google Analytics in Turnkey app
- Google tag manager
- How to access your Turnkey site with FTP
- Javascript calling Turnkey rest
- Layout and CSS
- LiveUpdate
- MDriven turnkey Face - theme - styles - fashion
- MDriven Turnkey Series
- MDriven Turnkey theming
- Memory optimization
- Openid AzureAD
- OpenIdConnect access token and refresh token
- Overriding AngularJS MDriven Turnkey Views
- Pricing
- QR and barcode
- QR-Code to drive a workflow in any MDriven turnkey app
- Receive post data not known at design time
- Render data as html
- Render MVC ViewModel without turnkey
- Rendering the MDriven Turnkey application yourself
- Samples
- Scaling out Turnkey on Azure
- Serverside Turnkey and MVC functioning
- SiteAssets
- Size in Images in Turnkey
- Streaming Client
- SVG
- SysSingleton optional properties
- Theme and style
- Theme as data
- Time zones and sanity–post for future reference
- TinyMCE editor
- Tkusercss
- Turnkey App Current state: Paused
- Turnkey Client Timeout
- Turnkey debug
- Turnkey email settings
- Turnkey extra meta tags
- Turnkey local development tips
- Turnkey MVC Controllers
- Turnkey reload automatically on script change or style change
- Turnkey sample Board Map Balls Gantt
- Turnkey sample Empty start model
- Turnkey sample ProjectPlanner
- Turnkey sample ProjectPlanning
- Turnkey Styling
- TurnkeySettings
- TurnkeyUserLogin
- Use c-sharp code to post to TurnkeyRest
- VClientScreenWidth
- View/Page override
- ViewMeta
- Change password MDriven Server
- Lost password or locked out
- MDriven Server problem with evolve
- MDrivenServer, opening the built-in database
- No suitable constructor found
- SQL not trusted
- The anti-forgery token could not be decrypted
- Troubleshooting Turnkey deployment
- Duplicate IDs in the database
- CodeFoundButOutOfSync
- AppPool user
- AsyncTicket
- Automating deployment of Turnkey and MDrivenServer on IIS
- CacheInvalidation
- Communication between MDrivenServers
- Deploy Turnkey on your own Azure with OneDrive
- Emailing from an app using MDrivenServer
- Eventlog
- Fill in the email settings in the admin UI
- HardServerUrl
- Installation of MDriven Server on Windows 7 Enterprise
- LocalhostDevCert
- LocalServers
- MDrivenServer log
- MDrivenServer Slack web hook
- MDrivenServer with Windows authentication
- MDrivenServerOverride
- MultiTenantPrefix
- Purpose of History Server
- RecyclableMemoryStream
- Server Wide Variables
- Serverinfo
- Serverside actions
- Setting up a History slave
- Slave and History Server
- Slave server in action
- SysAsync package
- SysSingleton GetSystemUrl
- Turnkey and MDrivenServer logs
- Use SqlServer
- VistaDB
- WSL Windows subsystem for Linux
- Change tracking help SQL
- How your classes are stored in the SQL database when using MDriven
- Microsoft.data
- MySQL–notes to support the use of MySQL with MDriven
- Other PMappers
- Possible file version mismatch detected between ADO.NET Provider and native binaries of SQL Server Compact
- SQL Database
- SQL Server change tracking
- SQLImport multiple tables with associations
- SQLite
- Authenticate with a jwt
- BankId
- Certificate
- External login screen problem
- Fault-finding certificate problems in IIS
- GDPRPolicy
- HashPassword
- HeaderBasedAuthenticationTag
- Hide Password login
- High or low exposure to risk
- How does OpenIdConnect work
- Lets encrypt
- OpenID config
- Sign client rest request with certificate
- AccessExpression
- Analyze ViewModel classes and expressions
- Breaking changes
- Comboboxes with strings
- Cursored or Full Tree
- Databind labels in ViewModels
- Databound Placeholder text
- Declarative ViewModels and Taborder
- Explaining “The ViewModel does not require a root object” warning
- How Null is represented in your picklist
- Import xml and JSon with MDriven
- Index page
- Input Controls
- JsonToObject vs Tajson
- Multiple file upload component
- QueryPlan
- RestAllowed UIAllowed
- SelfVM
- SOAP
- SOAP the protocol from the stone age
- The combobox
- The VMClassId Explained
- VCurrent and vSelected
- ViewModel access and security
Custom Controls
This article is taking things further still with the ability to merge your own displays in the ViewModel-aided view including:
- How to mold data from your model into ViewModels using the ViewModel-designer in Modlr.
- How ViewModels lend themselves to act as the complete definition of a view if you dress them up with a few user interface hints (UI-Hints) – and how easy that is in the Modlr ViewModel designer.
- An effective way to get an administrative UI up and running in minutes instead of hours.
Consider this simple model:
Accompanied by this ViewModel:
Here we display a list of all flowers by name, and some details on the focused flower – the name and the number of leaves – but also a ViewModelColumn called “HowItLooks”.
The “HowItLooks” column has the flag Content-Override set. When this flag is set, the well-behaving ViewModelUserControl should allow the developer to inject their own display with a callback event or the like. At this point, you can associate a WPF UI control in design time.
☚ When the ViewModelColumn is selected, the property inspector looks like this.
The ContentOverride and ContentOverrideTypeDesignTimePath are shown whenever the ContentOverride is true.
The ContentOverride expects the Assembly without the dll extension, a semi-colon, and the namespace and type of the UserControl you want to display here. <AssemblynamewithoutDLLExtension>;<NameSpace>.<TypeOfExternalUIControl>.
The type you want to use must implement this interface:
namespace Eco.ViewModel.Runtime { public interface IExternalWECPOFUIComponent { // Summary: // Go like this: (vmc.SpawnedComponents["control"] as Grid).Children.Add(this); void InstallYourSelf(ViewModelColumn vmc, bool isDesignTime); string TheRequirementsToShowDeveloperInDesignTime(); } }
A Simple Sample
In WPF, it is fairly simple to draw something that looks like a flower:
void DrawingArea_LayoutUpdated(object sender, EventArgs e) { if (NumberOfLeafs != _drawnleaves) { DrawingArea.Children.Clear(); Point center = new Point(DrawingArea.ActualWidth / 2, DrawingArea.ActualHeight / 2); double leafdeg = 0; for (int i = 0; i < NumberOfLeafs; i++) { Ellipse oneleaf = new Ellipse(); oneleaf.Width = 10; oneleaf.Height = center.Y; oneleaf.RenderTransform = new RotateTransform(leafdeg, 5, 0); oneleaf.Stroke = new SolidColorBrush(Colors.Black); oneleaf.Fill = new SolidColorBrush(Colors.Yellow); Canvas.SetLeft(oneleaf, center.X-5); Canvas.SetTop(oneleaf, center.Y); DrawingArea.Children.Add(oneleaf); leafdeg = (i+1)* 360 / NumberOfLeafs; } Ellipse pistills=new Ellipse() { Width = 20, Height = 20, Fill = new SolidColorBrush(Colors.Black) }; DrawingArea.Children.Add(pistills); Canvas.SetLeft(pistills, center.X-10); Canvas.SetTop(pistills, center.Y-10); _drawnleaves = NumberOfLeafs; } }
The code above draws the flower simulation based on the value of a NumberOfLeafs property.
We want to bind this property to a data source – in WPF, that implies a DependencyProperty:
public int NumberOfLeafs { get { return (int)GetValue(NumberOfLeafsProperty); } set { SetValue(NumberOfLeafsProperty, value); } } // Using a DependencyProperty as the backing store for NumberOfLeafs. This enables animation, styling, binding, etc... public static readonly DependencyProperty NumberOfLeafsProperty = DependencyProperty.Register("NumberOfLeaf", typeof(int), typeof(UserControl1), new UIPropertyMetadata(NumberOfLeafsChanged)); private static void NumberOfLeafsChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { (source as UserControl1).DrawingArea.InvalidateVisual(); }
The code above handles all the details of how to turn data into the display. We want to bind our data from the ViewModel to the properties of this custom control. This is where the IExternalWECPOFUIComponent comes in. Keep in mind – we do not want our flower simulation to need to know too much, or rather anything about our model. After all, the flower simulation might be useful in many other places in this model and also in many other models, we will create in the future. If your flower simulation saves someone else some time, consider licensing the rights to use your logic for money.
This is what we need to do to make our flower simulation work in a ViewModel UI:
public void InstallYourSelf(Eco.ViewModel.Runtime.ViewModelColumn vmc,bool designTime) { // add yourself to the viewmodel spawnedartifacts (vmc.SpawnedArtifacts["control"] as Grid).Children.Add(this); // I want the background transparent (vmc.SpawnedArtifacts["control"] as Grid).Background = null; // Bind ViewModelColumn to the NumberOfLeafsProperty this.SetBinding(NumberOfLeafsProperty,
new Binding(vmc.Name) { Source = vmc.OwningDisplayClass.BindingSource }); } public string TheRequirementsToShowDeveloperInDesignTime() { // Explain in text what your control assumes about the data return "This control needs the column to be of type int"; }
Done – let us run:
Following the changes in the data, the flower simulation is updated.
A Slightly More Complex Sample
We have reviewed how a single property could be visualized now - several rare properties and even data structures like lists and nestings.
Consider a case where you have data in both directions in a grid – this is typically called a pivot. A pivot can be used in many scenarios – dates in the x-axis, parts in the y-axis, and price in the cells. I am sure you can think of multiple usages for such a component. This sample is driven from a real-life case where we needed a good way to edit data like this. Normal ViewModel UI grids have the column decided in design time and only the rows are made up of data.
It might not look like much, but it covers all the complexity of a pivot. A list of values (CellThing) that are mapped onto a grid of ColumnThings and RowThings. Can we display and edit this successfully? We can edit and display any pivot needs.
Creating a ViewModel with UI hints for the maintenance of this data could look like this:
It is handling 4 axes of information “PivotExample”, “ColumnThing”, ”RowThing” and “CellThings” – it further allows for in-place ComboBoxes to be used for associating the Cellthings to one ColumnThing and one RowThing.
Now when we have this simple UI to populate the data I would like to use a pivot view to see the data values plotted in a grid of row and column things:
- Add ViewModelColumn and set Content-Override to true
- Fill in the control we want to use
- Let Modlr know the path to the assembly so that Modlr can show the component already in design time:
When Modlr can load the control in design time, it renders it and displays usage hints when you point at it:
Remember the TheRequirementsToShowDeveloperInDesignTime method of the IExternalWECPOFUIComponent interface:
public string TheRequirementsToShowDeveloperInDesignTime() { return "<ViewModelClass> \r\n"+ " XAxis -> nesting to <ViewModelClass> Header\r\n"+ " YAxis -> nesting to <ViewModelClass> Header\r\n"+ " Values (THIS IS THE COLUMN THAT HAS UIOverride) -> nesting to <ViewModelClass> X:string,Y:string,Value\r\n"+ "\r\n"+ "Follow the pattern above to present data in pivot table"; }
These are the assumptions the control needs to make on our data to do its generic – one size fits all – display.
This is a very important concept: The control knows nothing about your model – it does however require certain things from your ViewModel. Since the ViewModel is a tool/concept to mold model data into a situation or perspective or view, this is what we do here. The text in the TheRequirementsToShowDeveloperInDesignTime method describes the limitations.
Run it:
You can even edit in the pivot. Try to add X and Y-axis objects.
Updates 201909: You can add HeaderKey columns in X-axis and Y-axis to get a better key to match on than the Header value alone.
The pivot is delivered along with Modlr installation: WPFViewModelExternalControls; WPFViewModelExternalControls.PivotDataGrid
Some pointers on using components like this:
public void InstallYourSelf(Eco.ViewModel.Runtime.ViewModelColumn vmc, bool isDesignTime) { (vmc.SpawnedArtifacts["control"] as Grid).Children.Add(this); _XAxis = vmc.ViewModelClass.ColumnFromName("XAxis"); _YAxis = vmc.ViewModelClass.ColumnFromName("YAxis"); _Values = vmc.ViewModelClass.ColumnFromName("Values"); this.SetBinding(BindableColumnsProperty, new Binding() { Source = _XAxis.DetailAssociation.BindingSource }); this.SetBinding(BindableRowsProperty, new Binding() { Source = _YAxis.DetailAssociation.BindingSource }); this.SetBinding(BindableValuesProperty, new Binding() { Source = _Values.DetailAssociation.BindingSource }); Binding binding2 = new Binding() { Source = _pivotCells }; DataGrid.SetBinding(DataGrid.ItemsSourceProperty, binding2); DataGrid.CanUserAddRows = false; DataGrid.AutoGenerateColumns = false; DataGrid.CellEditEnding += new EventHandler<DataGridCellEditEndingEventArgs>(DataGrid_CellEditEnding); DataGrid.BeginningEdit += new EventHandler<DataGridBeginningEditEventArgs>(DataGrid_BeginningEdit); DataGrid.SelectedCellsChanged += new SelectedCellsChangedEventHandler(DataGrid_SelectedCellsChanged); DataGrid.SelectionUnit = DataGridSelectionUnit.Cell;
Another important piece of code fixes the feedback to WECPOF so that actions are highlighted etc:
void DataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) { IElement x,y,v; if (DataGrid.CurrentCell == null || DataGrid.CurrentCell.Column==null) { x=null; y=null; v=null; } else { x = _colDict[DataGrid.CurrentCell.Column]; y = (DataGrid.CurrentCell.Item as OneRow).Element; v=null; ICustomTypeDescriptor value; if ((DataGrid.CurrentCell.Item as OneRow).TryGetTypeDesc(DataGrid.CurrentCell.Column.Header as string, out value)) { v = (value as IElementProvider).Element; } } _XAxis.ViewModel.SetOclVariableValueFromName(_XAxis.ViewModelClass.NameOfCurrentVariable(), x); _YAxis.ViewModel.SetOclVariableValueFromName(_YAxis.ViewModelClass.NameOfCurrentVariable(), y); _Values.ViewModel.SetOclVariableValueFromName(_Values.ViewModelClass.NameOfCurrentVariable(), v); }
And to apply edit back into the data shown:
void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) { if (e.EditAction == DataGridEditAction.Commit) { string x = e.Column.Header as string; (e.Row.Item as OneRow).UpdateValue(x, (e.EditingElement as TextBox).Text); } }
Summing Up
Ultimately, we:
- Get a custom-built WPF UI into an otherwise strictly declarative model-driven solution. This will enable you to separate the concerns of display from the concerns of data handling. Surely this promise has been made by numerous techniques before – like Apple's OpenDoc, Microsoft's OLE, or standalone WPF for that matter.
- Allow for fully bound model-driven data structures to be used by WPF controls that in turn separate style from function.
At a glance, you might think this is a solution to the same problem that OLE/ActiveX solved back in the day, but besides being fully managed, this goes much further in allowing data into the component and truly absorbing and solving a business problem with minimal effort and without being too specialized so it cannot be reused.