Xamarin.forms 尝试将值转换器与OnBindingContextChanged的ListView(循环缓存策略)一起使用
不确定我的问题格式是否恰当,如果没有,请告诉我。但我试图简单地将背景颜色绑定到viewcell中的值。事实上,我有这个工作。问题是当我更新一个值时,我看不到背景颜色的变化。实现有点复杂,但下面是我的代码 ViewCell(OnBindingContextChanged) 所以本质上我只是建立我的布局。我决定只发布在我的Xamarin.forms 尝试将值转换器与OnBindingContextChanged的ListView(循环缓存策略)一起使用,xamarin.forms,Xamarin.forms,不确定我的问题格式是否恰当,如果没有,请告诉我。但我试图简单地将背景颜色绑定到viewcell中的值。事实上,我有这个工作。问题是当我更新一个值时,我看不到背景颜色的变化。实现有点复杂,但下面是我的代码 ViewCell(OnBindingContextChanged) 所以本质上我只是建立我的布局。我决定只发布在我的OnBindingContextChanged方法中设置绑定的相关代码。如果有人需要任何其他代码,我很乐意添加它,只是不知道它是否相关。我的ViewCell类是一个简单的类,它只继
OnBindingContextChanged
方法中设置绑定的相关代码。如果有人需要任何其他代码,我很乐意添加它,只是不知道它是否相关。我的ViewCell类是一个简单的类,它只继承了ViewCell
这是我的转换器:
public class GridCellBackgroundColorConverter : Xamarin.Forms.IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
try
{
var cell = (XamarinMobile.ViewModels.GridCellViewModel)value;
if(cell.HasRead)
{
//return with shadow
return Xamarin.Forms.Color.FromRgba(0,0,0,0.6);
} else
{
//return no shadow
return Xamarin.Forms.Color.FromRgba(0, 0, 0, 0.0);
}
} catch(System.Exception ex)
{
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
简单。它起作用了。现在是棘手的部分。我描述的网格是一个包含故事单元的列表视图。用户将单击一个图像,这将把他们带到一个故事页面。当用户在故事页面中时,他们可以返回网格转到另一个故事,或者向左或向右滑动,这样就可以转到另一个故事。当用户从我们的网格转到一个故事页面时,单元格会得到很好的更新。但是如果一个用户不是从网格中滑动到另一个故事,那就是我的问题所在。在我的故事页面中,我有一个逻辑,可以遍历网格单元格,找到你当前所在的故事(你浏览到的故事),并查看它是否在网格中,如果它在网格中,我会更新单元格的HasRead
属性。因此:
//find the cell in the grid (if exists)
ViewModels.GridCellViewModel cell = App.GridCells.Where(x => x.StoryId == App.Story.StoryId).FirstOrDefault();
if (cell != null)
{
cell.HasRead = true;
}
这是可行的,但是。。。它不会触发值转换器来更改属性。我做错了什么?如何获取它,以便更新属性并使其触发值转换器 我猜您的转换器没有触发,因为您在技术上绑定了viewcell本身,而不是HasRead属性。当您设置HasRead时,它将(假设它正在实现INotifyPropertyChanged)触发一个PropertyChangedEvent,它将触发绑定并调用值转换器。但是,由于绑定指向viewcell本身,因此只有当该对象发生更改时才会触发,并且忽略该对象上其他位置的属性更改 一种可能的解决方案是将绑定更改为指向HasRead(而不是“.”),并将转换器更新为直接使用布尔值,而不是使用viewcell。这对于转换器来说是一个更好的实践 这就是说,这并没有真正遵循通常推荐用于xamarin表单应用程序的mvvm模式。我的建议是使用一个viewmodel,该viewmodel的属性可以保存您的故事模型(如果需要逻辑,可以包装在它们自己的故事视图模型中),并确保VM和模型类实现INotifyPropertyChanged。将VM作为页面的datacontext,将列表绑定到listview源,listview itemtemplate内容将绑定到每个故事。每个故事都可以有一个HasRead属性,该属性通过更新的转换器绑定到背景色 像这样:
<ContentPage
x:Class="Stack_Stories.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Stack_Stories">
<ContentPage.BindingContext>
<local:StoriesViewModel x:Name="VM" />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<local:StoryReadBackgroundColorConverter x:Key="HasReadColor" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView ItemsSource="{Binding Stories}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid x:Name="StoryGrid" BackgroundColor="{Binding HasRead, Converter={StaticResource HasReadColor}}">
<Button Command="{Binding ToggleReadCommand}" Text="{Binding Name}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
公共类StoryViewModel:INotifyPropertyChanged
{
私有字符串_name=“”;
公共字符串名
{
获取{return\u name;}
设置{u name=value;OnPropertyChanged();}
}
private bool\u hasRead=false;
公共图书馆
{
获取{return\u hasRead;}
设置{u hasRead=value;OnPropertyChanged();}
}
专用命令_toggleRead;
公共命令切换READCOMMAND
{
得到
{
返回\u切换读取
??(_toggleRead=新命令(()=>HasRead=!HasRead));
}
}
公共事件属性更改事件处理程序属性更改;
受保护的OnPropertyChanged无效([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共类StoriesViewModel:INotifyPropertyChanged
{
公共故事视图模型()
{
//添加示例故事
添加(新的StoryViewModel{Name=“First Story”});
添加(新的StoryViewModel{Name=“Second Story”,HasRead=true});
}
私有ObservableCollection_stories=新ObservableCollection();
公开收集故事
{
获取{return\u stories;}
设置{u stories=value;OnPropertyChanged();}
}
公共事件属性更改事件处理程序属性更改;
受保护的OnPropertyChanged无效([CallerMemberName]字符串propertyName=null)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(propertyName));
}
}
公共类StoryReadBackgroundColorConverter:IValueConverter
{
公共对象转换(对象值、类型targetType、对象参数、CultureInfo区域性)
{
如果(!(值为bool))返回null;
返回(bool)值?Color.FromRgba(0,0,0,0.6):Color.FromRgba(0,0,0,0.0);
}
公共对象转换回(对象值、类型targetType、对象参数、CultureInfo区域性)
{
抛出新的NotImplementedException();
}
}
什么都没有?该死对如何通过onbindingcontext更改绑定viewcell一点都不了解?是的,我将它绑定到了我的viewmodel,瞧!工作得很有魅力。虽然有点混乱,因为在ViewCell中设置布局很奇怪,但是在更新视图模型时会发生变化。尽管如此,我还是采取了类似的方法,因此我标记了您的答案:)
<ContentPage
x:Class="Stack_Stories.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Stack_Stories">
<ContentPage.BindingContext>
<local:StoriesViewModel x:Name="VM" />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<local:StoryReadBackgroundColorConverter x:Key="HasReadColor" />
</ResourceDictionary>
</ContentPage.Resources>
<ListView ItemsSource="{Binding Stories}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid x:Name="StoryGrid" BackgroundColor="{Binding HasRead, Converter={StaticResource HasReadColor}}">
<Button Command="{Binding ToggleReadCommand}" Text="{Binding Name}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class StoryViewModel : INotifyPropertyChanged
{
private string _name = "";
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(); }
}
private bool _hasRead = false;
public bool HasRead
{
get { return _hasRead; }
set { _hasRead = value; OnPropertyChanged(); }
}
private Command _toggleRead;
public Command ToggleReadCommand
{
get
{
return _toggleRead
?? (_toggleRead = new Command(() => HasRead = !HasRead));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class StoriesViewModel : INotifyPropertyChanged
{
public StoriesViewModel()
{
// add sample stories
Stories.Add(new StoryViewModel { Name = "First Story" });
Stories.Add(new StoryViewModel { Name = "Second Story", HasRead=true });
}
private ObservableCollection<StoryViewModel> _stories = new ObservableCollection<StoryViewModel>();
public ObservableCollection<StoryViewModel> Stories
{
get { return _stories; }
set { _stories = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class StoryReadBackgroundColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool)) return null;
return (bool)value ? Color.FromRgba(0, 0, 0, 0.6) : Color.FromRgba(0, 0, 0, 0.0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}