0
votes

I'm working on a GUI application in WPF/MVVM. Let's say i have a Model class which is populated deserializing a (third-party) XML file

class Person
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string ReservationId { get; set; }
}

and a ViewModel which exposes to its View a lot of properties and commands to manipulate the Model, in this case strings (such as ReservationId):

class StringManipulatorViewModel
{
    string modelString; //here's the problem

    public StringManipulatorViewModel(string modelString) 
    {
        this.modelString = modelString;
    }

    //Stuff to let the view manipulate the string
}

StringManipulatorViewModel is highly reusable and it is used by a lot of ViewModels, e.g.

class PersonViewModel
{
    Person model;
    public StringManipulatorViewModel ReservationManipulatorVM; //aggregated ViewModel
    public StringManipulatorViewModel AddressManipulatorVM; //aggregated ViewModel

    public PersonViewModel(Person model)
    {
        this.model = model;
        ReservationManipulatorVM = new StringManipulatorViewModel(model.ReservationId); //here's the problem
        AddressManipulatorVM = new StringManipulatorViewModel(model.Address); //here's the problem
    }
}

Obviously passing the string as "model" to the ViewModel isn't effective, and C# doesn't seem to allow string references as fields. What is the best/right way to let member ViewModels manipulate the Model when dealing with string types?

Thank you

2

2 Answers

1
votes

Your problem is that you are trying to reference a property, not a string field.

But you can pass a delegate to the setter of the property.

If you also change the modelString field to a property, you can call this delegate automatically when the string is changed.

class StringManipulatorViewModel
{
    private string modelString
    {
        get { return _modelString; }
        set { _modelString = value; if (SetModelString != null) SetModelString(value); }
    }
    private string _modelString;
    Action<string> SetModelString;

    public StringManipulatorViewModel(string initialValue, Action<string> setModelString)
    {
        this.modelString = initialValue;
        SetModelString = setModelString;
    }
    //Stuff to let the view manipulate the string
}

You initiate the StringManipulatorViewModel in PersonViewModel like this:

        ReservationManipulatorVM = new StringManipulatorViewModel(model.ReservationId, value => model.ReservationId = value); //here's the problem

Here are some other ideas when you want to pass a property.

Passing properties by reference in C#

0
votes

I can't think of a "right" way to manipulate the string as a reference inside the StringManipulatorViewModel, once you pass the string as a value it has nothing to do with the model.

But a way to legitimately change the model string value whenever the StringManipulatorViewModel manipulates it, is by raising an Event in the view model when it manipulates the string and then add an event handler to update the model with the new value:

class StringManipulatorViewModel
{
    string modelString;

    public event EventHandler<string> StringManipulated;


    public StringManipulatorViewModel(string modelString) 
    {
        this.modelString = modelString;
    }


    public ManipulateString() 
    {
        // Manipulate the string

        StringManipulated?.Invoke(this, modelString);
    }
    
}

And in the PersonViewModel constructor:

class PersonViewModel
{
    Person model;
    public StringManipulatorViewModel ReservationManipulatorVM;
    public StringManipulatorViewModel AddressManipulatorVM;

    public PersonViewModel(Person model)
    {
        this.model = model;
        ReservationManipulatorVM = new StringManipulatorViewModel(model.ReservationId); 
        AddressManipulatorVM = new StringManipulatorViewModel(model.Address);

        ReservationManipulatorVM.StringManipulated += (sender, e) => model.ReservationId = e;
        AddressManipulatorVM.StringManipulated += (sender, e) => model.Address = e;
    }
}