Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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));

}
like image 810
Alan2 Avatar asked Oct 22 '18 01:10

Alan2


1 Answers

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";
           });
}

enter image description here

like image 156
Sharada Gururaj Avatar answered Sep 28 '22 13:09

Sharada Gururaj