尝试使用带有OnBindingContextChanged

时间:2017-04-18 14:57:23

标签: xamarin.forms

不确定我是否正确格式化了问题,如果我没有,请告诉我。但我试图简单地将背景颜色绑定到我的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;
            }

这样可行,但......它不会触发值转换器来更改属性。我究竟做错了什么?我怎样才能获得它以便我可以更新属性,并让它触发我的值转换器?

1 个答案:

答案 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();
    }
}