Xamarin.Forms:IValueConverter只能运行一次

时间:2018-01-12 15:49:06

标签: c# xaml xamarin mvvm xamarin.forms

我试图通过更改所选标签的颜色来控制标签的背景颜色。我遵循MVVM模式,我实现的方式如下:

  1. 在模型中,我创建了一个带有get和set的布尔值,它必须检测列表视图中的项是否被选中。 public boolean Selected {get; set;}

  2. 在我看来,我将背景颜色属性绑定到布尔值,并将IValueConverter设置为转换器

  3. 在ViewModel中,我实现了get和set
  4. 它似乎只检查一次,因为背景颜色总是白色。我在转换器中使用断点检查了它,并且只在启动列表时调用它,而不是在项目更新时调用。

    的IValueConverter:

    public class SelectedItemColorConverter : IValueConverter
        {
    
            #region IValueConverter implementation
    
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (value is bool)
                {
                    if ((Boolean)value)
                        return Color.Red;
                    else
                        return Color.White;
                }
                return Color.White;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
    
            #endregion
        }
    }
    

    这是ListView:

    <StackLayout x:Name="standingsStackLayout" IsVisible="False">
                    <ListView x:Name="standingsList" SeparatorColor="Black" ItemsSource="{Binding StandingsListSource}" SelectedItem="{Binding SelectedItem}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell>
                                    <Label x:Name="TournamentNameLabel" Text="{Binding TournamentName}" 
                                           TextColor="{StaticResource textColor}" HorizontalTextAlignment="Center" 
                                           VerticalTextAlignment="Center" 
                                           BackgroundColor="{Binding Selected, Converter={StaticResource colorConvert}}"/>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackLayout>
    

    ViewModel代码:

    public HistoricalStandingsData _selectedItem;
        public HistoricalStandingsData SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                if (_selectedItem != value)
                { 
                    if(_selectedItem != null)
                        _selectedItem.Selected = false;
    
                    _selectedItem = value;
    
                    if (_selectedItem != null)
                        _selectedItem.Selected = true;
    
    
                    TournamentLabelName = _selectedItem.TournamentName;
    
                    OnPropertyChanged(nameof(SelectedItem));
                    //OnPropertyChanged(nameof(_selectedItem.Selected));
                }
            }
        }
    

    我为转换器添加了<ContentPage.Resources>

1 个答案:

答案 0 :(得分:1)

让我们看一下你的观点

<StackLayout x:Name="standingsStackLayout" IsVisible="False">
    <ListView x:Name="standingsList" SeparatorColor="Black" ItemsSource="{Binding StandingsListSource}" SelectedItem="{Binding SelectedItem}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Label x:Name="TournamentNameLabel" Text="{Binding TournamentName}" 
                           TextColor="{StaticResource textColor}" HorizontalTextAlignment="Center" 
                           VerticalTextAlignment="Center" 
                           BackgroundColor="{Binding Selected, Converter={StaticResource colorConvert}}"/>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

我们可以看到这里发生了两个主要的数据绑定。首先,ListView的{​​{1}}属性绑定到视图模型的ItemsSource属性。有两件事可以改变这一点:StandingsListSource作为整体指向的对象或集合内容。

有关绑定的官方文档包含following to say regarding binding ListView.ItemsSource

  

StandingsListSource处理可能的更改非常复杂   动态发生在底层数据中,但只有在确定时才会发生   脚步。如果分配给ListView属性的项集合   运行期间ItemsSource更改的内容 - 即,是否可以添加项目   要从集合中删除,请使用ListView类   这些物品。 ObservableCollection实现了   ObservableCollection界面,INotifyCollectionChanged将安装   ListView事件的处理程序。

让我们这样做(我稍后将CollectionChanged类用作DataSource的完整实现:​​

BindingContext

为简单起见,我将public ObservableCollection<HistoricalStandingsData> StandingsListSource { get; } = new ObservableCollection<HistoricalStandingsData>(); 设为C# 6.0 readonly auto property,以消除跟踪其重新分配的需要。

现在,由于StandingsListSource也受到约束,我们需要一些方法来通知ListView.SelectedItem所选项目是从后面的代码更新的。输入之前提到的文档中的第二条建议:

  

如果项目的属性在运行时自身发生变化,那么   集合中的项目应实现ListView   接口和信号使用的变化到属性值   INotifyPropertyChanged事件。

这有两个含义:

  • PropertyChanged应该在其属性发生变化时通知,因为HistoricalStandingsData中的每一行都按ListView绑定到此属性:

    DataTemplate
  • 查看模型类应实现public class HistoricalStandingsData : INotifyPropertyChanged { public HistoricalStandingsData(string name) { this.TournamentName = name; } private bool selected; public bool Selected { get { return selected; } set { selected = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Selected))); } } public string TournamentName { get; } // From INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; } 以通知属性,在这种情况下INotifyPropertyChanged更改。

    SelectedItem

class DataSource : INotifyPropertyChanged { public ObservableCollection<HistoricalStandingsData> Items { get; } = new ObservableCollection<HistoricalStandingsData>(); public HistoricalStandingsData SelectedItem { // Information on selection is stored in items themselves, use Linq to find the single matching item get => Items.Where(x => x.Selected).SingleOrDefault(); set { // Reset previous selection var item = SelectedItem; if (item != null) item.Selected = false; // Mark new item as selected, raising HistoricalStandingItem.PropertyChanged if (value != null) value.Selected = true; // Notify observers that SelectedItem changed PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem))); } } // From INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; public DataSource() { // Helper ICommand used for appending new items to HistoricalStandingsData AddNew = new Command(() => { var item2 = new HistoricalStandingsData(DateTime.Now.ToString()); // Append, notifies observers that collection has changed. Items.Add(item2); // Set as selected, resetting previous selection SelectedItem = item2; }); } public ICommand AddNew { get; } } 命令是可选的,我将其添加用于测试目的。