如何在ViewModel中使用数组?

我的代码现在看起来像这样,每条消息有两行代码。 代码可以工作,但如果我有30个消息,我可以给每个值,然后我需要有60行代码来声明一切:

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

在C#中我分配给这些:

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

在XAML中,我将Text绑定到Msg1,将另一个Text绑定到Msg2

有人可以告诉我如何/如果我可以使用数组这样做,所以我会这样分配,希望所以数组的分配可以只用两行代替每行消息2行:

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

以供参考:

 public class ObservableObject : INotifyPropertyChanged { protected virtual bool SetProperty( ref T backingStore, T value, [CallerMemberName]string propertyName = "", Action onChanged = null) { if (EqualityComparer.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)); } 

您可以使用支持属性更改通知的索引创建一个简单的包装器类。

例如:

 public class Messages : ObservableObject { readonly IDictionary _messages = new Dictionary(); [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 + "]"); } } } 

并且,在视图模型中创建一个属性:

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

现在您可以将值设置或更新为:

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

XAML中的绑定与以下内容相同:

   

样本使用代码

XAML

         

代码隐藏,视图模型

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

在此处输入图像描述

数组不会引发属性更改事件。 您需要使用ObservableCollection ,它可以在集合发生更改时引发事件。 但是,当集合中的对象更改了它的值时,这不会引发事件。 您需要将对象(在本例中为字符串)包装为可以引发属性更改事件的类型。

像下面这样的东西会起作用:

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

你将绑定Msg[0].ValueMsg[1].Value等,

我假设您的给定示例正在运行并按预期工作(Atleast with 2 items)

查看 代码

假设您要将所有30条消息显示为列表。

  

您还应该正确设置DataContext, 如果需要任何帮助 ,请在下方注释

查看型号代码。

我们使用的是ObservableCollection而不是数组。 由于纯数组不支持正确的绑定function。

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

分配值

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

在赋值代码中MessagesArray = new ObservableCollection(); 分配一个ObservableCollection String的新对象

如果您是ObservableCollection新手,请将此视为string[]的包装,但实际上并非如此

SetProperty方法将告诉XAML View新的集合到达,因此UI将重新呈现列表。

当你调用vm.MessagesArray.Add("B"); 方法Add内部逻辑将告诉XAML View将一个新项添加到ObservableCollection以便视图可以使用新项重新呈现ListView

2018年10月27日更新

您可以使用以下任何方式创建自己的数组。 (不是全部)

  string[] dataArray = new string[30]; 

1.这将创建一个包含30个空值的数组

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

2.这将创建一个具有预定义值集的数组,最多可以达到30

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

3.这将创建一个包含空值的字符串数组,而不是String.Empty您可以放置​​任何字符串值。

选择以上任何一种方法

我推荐最后一个方法,然后你可以将它分配到一个Observable Collection中,如下所示。

 MessagesArray = new ObservableCollection(dataArray); 

现在的诀窍是

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

视图可能如下所示

   

不完全确定我有问题,但据我所知,最简单的方法是:

Viewmodel:

只需绑定到ObservableCollection字符串,因为它已经实现了INotifyCollectionChangedINotifyPropertyChangedRelayCommand只是ICommand一个实现,我假设你已经听说过它们,因为你正在做WPF MVVM。

 using System.Collections.ObjectModel; namespace WpfApp1 { public class MainWindowViewmodel { public ObservableCollection Messages { get; set; } public MainWindowViewmodel() { Messages = new ObservableCollection(); Messages.Add("My message!"); ChangeMessageCommand = new RelayCommand(ChangeMessageExcecute); } public RelayCommand ChangeMessageCommand { get; set; } private void ChangeMessageExcecute() => Messages[0] = "NEW message!"; } } 

风景:

在视图中,您可以将Textblocks绑定到ObservableCollectionElements 。 按下按钮时,将调用Command并更改窗口中的消息。

         

亲切的问候,误导

用reflection怎么样? 您可以询问名称为“Msg *”的字符串类型的所有公共属性。

例如:

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

如果这种类型的解决方案适合,您可以使用索引器对其进行包装,对数组进行排序以匹配索引和Msg [num]。