C# 形式:IValueConverter只工作一次

C# 形式:IValueConverter只工作一次,c#,xaml,xamarin,mvvm,xamarin.forms,C#,Xaml,Xamarin,Mvvm,Xamarin.forms,我试图通过更改选定标签的颜色来控制标签的背景色。我遵循MVVM模式,我的实现方式如下: 在该模型中,我创建了一个带有get和set的布尔值,它必须检测是否选中了listview中的某个项选定的公共布尔值{get;set;} 在我看来,我将background color属性绑定到布尔值,并将IValueConverter设置为转换器 在ViewModel中,我实现了get和set 它似乎只检查一次,因为背景色始终为白色。我用转换器中的断点检查了它,它只在列表启动时被调用,而在项目更新时不会被调用

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

  • 在该模型中,我创建了一个带有get和set的布尔值,它必须检测是否选中了listview中的某个项<代码>选定的公共布尔值{get;set;}

  • 在我看来,我将background color属性绑定到布尔值,并将IValueConverter设置为转换器

  • 在ViewModel中,我实现了get和set
  • 它似乎只检查一次,因为背景色始终为白色。我用转换器中的断点检查了它,它只在列表启动时被调用,而在项目更新时不会被调用

    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>
    

    我已经为转换器添加了

    让我们看看您的视图

    <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>
    
    为了简单起见,我制作了
    StandingsListSource
    a,以消除跟踪其重新分配的需要

    现在,由于
    ListView.SelectedItem
    也已绑定,我们需要某种方式通知
    ListView
    所选项目已从代码隐藏中更新。输入前面提到的文档中的第二条建议:

    如果项目本身的属性在运行时发生更改,则 集合中的项应实现INotifyPropertyChanged 使用
    属性已更改
    事件

    这有两个含义:

    • 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; } 
      }
      
      类数据源:INotifyPropertyChanged
      {
      公共ObservableCollection项{get;}=new ObservableCollection();
      公共历史标准数据选择编辑项
      {
      //有关选择的信息存储在项本身中,请使用Linq查找单个匹配项
      get=>Items.Where(x=>x.Selected).SingleOrDefault();
      设置
      {
      //重置以前的选择
      var item=SelectedItem;
      如果(项!=null)
      item.Selected=false;
      //将新项目标记为选中,提升HistoricalStandingItem.PropertyChanged
      if(值!=null)
      value.Selected=true;
      //通知观察者SelectedItem已更改
      PropertyChanged?.Invoke(这是新的propertychangedventargs(nameof(SelectedItem));
      }
      }
      //从InotifyProperty更改
      公共事件属性更改事件处理程序属性更改;
      公共数据源()
      {
      //Helper ICommand用于将新项附加到HistoricalStandingData
      AddNew=new命令(()=>
      {
      var item2=new HistoricalStandingsData(DateTime.Now.ToString());
      //追加,通知观察者集合已更改。
      增加(第2项);
      //设置为已选择,重置以前的选择
      选择editem=item2;
      });
      }
      public ICommand AddNew{get;}
      }
      

    AddNew
    命令是可选的,我添加它是为了测试目的。

    对集合的选择跟踪通常通过CollectionView完成。请参阅我编写的旧MVVM简介中的第8点:我假设您的数据源需要实现“INotifyPropertyChanged”,以便在属性更新时通知burbs。@orhtej2:谢谢您的建议,但这不起作用:(@Hudhud的事情是,您所做的只是将绑定源代码更新到ListView作为一个整体,您需要做的是在
    HistoricalStandingsData
    的各个属性上实现
    INotifyPropertyChanged
    ,因为这是您的转换器所绑定的。您应该使用一个baseViewModel来实现所有ViewModel,因为e是对的。这是使用baseViewModel最简单的方法:)
    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;
    }
    
    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; } 
    }