2
votes

I have created a few Custom Controls (NOT UserControls) with bind-able "ClearCommand" ICommand dependency properties. This property will do exactly what it sounds: it will clear all the values from the control (textboxes, etc). I also bind (some) of those same properties to the VM I describe below.

Now I'm stuck trying to trigger the ClearCommand in those controls in the following MVVM scenario:

I've added a few such controls into my View. The View also includes a "Save" button that binds to my ViewModel's SaveCommand DelegateCommand property.

What I need to happen is that, upon a successful save, the VM should trigger the ClearCommand on those controls found in the View.enter image description here

UPDATE

I've added code examples below. I have a few controls that resemble the ExampleCustomControl. Also, just to note, I am open to restructuring some of this if it's completely off.

Example Control snippet:

public class ExampleCustomControl : Control {

    public string SearchTextBox { get; set; }
    public IEnumerable<CustomObject> ResultList { get; set; }

    public ExampleCustomControl() {
        ClearCommand = new DelegateCommand(Clear);
    }

    /// <summary>
    /// Dependency Property for Datagrid ItemSource.
    /// </summary>
    public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
        typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject)));

    public CustomObject SelectedItem {
        get { return (CustomObject)GetValue(SelectedCustomObjectProperty); }
        set { SetValue(SelectedCustomObjectProperty, value); }
    }

    public static DependencyProperty ClearCommandProperty = DependencyProperty.Register("ClearCommand", typeof(ICommand),
            typeof(ExampleCustomControl), new PropertyMetadata(default(ICommand)));

    /// <summary>
    /// Dependency Property for resetting the control
    /// </summary>
    [Description("The command that clears the control"), Category("Common Properties")]
    public ICommand ClearCommand {
        get { return (ICommand)GetValue(ClearCommandProperty); }
        set { SetValue(ClearCommandProperty, value); }
    }

    public void Clear(object o) {
        SearchTextBox = string.Empty;
        SelectedItem = null;
        ResultList = null;
    }
}

Example View snippet:

<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <control:ExampleCustomControl Grid.Row="0"
        SelectedItem="{Binding Selection, UpdateSourceTrigger=PropertyChanged}" />
    <Button Grid.Row="1" x:Name="ResetButton" Command="{Binding SaveCommand}">
        Save
    </Button>
</Grid>

Example ViewModel:

public class TestViewModel : WorkspaceTask {
    public TestViewModel() {
        View = new TestView { Model = this };
        SaveCommand = new DelegateCommand(Save);
    }

    private CustomObject _selection;
    public CustomObject Selection {
        get { return _selection; }
        set {
            _selection = value;
            OnPropertyChanged("Selection");
        }
    }

    public DelegateCommand SaveCommand { get; private set; }
    private void Save(object o) {
        // perform save
        // clear controls
    }
}
3
Typically, my commands are on my ViewModel. From the ViewModel, you are able to call MyCommand.Execute();. If that is not how your project is structured, please post some code to clarify.cadrell0
As a quick workaround you could set the PropertyChangedCallback in the PropertyMetadata when you register SelectedIOtemProperty and in the callback, have it clear everything you want. I would however advise having a think about restructuring your setup and putting the clear in the VM along with all the bound properties.HAdes
My issue with having all of the ExampleCustomControl's properties bound to a viewmodel is reuse. I want the search logic/etc sitting in the custom control and I want to be able to reuse the same control in a few different views.Alex

3 Answers

2
votes

As others have said the VM shouldn't know about the view directly in MVVM so it doesn't make sense really that the VM triggers something on your custom control to clear everything.

I would have set the DataContext of the custom control to an object that has all the properties you want to clear, which are all each bound (two-way) to your textboxes etc. Then in the Save() method you can set a new object (which the custom control DataContext is bound to) and all the properties will be cleared for you (assuming you have implemented INotifyPropertyChanged on the object).

UPDATED:

As per my comment, see an example of the workaround for your current setup (untested btw):

    public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
        typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject), OnSelectedItemChanged));


    private static void OnSelectedItemChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        var cont = source as ExampleCustomControl;

            //do all the clearing of txtboxes etc here....
        cont.SearchTextBox = string.Empty;
    }

But I would still try and move all this into the VM. i.e. have a clear command, like you do with the save command and bind the textbox text etc to a property in the VM and when the command is called it clears everything, which you can then easily call from the Save method in the VM too. But obviously I have no idea what you are trying to achieve in the long run or how selectedItem and the textboxes etc are related, so depends (as always) i guess.

1
votes

It sounds like you are thinking about this the wrong way. In MVVM the ViewModel should never know anything about the custom controls (hence you are having a problem with this Clear functionality).

Your requirements are a bit vague, but have you considered:

1) If the properties are bound from the VM, can't the Control detect when these are changed?

2) If you really need to call Clear from the XAML layer and want to keep it pure MVVM, then consider something like the Expression Blend SDK's CallMethodAction.

0
votes

As a followup to my comment. I suspect your command is targeting the View and clearing the TextBoxes directly. Instead, have your command target the ViewModel and clear the properties the View is bound to. Then you can have the command be a property on the ViewModel and call it whenever needed.