我的代码现在看起来像这样,每条消息有两行代码。该代码有效,但是如果我有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中,我将我的文本绑定到Msg1,将另一个文本绑定到Msg2
有人可以告诉我如何/是否可以对数组执行此操作,以便我可以这样分配,并希望数组的分配可以只用两行完成,而不是每条消息的两行:
vm.Msg[0] = "A";
vm.Msg[1] = "B";
供参考:
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));
}
答案 0 :(得分:7)
数组不会引发属性更改事件。您需要使用一个ObservableCollection
,它可以在更改集合后引发一个事件。但是,当集合内的对象更改其值时,这不会引发事件。您需要将对象(在这种情况下为字符串)包装为可以引发属性更改事件的类型。
类似以下的方法将起作用:
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); }
您将绑定到Msg[0].Value
,Msg[1].Value
等,
答案 1 :(得分:6)
您可以使用indexing that supports property change notification创建一个简单的包装器类。
例如:
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 + "]");
}
}
}
然后,在视图模型中创建一个属性,如下所示:
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中的绑定将与:
<Label Text="{Binding Msg[0]}" />
<Label Text="{Binding Msg[1]}" />
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>
代码隐藏的视图模型
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";
});
}
答案 2 :(得分:3)
我假设您给定的示例正在按预期方式运行和运行(Atleast有2个项目)
查看 代码。
假设您希望将所有30条消息显示为列表。
<ListView ItemsSource="{Binding MessagesArray}"/>
还应该正确设置DataContext,如果需要帮助,请在下面注释
查看模型代码。
我们使用的是 ObservableCollection 而不是数组。由于纯数组不支持适当的绑定功能。
private ObservableCollection<string> _messagesArray;
public ObservableCollection<string> MessagesArray
{
get { return _messagesArray; }
set { SetProperty(ref _messagesArray, value); }
}
分配值
MessagesArray = new ObservableCollection<string>();
vm.MessagesArray.Add("A");
vm.MessagesArray.Add("B");
在分配代码MessagesArray = new ObservableCollection<string>();
中分配一个ObservableCollection
的{{1}}新对象
如果您是
String
的新手,可以将其视为ObservableCollection
的包装,但实际上并非如此
string[]
方法将告诉SetProperty
视图新集合已到达,因此UI将重新呈现该列表。
当您调用XAML
内部的vm.MessagesArray.Add("B");
内部逻辑时,将告诉Add
视图。新项目已添加到XAML
中,因此视图可以重新呈现{{ 1}}和新项目。
您可以使用以下任何一种方法来创建自己的数组:任何。 (不是全部)
ObservableCollection
1。这将创建一个包含30个空值的数组
ListView
2。这将创建一个具有预定义值集的数组,您最多可以设置30个
string[] dataArray = new string[30];
3。这样会创建一个带有字符串的数组,该字符串包含空值,您可以放置任何字符串值来代替 string[] dataArray = { "A", "B", "C" }; //Go up to 30 items
。
选择以上任何一种方法
我建议使用最后一种方法,然后可以将其分配给如下所示的Observable Collection。
string[] dataArray = Enumerable.Repeat<string>(String.Empty, 30).ToArray();
现在的诀窍是
String.Empty
视图可能如下所示
MessagesArray = new ObservableCollection<string>(dataArray);
答案 3 :(得分:3)
不能完全确定我有这个问题,但是据我了解,最简单的方法是:
只需绑定到字符串ObservableCollection
,因为它已经实现了INotifyCollectionChanged
和INotifyPropertyChanged
。
RelayCommand
只是ICommand
的实现,我假设您在做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!";
}
}
在视图中,您可以将Textblocks
绑定到Elements
的{{1}}。当您按下按钮时,ObservableCollection
会被调用并更改窗口中的消息。
Command
亲切的问候, 误导
答案 4 :(得分:2)
使用反射怎么样? 您可以索取名称为“ 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]。