Question

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?

Était-ce utile?

La solution 2

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.

Autres conseils

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>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top