不确定我是否正确格式化了问题,如果我没有,请告诉我。但我试图简单地将背景颜色绑定到我的viewcell中的值。实际上,我有这个工作。问题是当我更新一个值时,我看不到背景颜色的变化。实现有点复杂,但这是我的代码。
ViewCell(OnBindingContextChanged)
...
ShowReadOverlay.SetBinding(Xamarin.Forms.VisualElement.BackgroundColorProperty, new Xamarin.Forms.Binding(".", Xamarin.Forms.BindingMode.TwoWay, new XamarinMobile.Converters.GridCellBackgroundColorConverter(), null, null, null));
...
所以基本上我只是构建我的布局。我决定只发布在我的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;
}
这样可行,但......它不会触发值转换器来更改属性。我究竟做错了什么?我怎样才能获得它以便我可以更新属性,并让它触发我的值转换器?
答案 0 :(得分:0)
我的猜测是你的转换器没有触发,因为你在技术上绑定到了视单元本身,而不是HasRead属性。当你设置HasRead时,它会(假设它正在实现INotifyPropertyChanged)触发一个PropertyChangedEvent,它将触发绑定并调用值转换器。但是,由于绑定指向视单元本身,因此只有在更改时才会触发,并忽略该对象上其他位置的属性更改。
一种可能的解决方案是将绑定更改为指向HasRead(而不是'。'),并更新转换器以直接期望布尔值而不是接收视单元。无论如何,这对于转换器来说都是更好的做法。
也就是说,这并不是真正遵循通常为xamarin表单应用推荐的mvvm模式。我的建议是拥有一个viewmodel,它具有一个属性来保存你的故事模型(如果你需要逻辑那么包装在他们自己的StoryViewModels中)并确保VM和Model类实现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>
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();
}
}