Kugel
Kugel

Reputation: 19844

Recommend best approach in this situation

I'm experimenting with MVVM and I can't quite wrap my mind around it.

I have a View (windows) that has several repeated controls.

Let's say I have 4 textbox - button pairs. Their behavior should be the same, after pressing a button paired textbox says "Hello World!"

I have tried several options I could think of:

  1. One ViewModel, 4 string properties binded to textboxes, 1 command
    • When I bind each button to the same command I can't tell which property needs to be set.
    • Passing enum to CommandParameter feels awkward.
  2. One ViewModel and UserControl that hosts a textbox and a button repeated 4 times.
    • Now I need to expose all the properties like Command, CommandParameter, Text etc.. Seems like a lot of work.
    • Still can't say which property needs to be updated after click.
  3. Each UserControl has a ViewModel
    • This solves button clicking and setting property, but now I have no clue how to extract texts from nested ViewModels to the window ViewModel.

Is there any other way? I suspect DataTemplates could be of use, but I'm not sure how.

Upvotes: 2

Views: 121

Answers (3)

John Bowen
John Bowen

Reputation: 24453

There's no need to create a separate ViewModel for a reusable control that has such simple behavior. Just by adding a few DependencyProperties and an event handler to the simple UserControl you can reuse the logic and only set the properties that are actually different on each instance. For the UserControl XAML you just need to hook up the TextBox to the DependencyProperty and the Button to a Click handler.

<DockPanel>
    <Button Content="Reset" Click="Button_Click"/>
    <TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Text}"/>
</DockPanel>

The code for the UserControl just needs to define the properties that can be bound externally and the handler to reset the Text.

public partial class ResetTextBox : UserControl
{
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
        "Text",
        typeof(string),
        typeof(ResetTextBox),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty ResetTextProperty = DependencyProperty.Register(
        "ResetText",
        typeof(string),
        typeof(ResetTextBox),
        new UIPropertyMetadata(String.Empty));

    public string ResetText
    {
        get { return (string)GetValue(ResetTextProperty); }
        set { SetValue(ResetTextProperty, value); }
    }

    public ResetTextBox()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Text = ResetText;
    }
}

Then to use the control you just need to bind to your ViewModel string properties and set the default text that should be applied on a reset which can either be hardcoded here, or bound to other VM properties.

<StackPanel>
    <local:ResetTextBox ResetText="One" Text="{Binding Name1}"/>
    <local:ResetTextBox ResetText="Two" Text="{Binding Name2}"/>
    <local:ResetTextBox ResetText="Three" Text="{Binding Name3}"/>
</StackPanel>

Upvotes: 1

Kieren Johnstone
Kieren Johnstone

Reputation: 42003

What you describe is such an abstract and contrived idea that it doesn't warrant MVVM. You're talking about TextBoxes and Buttons, which are all 'View', and not the MVVM way of thinking. You'd almost always start with a Model.

There is no 'Model' per-se here though; your specification is literally to set the value of a TextBox on a Button click. The seemingly random list of '4' items (picked out of nowhere) and a seemingly useless TextBox mean nothing.

Putting that aside and assuming that you have a set of 4 business entities, each with a field on them that is user-editable, and an action that the user can trigger, you'd do this:

  • Create a ViewModel class to represent an item - eg MyItemModel
  • Create a ViewModel class to represent the set of items (likely it will just return a Collection of the first) - eg AllMyItemsListModel

Then for the View:

  • Create an ItemsControl, with ItemsSource bound to an instance of the 'collection' of the second ViewModel class
  • For each ItemTemplate, have a template or UserControl for each item
  • Within the template or UserControl, bind the TextBox's Text property to the appropriate property of the first class
  • Bind the Button's Command property to a property on the first class returning an ICommand - using RelayCommand for example

I don't know what you mean about 'extracting texts from nested ViewModels to the window ViewModel' - what does that mean and why would you want to do it?

Hope that helps.

Upvotes: 2

nportelli
nportelli

Reputation: 3916

Number 3. Except there would just be one UserControl with viewmodel and then the Main page that would have multiple instances of that UserControl on it. If the main window needs info from the UserControl you could pass it through events or use something like MVVM Light and it's messenger class.

Upvotes: 1

Related Questions