如何在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].Value
, Msg[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
字符串,因为它已经实现了INotifyCollectionChanged
和INotifyPropertyChanged
。 RelayCommand
只是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
绑定到ObservableCollection
的Elements
。 按下按钮时,将调用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]。