我有一个默认的MasterDetailPage
项目,其中有一个ItemPage
和一个ItemDetailPage
(当我单击一项时)。在我的ItemDetailPage
中,我将Item.Text
属性更改为batata
,并期望ItemPage Item
文本会更改,但没有更改。如何在ItemDetailPage
中更改属性,并在ItemPage
中也更改属性?
<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;
}
}
ItemsViewModel
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;
}
}
}
ItemsDetailViewModel
public class ItemDetailViewModel : BaseViewModel
{
public Item Item { get; set; }
public ItemDetailViewModel(Item item = null)
{
Title = item?.Text;
Item = item;
}
}
ItemModel:
public class Item
{
public string Id { get; set; }
public string Text { get; set; }
public string Description { get; set; }
}
答案 0 :(得分:2)
问题是您绑定到类Item
的属性,这些属性不会通知其更改,例如Text
和Description
。因此,更改属性值后,视图中的值不会更新。
您可以通过Item
中的implementing INotifyPropertyChanged
解决此问题:
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
和Item.Description
,只需直接绑定到视图模型中的属性即可。
例如,在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));
}
}
}
此代码有点混乱,可以对其进行改进以使其更优雅,但希望您能理解。
答案 1 :(得分:0)
一种解决方案是将其绑定到静态属性
public static class MyClass
{
public static string MyProperty { get; set; }
}
在xaml中添加对MyClass文件位置的引用,并绑定到静态属性
{x:Static local:MyClass.MyProperty}">