7

I am trying to convert a C# Dependency Property that limits the maximum length of text entered into a ComboBox to F#. The program is a MVVM program that uses F# for the model and viewmodel, and C# for the view. the working C# code is this:

public class myComboBoxProperties
    {
        public static int GetMaxLength(DependencyObject obj)
        {
            return (int)obj.GetValue(MaxLengthProperty);
        }

        public static void SetMaxLength(DependencyObject obj, int value)
        {
            obj.SetValue(MaxLengthProperty, value);
        }

        // Using a DependencyProperty as the backing store for MaxLength. This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxLengthProperty =
            DependencyProperty.RegisterAttached("MaxLength",
            typeof(int),
            typeof(myComboBoxProperties),
            new UIPropertyMetadata(OnMaxLengthChanged));

        private static void OnMaxLengthChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (obj is ComboBox)
            {
                ComboBox comboBox = (ComboBox)obj;

                comboBox.Loaded += (sender, e) =>
                {
                    TextBox textBox = comboBox.Template.FindName("PART_EditableTextBox", comboBox) as TextBox;

                    if (textBox != null)
                    {
                        textBox.SetValue(TextBox.MaxLengthProperty, args.NewValue);
                    }
                };
            }
        }
    }

The F# code is this:

type myComboBoxProperties() =

    static let OnMaxLengthChanged  (myobj1 : DependencyObject, args : DependencyPropertyChangedEventArgs)  =

        let comboBox = myobj1 :?> ComboBox

        comboBox.Loaded.Subscribe (fun _ -> 
                                    let textBox : TextBox = comboBox.Template.FindName("PART_EditableTextBox", comboBox) :?> TextBox 
                                    match textBox with
                                    | null -> ()
                                    |_ -> textBox.SetValue(TextBox.MaxLengthProperty, args.NewValue))

    static let MaxLengthProperty = DependencyProperty.RegisterAttached("MaxLength", typeof<int>, typeof<myComboBoxProperties>, new UIPropertyMetadata(OnMaxLengthChanged))

    static member GetMaxLength (myobj : DependencyObject) = myobj.GetValue(MaxLengthProperty) :?> int

    static member SetMaxLength (myobj : DependencyObject, value : int) = myobj.SetValue(MaxLengthProperty, value)

The problem I am having is that the XAML error I get is:

Default value type does not match type of property MaxLength

What am I doing wrong?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Mark W
  • 811
  • 2
  • 11
  • 17
  • 1
    Can you provide a stack trace? You can also try to set a default value explicitly for the sake of experiment. `new UIPropertyMetadata(0, OnMaxLengthChanged));` for example. Looks like error occur at the moment of [check](http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/DependencyProperty.cs,4aed0e0929184ad0) of parameterType and type of [automatically generated](http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/DependencyProperty.cs,c2c01ce10ae3e445,references) by framework default value equality which is stange for me. – Leonid Vasilev May 16 '15 at 17:51
  • there is no stack trace, I am trying this in debug. I can't give a default because I don't know how to change OnMaxLengthChanged into a propertycallback. I am new to F# – Mark W May 16 '15 at 18:07

1 Answers1

4

You could try this

open System.Windows
open System.Windows.Controls

type MyComboBoxProperties() =

  static let OnMaxLengthChanged  (myobj1 : DependencyObject) (args : DependencyPropertyChangedEventArgs) =

    let comboBox = myobj1 :?> ComboBox

    comboBox.Loaded.Add (
      fun _ -> 
        let textBox : TextBox = comboBox.Template.FindName("PART_EditableTextBox", comboBox) :?> TextBox 
        match textBox with
        | null -> ()
        |_ -> textBox.SetValue(TextBox.MaxLengthProperty, args.NewValue)
      )

  static let MaxLengthProperty = 
    DependencyProperty.RegisterAttached(
      "MaxLength", 
      typeof<int>, 
      typeof<MyComboBoxProperties>, 
      UIPropertyMetadata(0, PropertyChangedCallback OnMaxLengthChanged)
      )

  static member GetMaxLength (myobj : DependencyObject) = myobj.GetValue(MaxLengthProperty) :?> int

  static member SetMaxLength (myobj : DependencyObject, value : int) = myobj.SetValue(MaxLengthProperty, value)

The key difference to your code is this UIPropertyMetadata(0, PropertyChangedCallback OnMaxLengthChanged) that converts OnMaxLengthChanged into a PropertyChangedCallback.

But it feels odd to me that you subscribe to the .Loaded even each time the max value you change. I suspect you only like to subscribe the first time?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • i will try this! I was just trying to convert a c# project to f#. thanks I will let you know how it works – Mark W May 16 '15 at 20:11
  • I think you have two ways to go about ... one way is to provide your own `ComboBoxTextBox` style that does a `TemplateBinding` to the attached `MaxLength` property. If you like to keep your current pattern an approach is to introduce a property keeping track of additional state. I can demonstrate that but it's a new question IMO. – Just another metaprogrammer May 17 '15 at 09:50