1

I'm writing WPF application with MVVM structure using MVVM Light. I have class Foo in the Model:

class Foo: ObservableObject
{
    private string _propA = String.Empty;
    public string PropA
    {
        get => _propA ;

        set
        {
            if (_propA == value)
            {
                return;
            }

            _propA = value;
            RaisePropertyChanged("PropA");
        }
    }

    // same for property PropB, PropC, PropD, etc.
}

And I have some collection of Foo objects in the Model:

class FooCollection: ObservableObject
{
    private ObservableCollection<Foo> _items = null;
    public IEnumerable<Foo> Items 
    {
        get { ... }
        set { ... }
    } 

    public string Name { get; set; }

    // ...
    // and other methods, properties and fields
}

Now I have a ViewModel where this list is populated via some injected provider:

class MainWindowModel: ViewModelBase
{
     private FooCollection _fooList;

     public FooList 
     {
         get => _fooList;
         set 
         {
             _fooList = value;
             RaisePropertyChangedEvent(FooList);
         }
     }

     public MainWindowModel(IFooListProvider provider)
     {
         FooList = provider.GetFooList();
     } 
}

And the View, with MainWindowModel as data context:

<TextBlock Text={Binding FooList.Name} />
<ItemsControl ItemsSource="{Binding FooList.Items}">
     <ItemsControl.ItemTemplate>
         <DataTemplate>
             <TextBlock Text={Binding PropA} />
             <Button Content={Binding PropB} />
             <!-- other controls with bindings -->
         </DataTemplate>
     </ItemsControl.ItemTemplate>
</ItemsControl>

Everything works fine, I can delete and add new items, edit them and etc. All changes in View reflects automatically in ViewModel and Model via bindings and observable objects, and vice versa.


But now I need to add ToggleButton to data template of ItemsControl, which controls visibility of particular item in other part of window. I need IsChecked value in ViewModel, because control in other part of window is Windows Forms control and I can't bind IsChecked directly without ViewModel. But I don't want to add new property (Visibility, for example) in model classes (Foo, FooCollection), because it is just an interface thing and it doesn't need to be saved or passed somewhere outside ViewModel.

So my question: what is the best way to add new property to Model collection in ViewModel?

I could create new collection of wrappers in ViewModel (some sort of class Wrapper { Foo item, bool Visibility }) and bind it to ItemsControl. But in this case I have to control adding, removing and editing manually and transfer all changes from List<Wrapper> to FooList.Items, so I don't like this solution. Is there any more simple way to achieve this?


Edition to clarify the question. Now I have:

<ItemsControl ItemsSource="{Binding FooList.Items}">
     <ItemsControl.ItemTemplate>
         <DataTemplate>
             <TextBlock Text={Binding PropA} />
             <Button Content={Binding PropB} />
             <ToggleButton IsChecked={Binding ????????????} />
             <!-- other controls with bindings -->
         </DataTemplate>
     </ItemsControl.ItemTemplate>
</ItemsControl>

I have no field in class to bind IsChecked and I don't want to add it to class, because it's only interface thing and not data model field. How can I, for example, create another collection of bools and bind it to this ItemControl alongside with FooList.Items?

Aleksei Petrov
  • 936
  • 12
  • 31

2 Answers2

1

The best place to add the property is of course in the Foo class.

Creating another collection of some other type, add an object per Foo object in the current collection to this one, and then bind to some property of this new object seems like a really bad solution compared to simply adding a property to your current class.

Foo is not an "interface thing", or at least it shouldn't be. It is view model that is supposed to contain properties that the view binds to. There is nothing wrong with adding an IsChecked property to it. This certainly sounds like the best solution in your case.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • I think the concern is adding a view-specific property to the model class. I don't see `Foo` as a view model here. – Tyler Daniels Sep 05 '17 at 15:00
  • 1
    It is/should be a view model. Binding directly to a domain object is not useful very often. – mm8 Sep 06 '17 at 09:32
  • Foo IS NOT interface thing. `IsVisible` property IS interface thing. That's why I don't want to add interface-only property in model class. – Aleksei Petrov Sep 07 '17 at 08:11
  • You missed the point; Foo is not supposed to be a model class. It is a view model with properties that the view binds to. You generally don't bind to domain objects without "interface" properties. This is your issue. – mm8 Sep 07 '17 at 09:23
  • @mm8 Do you suggest to create some wrapper to domain object, which will have "interface" property? – Aleksei Petrov Sep 07 '17 at 10:05
  • @mm8 Thanks, got it =) – Aleksei Petrov Sep 07 '17 at 10:06
0

I'm not sure if I understand why you would need to add a property in the model.

Can't you just use the command property or add an EventTrigger to your toggle button? (See Sega and Arseny answer for both examples Executing a command on Checkbox.Checked or Unchecked )

This way, when you check the toggleButton, there is a method in your viewModel which enable or disable the visibility property of your Winform control.

To change the visibility of your control from a command in your viewModel, you could use the messenger functionnality of MVVM LIGHT MVVM Light Messenger - Sending and Registering Objects

The ViewModel sends a message to you're Windows Forms and this one handles the visibility of your control.

Speuline
  • 201
  • 2
  • 15
  • Of course I can just attach an event handler, or a command, or whatever envokable when users click on ToggleButton, but it doesn't solve the problem: there is no property of ViewModel binded to toggle button. The question is how to add a new field in collection to which I can bind new user control without touching Model class itself. – Aleksei Petrov Aug 30 '17 at 08:17