How to write a constructor that accesses inherited instance members?

Category: visual studio fsharp

Question

WinstonSmith6079 on Sat, 02 Apr 2016 20:22:30


I have a base class in a referenced assembly written in C# that I want to subclass in F#. The goal is to make an instance of the subclass, called "MainViewModel", the DataContext of the main window of a WPF application. That way I can bind properties and commands and handle Actions that are defined from both static and instance constructors.

Doing this in C# is straightforward. Attempting to access instance properties with existing objects assigned to them in the base class from F# seems impossible. 

Here's the assembly code:

  public class MainViewModelBase : NotifyBase
   {

      public MainViewModelBase()
      {         
         this._Text = "Test Text";
      }

      #region New UI Command

      private UICommand _New = new UICommand("New");

      public UICommand New
      {
         get { return this._New; }
         private set { this._New = value; }
      }

      #endregion


      #region Text (Notify Property)

      public static readonly String TextPropertyName = "Text";
      string _Text;
      /// <summary>
      /// <para>Text (Notify property.)</para>
      /// <para>Text property</para>
      /// </summary>
      [Notify]
      public string Text
      {
         get { return this._Text; }
         set
         {
            if (this._Text != value)
            {
               this.NotifyPropertySetValue(
                  TextPropertyName,
                  ref this._Text, value,
                  TextChanged);
            }
         }
      }
      /// <summary>
      /// Text property-changed Action (instance, new-value, old-value)
      /// </summary>
      public static Action<MainViewModelBase, string, string> TextChanged;

      #endregion

   }

Here's how I would code it in C#:

   public class MainViewModel : MainViewModelBase
   {
      public ViewModel()
      {
         this.HandleCommands()
      }

      void HandleCommands()
      {
         this.New.ExecuteCommand = () => this.Text = "New Text";
      }
   
   }
So how do I write this simple bit of code in F#? 

(The function of the code: a Button called "New" is clicked and a TextBox with "Test Text" is changed to "New Text." The Button Command property has a Binding to the New ICommand object. The TextBox has a Binding to the Text property that implements the INotifyPropertyChanged contract.)  


Replies

Mr. Tines on Sat, 02 Apr 2016 21:12:02


If you consult the MSDN page on Classes in F#, you will read that

You can access the methods and properties of the base class from the derived class by using the language keyword base as an identifier, followed by a period (.) and the name of the member.


so in this case you want "base.Text" rather than "this.Text" in the action.

WinstonSmith6079 on Sat, 02 Apr 2016 21:53:17


Thanks for the reply. I've tried this approach already. 

type MainViewModel() =
        inherit MainViewModelBase()
            do
                base.New.ExecuteCommand <- fun () -> base.Text <- "New Text"    

I get the following errors:

"The 'base' keyword is used in an invalid way. Base calls cannot be used in closures. Consider using a private member to make base calls."

"A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope."

F# is very finicky in the way it constructs an object. But I'm sure there's some way of arranging it.

I would also like to be able to call many member functions from the instance constructor that have instance member access to better organize command handling by category. E.g.,: "HandleFileCommands()", "HandleEditCommands()", etc. 

Mr. Tines on Sat, 02 Apr 2016 22:14:22


Try this to encapsulate the base references

type MainViewModel() as self =
        inherit MainViewModelBase()
            do
                self.DoNew()
            member private this.DoNew'() = base.Text <- "New Text"
            member private this.DoNew() = base.New.ExecuteCommand <- this.DoNew'()   
It's possible that the double layering is being more defensive than actually needed in the simple case, but it would provide scope for you to organize the commands.

WinstonSmith6079 on Sat, 02 Apr 2016 22:53:05


Thanks. That's the ticket. The "as self" gives me the access. 

I simplified the code to:

type MainViewModel() as self =
        inherit MainViewModelBase()
            do
                self.HandleCommands()             
        member private t.HandleCommands() = 
            t.New.ExecuteCommand <- fun () -> 
                t.Text <- "New Text"
 

BTW, in case you were wondering, I have C# code in a generated assembly that I used F# to parse and compile. That way I can create model classes with various kinds of properties and commands with lists of tuples that contain a name string with flags + xml doc string + instantiated object.

Not as clean as a type provider. But a lot easier than using code snippets. (Haven't gotten to the type provider level yet.)