5

I have some code that works intermittently and I can't understand why (worked perfectly until today morning when windows automatically installed some updates, but none related to .NET 4 - version used in my project).

My password box ...

<PasswordBox x:Name="TboxPassword" Grid.Row="1" Grid.Column="0" 
                controls:TextboxHelper.Watermark="Password ..."
                controls:TextboxHelper.ClearTextButton="True"
                Margin="10, 10, 0, 0">
    <i:Interaction.Behaviors>
        <misc:PasswordBoxBehavior Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"/>
    </i:Interaction.Behaviors>
</PasswordBox>

My behavior:

public class PasswordBoxBehavior : Behavior<PasswordBox>
{
    #region Fields

    private readonly object _tryToExecuteActionSyncObject = new object();
    private bool _isUpdating;

    #endregion

    #region Properties

    public string Password
    {
        get { return (string)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(string), typeof(PasswordBoxBehavior),
        new PropertyMetadata(string.Empty, OnPasswordPropertyChanged));

    #endregion

    #region Methods

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PasswordChanged += OnAssociatedObjectPasswordChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        AssociatedObject.PasswordChanged -= OnAssociatedObjectPasswordChanged;
    }

    private void OnAssociatedObjectPasswordChanged(object sender, RoutedEventArgs e)
    {
        TryToExecuteAction(() => Password = AssociatedObject == null
            ? string.Empty
            : AssociatedObject.Password);
    }

    private static void OnPasswordPropertyChanged
        (DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        PasswordBoxBehavior passwordBoxBehavior;
        if (sender == null
            || (passwordBoxBehavior = sender as PasswordBoxBehavior) == null
            || passwordBoxBehavior.AssociatedObject == null)
        {
            return;
        }

        passwordBoxBehavior.TryToExecuteAction
            (() => passwordBoxBehavior.AssociatedObject.Password =
                    (e.NewValue == null
                        ? string.Empty
                        : (string) e.NewValue));
    }

    private void TryToExecuteAction(Action actionToExecute)
    {
        bool continueExecution;
        lock (_tryToExecuteActionSyncObject)
        {
            continueExecution = _isUpdating == false;
            _isUpdating = true;
        }

        if (continueExecution == false)
        {
            return;
        }

        try
        {
            if (actionToExecute != null)
            {
                actionToExecute();
            }
        }
        finally
        {
            lock (_tryToExecuteActionSyncObject)
            {
                _isUpdating = false;
            }
        }
    }

    #endregion
}

I get 0 (zero) compilation errors. When running the application, 90% of the time I'm getting a runtime exception stating that:

{"Cannot add instance of type 'PasswordBoxBehavior' to a collection of type 'BehaviorCollection'. Only items of type 'T' are allowed."}

Debugger stops at the tag Interaction.Behaviors

Please keep in mind that I never received this error until today. Now I receive it even after I revert everything I done today.

Please advise .. :D

PS: I just commented out all the code from inside the behavior. Also removed the Password binding. Still doesn't work :(

PPS: If I close Visual Studio (2012), delete my bin folder, open VS, open project, rebuild all, the application WORKS until the first change to the code.

daniele3004
  • 13,072
  • 12
  • 67
  • 75
smiron
  • 408
  • 3
  • 13
  • 4
    posting your code for this behavior may help others help you – Theodosius Von Richthofen Jul 30 '14 at 12:37
  • I would hazard a guess that something in your PasswordBoxBehavior constructor is throwing an exception, when run at runtime. Like you say, not sure why that would happen today and not yesterday. Can you post a bit more code? – kidshaw Jul 30 '14 at 12:42
  • No constructor override ... – smiron Jul 30 '14 at 12:45
  • @User123456789 The collection is defined by Microsoft. In order to add to that collection all I do is put my behavior inside the i:Interaction.Behaviors section. – smiron Jul 30 '14 at 14:01
  • Just a question out of pure interest - is this behaviours technique secure against snooping, as in [this article](http://www.programmersranch.com/2014/06/cwpfmvvm-why-you-shouldnt-bind.html)? (I'm guessing it isn't, given that Password is now a dependency property, but I'm curious to know anyway.) – Gigi Jul 30 '14 at 16:38
  • @Gigi No, its not secure. I know .. but I didn't like the alternative (sending the entire PasswordBox to the ViewModel). That makes it less testable. A good alternative is to have a behavior that takes the password from the PasswordBox, creates a SecureString and passes that to the ViewModel. That way we don't have issues with snoop and it is also testable. This is my plan for a future revision. PS: this version is also testable :) – smiron Jul 31 '14 at 07:41
  • @smiron ok, I just wanted to check because I'm always interested in finding alternative ways to bind passwords securely. For the method you mentioned, I believe PasswordBox already has a SecureString property you can use - not sure if you can bind it though. But using your same technique you could perhaps bind it and pass it back to the viewmodel. – Gigi Jul 31 '14 at 09:24
  • @Gigi You can't use it directly in XAML (it doesn't have a public setter). That is why you must use a behavior or attached property :). – smiron Jul 31 '14 at 13:08
  • @smiron: yes, by "same technique" I meant you also use behaviour/attached property, but instead of binding Password, you bind SecurePassword - http://msdn.microsoft.com/en-us/library/system.windows.controls.passwordbox.securepassword(v=vs.110).aspx . I've never tried this though, so it may or may not be possible. :) – Gigi Jul 31 '14 at 14:19

2 Answers2

4

I know this question is very old. But I will post the answer in case someone face it again.

I just came across the same issue and I found a solution, I noticed that you are using behaviors in your code. Just make sure that your are referencing the right version of Blend SDK Windows.Interactivity

In my case the problem was that I installed the NugetPackage for Blend SDK for WPF 4.5 only on the main WPF project, and I forgot to install it on the other WPF projects in the solution that uses behaviors.

I solved the problem by installing the same NugetPackage on the project containing the code causing the problem.

Hope this helps you!

Mohammed A. Fadil
  • 9,205
  • 7
  • 50
  • 67
1

Blend SDK Windows.Interactivity has been abandoned, and the file System.Windows.Interactivity.dll has been removed from GAC. Use Microsoft.Xaml.Behaviors.Wpf instead, you can install it from Nuget.org.

If you switch the "interactive library" from Blend SDK Windows.Interactivity(System.Windows.Interactivity) to Microsoft.Xaml.Behaviors.Wpf(Microsoft.Xaml.Behaviors) You can:

  1. Remove System.Windows.Interactivity reference from project.
  2. Close/Unload this solution/project, and open the solution/project directory.
  3. Delete belows folders :.vs, bin, obj.
  4. Reopen/Reload the solution/project.
  5. Replace all namespace of System.Windows.Interactivity to Microsoft.Xaml.Behaviors
  6. Build the solution/project.

Then it will be fine.