2

I have a custom component that is basically a text box with an attached button. The button is supposed to perform an action on the text box; for example clicking the button could fill the text box with some random string.

The text fields are bound to properties in the ViewModel. It basically looks like this:

Three custom components with an attached button each

What would be the best way to set up a commanding that is general to the component?

What I did so far is that I have a single general RelayCommand in my ViewModel that expects a parameter. Each button has its command set to that single command and I use the CommandParameter property to add some information about which text field component I am actually talking about. The ViewModel then uses that information to find out the correct property and change its value (updating the text boxes via binding).

While this works fine, I dislike that I have to manually insert the information about the related text box or context. Ideally, I would like to have the command executed within a context-scope that already knows which text box or bound property it is talking about. Is there some way to do this?

Another problem I have run into is that I want to bind the button action to a key command. So when I’m focussing a text box and press a key shortcut, I want it to behave as if I have clicked the correct button, i.e. execute the command and pass the correct context information. My alternative would be to put this into the code-behind and basically extract the command parameter from the current focus, but I’d prefer a cleaner solution.

Is there any good way to make this work with MVVM?

Scroog1
  • 3,539
  • 21
  • 26
poke
  • 369,085
  • 72
  • 557
  • 602

2 Answers2

1

How about something along these lines:

public class TextBoxAndCommandPresenter : INotifyPropertyChanged
{
    private readonly Action<TextBoxAndCommandPresenter> _action;

    public TextBoxAndCommandPresenter(string description,
                                      Action<TextBoxAndCommandPresenter> action)
    {
        Description = description;
        _action = action;
    }

    public string Description { get; private set; }
    public string Value { get; set; }
    public ICommand Command
    {
        get { return new DelegateCommand(() => _action(this)); }
    }
}

Used like this:

var presenter = new TextBoxAndCommandPresenter("Field 1",
                                               p => p.Value = "hello world");

With XAML:

<UserControl ...>
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type TextBoxAndCommandPresenter}">
            <StackPanel Orientation="Horizontal">
                <Label Content="{Binding Description}"/>
                <TextBox Text="{Binding Value}"/>
                <Button Command="{Binding Command}">Click</Button>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>

    <ContentPresenter Content="{Binding}"/>
</UserControl>
Scroog1
  • 3,539
  • 21
  • 26
  • Hmm, do you have any idea how to solve the problem with the key binding? – poke Sep 06 '12 at 10:19
  • You could add an IsSelected flag on the presenter and then a command on the parent presenter that calls Command on the presenter that has IsSelected set to True and bind the key press to that. – Scroog1 Sep 06 '12 at 10:32
0

As I already had a custom control for the text box and the button combination, creating a UserControl wasn’t really a necessary option for me. My control exposes bindable properties for the button’s command and command parameter, and for now, I’m sticking with what I have explained in the question; using the command parameter to update the corresponding property in the view model that is then updated via data binding.

Depending on how repetitive it will become later, I might encapsulate that in either multiple custom controls or build a similar helper as Scroog1 showed.

As for the key command, which was actually my primary concern, I realized that this is ultimately something the view alone should handle. So my view model is completely oblivious of the key command.

I know have a standard command binding to the window’s code-behind that looks up the currently focused element, checks if it is of the type of my custom control and then simply executes the underlying command. So the code-behind is essentially just delegating the command execution to the focused control.

While this is not a perfect solution, as I’d rather have some actual “context sensitivity” for commands, this is working fine for now and still separates the view from the logic correctly.

poke
  • 369,085
  • 72
  • 557
  • 602