1

WPF project + Prism 7 + (Pure MVVM pattern)

Simple, I have TextBox which need to be cleared when some button is pressed (without the violation to the MVVM pattern)

<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
    <i:Interaction.Behaviors>
        <local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
    </i:Interaction.Behaviors>
</TextBox>

ViewModel

public class ViewModel {
    public ICommand ClearCommand { get; set; }
}

Behavior

public class ClearTextBehavior : Behavior<TextBox>
{
    public ICommand ClearTextCommand
    {
        get { return (ICommand)GetValue(ClearTextCommandProperty); }
        set
        {
            SetValue(ClearTextCommandProperty, value);
            RaisePropertyChanged(); 
        }
    }

    public static readonly DependencyProperty ClearTextCommandProperty =
        DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));

    public ClearTextBehavior()
    {
        ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
    }

    private void ClearTextCommandExecuted()
    {
        this.AssociatedObject.Clear();
    }
}

The problem is the command in the ViewModel is always null (it did not bound to the command in the Behavior), Although I made sure that it is initialized in the behavior class.

NOTE: please do NOT suggest to set the File property to empty string, because this is just an example, In my real case, I need to select all the Text, so I really need an access to the AssociatedObject of the behavior

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
  • ClearCommand is null in your viewmodel there. – Andy Nov 20 '18 at 19:56
  • @Andy yes, and I do not know why, it should be the bound from the behavior, but it does not – Hakan Fıstık Nov 21 '18 at 05:31
  • Do you want to solve your SelectAllText problem or this command problem (which you think will solve your SelectAllText problem)? – Sir Rufo Nov 21 '18 at 05:33
  • @SirRufo the main problem is SelectAllText, I want to solve this problem – Hakan Fıstık Nov 21 '18 at 06:00
  • Then you should rewrite your question: How to ... SelectAllText? I tried with ... but no success. – Sir Rufo Nov 21 '18 at 06:24
  • I think you missed my point. Unless you set ClearCommand to some sort of a command then it will be null. Are you setting it to anything? Because there's nothing in your code does that. All you do is define the property. – Andy Nov 21 '18 at 09:23
  • @Andy I am not setting it in the code, but I am binding it like this `` see the XAML code above – Hakan Fıstık Nov 21 '18 at 10:22

2 Answers2

1

If i understood your Question correctly, you want to know why the ICommand in the ViewModel is not set to the DelegateCommand defined in the Behaviour.

The Problem is, that the ICommand and the DelegateCommand do not have a direct connection. I assume you may misunderstood how a Binding works and what happens by using those.

First of all, the ICommand is 'comes' from a Class and is therefore a reference Type.

Second, the reference to the ICommand is saved within the DependencyProperty ClearTextCommandProperty.

Third, by using a Binding in the XAML something like this happens as C# code:

Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand; 
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);

Now the important thing: I don't know exactly which assignment comes first, but both lines will override the Value reference in the ClearTextCommandProperty!

//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);

//or here 
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));

At no point there is an assignment like this:

ViewModel.ClearCommand = SomeICommand;

Therefore it is Null, as @Andy mentioned


Edited to match select all Text

Additionally, i suggest you drop this complex stuff and use the full potential of the Interactivity Package like this:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

<Button>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <utils:SelectAllText TargetName="TextToSelect"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

<TextBox x:Name="TextToSelect" Text="{Binding File}"/>

And the SelectAllText

public class SelectAllText : TargetedTriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        if (Target == null) return;

        Target.SelectAll();
        Target.Focus();
    }
}
LittleBit
  • 1,076
  • 6
  • 14
0

If you take a look at this sample here: https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx You will notice that I have an ICommand there and it's set up to run a method.

If it was just an ICommand with a Get and Set like you have there then it would be NULL. There's a property but it's null until it is set to something.

This a very clunky way to implement an ICommand but relies on no external libraries or anything.

If you take a look at the second article in that series, it uses mvvmlight and relaycommand so creating a command is rather less clunky.

https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx

public RelayCommand AddListCommand { get; set; }

public MainWindowViewModel()
{
    AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
    stringList.Add(myString));
}

If you look at that code AddListCommand is initially null. It is set in the constructor to a new RelayCommand which means it is then not null.

This is fairly simple but the code for the command is in a different place to the property so a more elegant approach is usual. As shown here: https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx


Having said all that. Selecting all text is something to do in the view, not the viewmodel. You shouldn't really be passing a piece of UI from the view into a viewmodel.

Rather than a command it could well be that you should be binding a bool which is set in the viewmodel and acted on in the behaviour.

Andy
  • 11,864
  • 2
  • 17
  • 20