How can I use an array in a ViewModel?

My code looks like this right now with two lines of code for each message. The code works but if I have for example 30 messages that I can each give values to then I will need to have 60 lines of code just to declare everything:

string _msg1;
string _msg2;
public string Msg1 { get => _msg1; set => SetProperty(ref _msg1, value); }
public string Msg2 { get => _msg2; set => SetProperty(ref _msg2, value); }

and in C# I assign to these:

vm.Msg1 = "A";
vm.Msg2 = "B"; 

and in the XAML I bind my Text to Msg1 and another Text to Msg2

Can someone tell me how / if I can do this with array so that I would assign like this and hopefully so the assignment of the array can just be done in two lines instead of 2 lines for every single message:

vm.Msg[0] = "A";
vm.Msg[1] = "B";

For reference:

public class ObservableObject : INotifyPropertyChanged
{

    protected virtual bool SetProperty<T>(
        ref T backingStore, T value,
        [CallerMemberName]string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

You can create a simple wrapper class with indexing that supports property change notification.

For example:

public class Messages : ObservableObject
{
    readonly IDictionary<int, string> _messages = new Dictionary<int, string>();

    [IndexerName("Item")] //not exactly needed as this is the default
    public string this[int index]
    {
        get
        {
            if (_messages.ContainsKey(index))
                return _messages[index];

//Uncomment this if you want exceptions for bad indexes
//#if DEBUG
//          throw new IndexOutOfRangeException();
//#else
            return null; //RELEASE: don't throw exception
//#endif
        }

        set
        {
            _messages[index] = value;
            OnPropertyChanged("Item[" + index + "]");
        }
    }
}

And, create a property in view model as:

private Messages _msg;
public Messages Msg
{
    get { return _msg ?? (_msg = new Messages()); }
    set { SetProperty(ref _msg, value); }
}

Now you can set or update values as:

vm.Msg[0] = "A";
vm.Msg[1] = "B";

Bindings in XAML will be same as:

<Label Text="{Binding Msg[0]}" />
<Label Text="{Binding Msg[1]}" />
Sample usage code

XAML

<StackLayout Margin="20">
    <Label Text="{Binding Msg[0]}" />
    <Label Text="{Binding Msg[1]}" />
    <Label Text="{Binding Msg[2]}" />
    <Label Text="{Binding Msg[3]}" />
    <Label Text="{Binding Msg[4]}" />

    <Button Text="Trigger update" Command="{Binding UpdateMessage}" />
</StackLayout>

Code-behind, view-model

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        var viewModel = new MainViewModel();

        viewModel.Msg[0] = "Original message 1";
        viewModel.Msg[1] = "Original message 2";
        viewModel.Msg[2] = "Original message 3";
        viewModel.Msg[3] = "Original message 4";
        viewModel.Msg[4] = "Original message 5";

        BindingContext = viewModel;
    }
}

public class MainViewModel : ObservableObject
{
    private Messages _msg;
    public Messages Msg
    {
        get { return _msg ?? (_msg = new Messages()); }
        set { SetProperty(ref _msg, value); }
    }

    public ICommand UpdateMessage => new Command(() =>
           {
               Msg[2] = "New message 3";
               Msg[0] = "New message 1";
           });
}

I'm trying to bind each of them to my viewmodel, but I think I might have the wrong approach. We'll use a combobox as an example. My model to contain the data: Used in a website to show User profile widget. Used to make Dashboards for integrating multiple data into one place. Used as an Aggregate Root to make reports in Domain Driven Design (DDD) These are the complex scenarios in which ViewModel can be used for better maintainability, reliability, and testability of code.

Arrays will not raise property changed event. You'll need to use an ObservableCollection that can raise an event when the collection has changed. However, this doesn't raise an event when the object inside the collection has changed it's value. You'll need to wrap your object, in this case a string, into a type that can raise property changed events.

Something like the following would work:

    public class BindableValue<T> : INotifyPropertyChanged
    {
        private T _value;
        public T Value
        { get => _value; set => SetProperty(ref _value, value); }
        // INotifyPropertyChanged and SetProperty implementation goes here
    }

    private ObservableCollection<BindableValue<string>> _msg;
    public ObservableCollection<BindableValue<string>> Msg
    { get => _msg; set => SetProperty(ref _msg1, value); }

you would be binding to Msg[0].Value, Msg[1].Value etc.,

When the user clicks the submit button Save, the appropriate Action in the Controller should take care of update the properties of the model and  We can say that the array, cellViewModels, represents the table view. Once we update the cellViewModels in ViewModel, the closure reloadTableViewClosure will be called and the View updates

I Assume that your given example is running and working as expected (Atleast with 2 items)

View Code.

Assuming you want to show all the 30 messages as a list.

<ListView ItemsSource="{Binding MessagesArray}"/>

Also you should set the DataContext properly, Comment below if you need any help

View Model Code.

We are using an ObservableCollection instead of array. Since pure arrays doesn't support proper binding features.

private ObservableCollection<string> _messagesArray;

public ObservableCollection<string> MessagesArray  
{  
    get { return _messagesArray; }  
    set { SetProperty(ref _messagesArray, value); }  
}  

Assigning Values

MessagesArray = new ObservableCollection<string>();
vm.MessagesArray.Add("A");
vm.MessagesArray.Add("B");

In the assignment code MessagesArray = new ObservableCollection<string>(); assigns a new object of ObservableCollection of String

If you are new to ObservableCollection think of this as an wrapper to string[], but not actually true

SetProperty method will tell the XAML View that a new collection is arrived, so the UI will rerender the list.

When you call vm.MessagesArray.Add("B"); internal logics inside the method Add will tell the XAML View a new item is added to the ObservableCollection so the view can rerender the ListView with the new item.

Update 27 October 2018

You can create your own array using any of the below ways. (Not all)

 string[] dataArray = new string[30];

1. this will create an array with 30 null values

 string[] dataArray = { "A", "B", "C" }; //Go up to 30 items

2. this will create an array with predefined set of values, you can go up to 30

 string[] dataArray = Enumerable.Repeat<string>(String.Empty, 30).ToArray();

3. this will create an array with string which holds empty values, Instead of String.Empty you can put any string value.

Choose any of the above method

I recommend the last method, then you can assign that into a Observable Collection like below.

MessagesArray = new ObservableCollection<string>(dataArray); 

Now the trick is

vm.MessagesArray[0] = "A"
vm.MessagesArray[25] = "Z"

View might look like below

<TextBlock Text="{Binding MessagesArray[0]}"/>
<TextBlock Text="{Binding MessagesArray[1]}"/>

NET MVC 4 sample application to show how to data bind/postback get a null value in the array parameter or ViewModel array property inside  Adding CustomerVM in ViewModels folder. Inside this folder let’s add ViewModel with name CustomerVM.cs for that just right Click on ViewModels folder then select Add and last select Class like as shown below. Once we select class then Add New Item dialog will popup in that select class and give name as CustomerVM.cs.

Not entirely sure that I got the question, but as I understood the simplest way is this:

The Viewmodel:

Just bind to an ObservableCollection of strings, because it already implements INotifyCollectionChanged and INotifyPropertyChanged. RelayCommand is just an implementation of ICommand and I'm assuming you have heard of them since you are doing WPF MVVM.

using System.Collections.ObjectModel;

namespace WpfApp1
{
    public class MainWindowViewmodel
    {
        public ObservableCollection<string> Messages { get; set; }
        public MainWindowViewmodel()
        {
            Messages = new ObservableCollection<string>();
            Messages.Add("My message!");
            ChangeMessageCommand = new RelayCommand(ChangeMessageExcecute); 
        }
        public RelayCommand ChangeMessageCommand { get; set; }
        private void ChangeMessageExcecute() => Messages[0] = "NEW message!";
    }
}
The View:

In the view you can just bind your Textblocks to the Elements of the ObservableCollection. When you press the button, the Command gets called and changes the message in the window.

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Messages[0]}" HorizontalAlignment="Center"/>
            <Button Content="Change Message" Command="{Binding ChangeMessageCommand}" Width="200"/>
        </StackPanel>
    </Grid>
</Window>

Kind regards, misdirection

In my case my model has single property – List of strings. Let's take a look at the code. using System.Collections.Generic; using System.Web.Mvc;  To access or change UI data, you can now use the data in your ViewModel. Here’s an example of the new onCreate method and a method for updating the score by adding one point to team A:

What about using reflection? You can ask for all the public properties of type string with name "Msg*".

For example:

static class Program
{
    static void Main(string[] args)
    {
        var vm = new MessagesViewModel();

        PropertyInfo[] myProperties = vm.GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.PropertyType == typeof(string) && p.Name.Contains("Msg"))
            .ToArray();

        foreach (var propertyInfo in myProperties)
        {
            //You can also access directly using the indexer --> myProperties[0]..
            propertyInfo.SetValue(vm, $"This is {propertyInfo.Name} property");
        }

        Console.WriteLine(vm.Msg1);
        Console.WriteLine(vm.Msg2);
    }
}

public class MessagesViewModel
{
    string _msg1;
    string _msg2;
    public string Msg1 { get => _msg1; set => _msg1 = value; }
    public string Msg2 { get => _msg2; set => _msg2 = value; }
}

If this type of solution fits, you can wrap it with an indexer, sort the array to match the index and the Msg[num].

Join a community of over 2.6m developers to have your questions answered on Setting Column Values to an array in my view model - using  Sharing resources: You can extend LiveData object using the singleton pattern to wrap system services so that they can be shared in your app. Android App Setup: So, enough of this theory lets see how we can use this in our Android app. For to use LiveData add the following dependency in the build.gradle file.

Dealing with Unexpected Values by Adding Them to the View Model <scripts var I changed the view model so that the items array is an observable item using  how can i copy an array from controller viewModel I am just copying the array from viewModel which I got from resolve in the state. It's not working as I thought.

The simplest way to populate a ListView involves using an array of strings: properties of some CLR object, such as a class in your viewmodel. "Can I use" provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers. The site was built and is maintained by Alexis Deveria, with occasional updates provided by the web development community. The design used as of 2014 was largely created by Lennart Schoors. FAQ

An observable array behaves like a regular array but also provides a set ofnew features thatallows managing alist of elements inside a ViewModel. The most  In this tutorial, we looked at how to pass data from the Controller to View using ViewModel. You can use the dynamic property of the ViewBag or use the Special Property Model provided by the ViewData. If you use the Model property, then you can make use of the @model directive to declare the strongly typed View.

Comments
  • Just a reminder. For the bounty I am looking for an alternate answer to LadderLogic's so I can have something else to consider. If the alternate is better explained or seems more appropriate I will accept that one.
  • I am a little confused and need some clarification based on my understanding. The original model example has individual properties but then you mention something about arrays. I got lost with the jump from the individual properties to arrays. So are you asking if you can access those properties the same way you would with an array?
  • Currently I am declaring all of the messages like this, public string Msg1 and public string Msg1. But what if I have 30 messages Msg1, Msg2 .. Msg30. I would like to be able to declare these with just an array and not have to repeat "public string Msg1" and "public string Msg1 { get => _msg1; set => SetProperty(ref _msg1, value); }" 30 times.
  • The answer given mentions "// INotifyPropertyChanged and SetProperty implementation goes here" but I have already given my SetProperty code so I am wondering where this fits in. What I would like is an answer that's nicely formatted and that would show me (and others) just the lines of code that would be needed. I think maybe 90% of the work has been done but the existing answer doesn't look very clear to me so I am hoping for something that I can try cutting and pasting into my code (or other people can cut paste) that will do what's needed. Hope this makes sense.
  • Yes this clarifies most of my concerns / confusion.
  • Can you show how this could be used using the SetProperty method that's in the question so I can get a better understanding of what you mean. Also can you show if I can use the get => and set => shortcuts. Thanks
  • edited with SetProperty. I'd recommend implementing INotifyPropertyChanged in the base class and reusing them instead of re-writing. Or, if you're still at the infancy of the project, I recommend looking up an existing MVVM framework that does this for you. One of my favorites is the Prism.
  • Can you explain (or add to the code), what you mean by // INotifyPropertyChanged and SetProperty implementation goes here } Thanks
  • Hi, I don't want to show the array as a list. I have a XAML page where I am using vm.Msg[0] in one place, vm.Msg[1] in another place etc. I don't want to call "vm.MessagesArray.Add("B");" I want to be able to assign a value of "B" to an element in an array. For example vm.Msg[25] = "BBB";
  • @Alan2 Can you show the XAML Code, because only after knowing how do you want to presnt the data we can help you. What is the control you are binding to.. if you are binding it to textblock or label then even in XAML you have write the code 30 time, Please explain more
  • Once the array has been created and if I have added vm.MessagesArray[25] = "Z", then can I later change it to vm.MessagesArray[25] = "ZZZZ" and have what appears on the screen change? This is what I am looking for.
  • @Alan2 check the new updated answer, mentioned below "Update 27 October 2018", yest if you set vm.MessagesArray[25] = "Your new value it will update automatically"