c# - 如何更改 ViewModel 属性并将其保存到我的所有页面?
问题描述
我有默认MasterDetailPage
项目,其中有一个ItemPage
和一个ItemDetailPage
(当我单击一个项目时)。在我的ItemDetailPage
我将Item.Text
属性更改为batata
并且我期望ItemPage Item
文本会更改,但它没有。如何更改我的属性ItemDetailPage
并更改它ItemPage
?
ItemPage Xaml 代码
<StackLayout>
<ListView x:Name="ItemsListView"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
RefreshCommand="{Binding LoadItemsCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
CachingStrategy="RecycleElement"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10">
<Label Text="{Binding Text}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<Label Text="{Binding Description}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class ItemsPage : ContentPage
{
ItemsViewModel viewModel;
ItemDetailViewModel itemViewModel;
public ItemsPage()
{
InitializeComponent();
BindingContext = viewModel = new ItemsViewModel();
}
async void OnItemSelected(object sender, SelectedItemChangedEventArgs args)
{
this.BindingContext = null;
var item = args.SelectedItem as Item;
if (item == null)
return;
if(itemViewModel == null)
itemViewModel = new ItemDetailViewModel(item);
await Navigation.PushAsync(new ItemDetailPage(itemViewModel));
// Manually deselect item.
ItemsListView.SelectedItem = null;
this.BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
if (viewModel.Items.Count == 0)
viewModel.LoadItemsCommand.Execute(null);
}
}
<StackLayout Spacing="20" Padding="15">
<Label Text="Text:" FontSize="Medium" />
<Label Text="{Binding Item.Text}" FontSize="Small"/>
<Label Text="Description:" FontSize="Medium" />
<Label Text="{Binding Item.Description}" FontSize="Small"/>
</StackLayout>
public partial class ItemDetailPage : ContentPage
{
ItemDetailViewModel viewModel;
public ItemDetailPage(ItemDetailViewModel viewModel)
{
InitializeComponent();
viewModel.Item.Text = "batata";
BindingContext = this.viewModel = viewModel;
}
}
项目视图模型
public class ItemsViewModel : BaseViewModel
{
public ObservableCollection<Item> Items { get; set; }
public Command LoadItemsCommand { get; set; }
public ItemsViewModel()
{
Title = "Browse";
Items = new ObservableCollection<Item>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
MessagingCenter.Subscribe<NewItemPage, Item>(this, "AddItem", async (obj, item) =>
{
var newItem = item as Item;
Items.Add(newItem);
await DataStore.AddItemAsync(newItem);
});
}
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try
{
Items.Clear();
var items = await DataStore.GetItemsAsync(true);
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
项目详细视图模型
public class ItemDetailViewModel : BaseViewModel
{
public Item Item { get; set; }
public ItemDetailViewModel(Item item = null)
{
Title = item?.Text;
Item = item;
}
}
产品型号:
public class Item
{
public string Id { get; set; }
public string Text { get; set; }
public string Description { get; set; }
}
解决方案
问题是您正在绑定到Item
不通知其更改的类的属性,例如Text
和Description
。因此,当属性的值发生更改时,视图中的值不会更新。
您可以通过实施INotifyPropertyChanged
解决它Item
:
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Id { get; set; }
private string _text;
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
NotifyPropertyChanged();
}
}
}
private string _description;
public string Description
{
get => _description;
set
{
if (_description != value)
{
_description = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
但是,我还建议您不要绑定到嵌套属性,因为这可能会出现问题。因此,无需绑定到Item.Text
and Item.Description
,而是直接绑定到视图模型中的属性。
例如,您可以创建两个名为and的ItemDetailViewModel
属性:ItemText
ItemDescription
public string ItemText => Item?.Text;
public string ItemDescription => Item?.Description;
<!-- Updated bindings -->
<StackLayout Spacing="20" Padding="15">
<Label Text="Text:" FontSize="Medium" />
<Label Text="{Binding ItemText}" FontSize="Small"/>
<Label Text="Description:" FontSize="Medium" />
<Label Text="{Binding ItemDescription}" FontSize="Small"/>
</StackLayout>
为了确保这些属性在相应属性发生更改时通知它们的Item
更改,您需要订阅该事件Item.PropertyChanged
,以便传播更新:
// (Assuming the base viewmodel implements INotifyPropertyChanged the same way than Item)
public class ItemDetailViewModel : BaseViewModel
{
private Item _item;
public Item Item
{
get => _item;
set
{
if (_item != value)
{
if (_item != null)
{
// Unsubscribe from the old item
_item.PropertyChanged -= OnItemPropertyChanged;
}
_item = value;
NotifyPropertyChanged();
if (value != null)
{
// Subscribe to the new item
value.PropertyChanged += OnItemPropertyChanged;
}
// Since the entire item has changed, we notify
// about changes in all the dependant properties
Title = Item?.Text;
NotifyPropertyChanged(nameof(ItemText));
NotifyPropertyChanged(nameof(ItemDescription));
}
}
}
public string ItemText => Item?.Text;
public string ItemDescription => Item?.Description;
public ItemDetailViewModel(Item item = null)
{
Item = item;
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// When a property of the item changes, we propagate the changes
// to the properties of this viewmodel that now depend on it
if (e.PropertyName == nameof(Item.Text))
{
Title = Item?.Text;
NotifyPropertyChanged(nameof(ItemText));
}
else if (e.PropertyName == nameof(Item.Description))
{
NotifyPropertyChanged(nameof(ItemDescription));
}
}
}
这段代码有点乱,可以改进以使其更优雅,但我希望你能明白。
推荐阅读
- c# - 使用 OpenTK 在 SkiaSharp 中创建模板附件时出错
- html - 这个 css 选择器有一个简短的形式吗
- html - 下拉值在 Windows 上不可见
- reactjs - 使用 React Navigation 在 Stacknavigator 中使用 hoc 包裹屏幕
- c# - 为什么 if(dynamic && string) 会编译?
- android - findFirstAsync() 返回“无效对象”,但 findFirst() 或 findAllAsync() 有效
- c# - 使用 POS .Net 使用 datalogic heron HD3430 扫描仪捕获图像
- java - 是否可以在 Hashmap 中获取先前存储的值?
- javascript - 如何使用正则表达式返回查询参数?
- c# - 如何将使用半列拆分数据的文本文件解析为数据表