Custom controls in ViewModel aided Views

This article is taking things further still – the ability to merge in 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 such viewmodels lend themselves to act as the complete definition of a view if you just dress them up with a few user interface hints (UI-Hints) – and how easy that is in the Modlr viewmodel designer.
  • effective way to get an administrative UI up and running in minutes instead of hours.

Consider this simple model:

Image.png

Accompanied by this viewmodel:

Image1.png

Here we display a list of all flowers by name, and a 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 for 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 ti

Image2.png

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 a the value in a NumberOfLeafs property.

We want to be able to bind this property to a datasource – 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 display. But 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 to license 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;

Image thumb3.pngImage thumb4.png

This page was edited 91 days ago on 02/10/2024. What links here