【问题标题】:How to trigger converter when ObservableCollection is changed in WPF using MVVM?使用 MVVM 在 WPF 中更改 ObservableCollection 时如何触发转换器?
【发布时间】:2020-07-30 17:35:32
【问题描述】:

我有两个这样的列表:

<ListView Name="listView" Height="300" ItemsSource="{Binding Resultados}">
   <ListView.View>
      <GridView>
         <GridViewColumn>
            <GridViewColumn.CellTemplate>
               <DataTemplate>
                  <CheckBox
                     IsChecked="{Binding Path=CommandParameter, RelativeSource={RelativeSource Self}, Converter={StaticResource CheckBoxCeldaConverter}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
                     Command="{Binding Path=DataContext.SeleccionarCeldaCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" 
                     CommandParameter="{Binding}"/>
               </DataTemplate>
            </GridViewColumn.CellTemplate>
         </GridViewColumn>
         <GridViewColumn Header="EDIFICIO" DisplayMemberBinding="{Binding NIVEL.PASILLO.EDIFICIO.NOMBRE}"/>
         <GridViewColumn Header="PASILLO" DisplayMemberBinding="{Binding NIVEL.PASILLO.NOMBRE}"/>
         <GridViewColumn Header="NIVEL" DisplayMemberBinding="{Binding NIVEL.NOMBRE}"/>
         <GridViewColumn Header="CELDA" DisplayMemberBinding="{Binding CELDA.NOMBRE}"/>
         <GridViewColumn Header="CLASIFICACIÓN" DisplayMemberBinding="{Binding CELDA_CATEGORIA.NOMBRE}"/>
         <GridViewColumn>
            <GridViewColumn.CellTemplate>
               <DataTemplate>
                  <Button
                     Command="{Binding Path=DataContext.MostrarDiasVisitasCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     CommandParameter="{Binding CELDA.CELDA_DIAS_VISITA}"
                     IsEnabled="{Binding TieneDiasVisita}">
                     <materialDesign:PackIcon Kind="Eye"/>
                  </Button>
               </DataTemplate>
            </GridViewColumn.CellTemplate>
         </GridViewColumn>
      </GridView>
   </ListView.View>
</ListView>
<ListView Name="listView2" Height="300" ItemsSource="{Binding CeldasSeleccionadas}" Grid.Column="1" Margin="20 0 0 0">
   <ListView.View>
      <GridView>
         <GridViewColumn Header="EDIFICIO" DisplayMemberBinding="{Binding NIVEL.PASILLO.EDIFICIO.NOMBRE}"/>
         <GridViewColumn Header="PASILLO" DisplayMemberBinding="{Binding NIVEL.PASILLO.NOMBRE}"/>
         <GridViewColumn Header="NIVEL" DisplayMemberBinding="{Binding NIVEL.NOMBRE}"/>
         <GridViewColumn Header="CELDA" DisplayMemberBinding="{Binding CELDA.NOMBRE}"/>
         <GridViewColumn Header="CLASIFICACIÓN" DisplayMemberBinding="{Binding CELDA_CATEGORIA.NOMBRE}"/>
         <GridViewColumn>
            <GridViewColumn.CellTemplate>
               <DataTemplate>
                  <Button
                     Command="{Binding Path=DataContext.BorrarCeldaCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
                     CommandParameter="{Binding}">
                     <materialDesign:PackIcon Kind="Delete"/>
                  </Button>
               </DataTemplate>
            </GridViewColumn.CellTemplate>
         </GridViewColumn>
      </GridView>
   </ListView.View>
</ListView>

在第一个列表中,元素有一个 CheckBox,如果按下 CheckBox,则从第二个列表中添加或删除元素,它的 ItemSource 绑定到 ViewModel 中的 CeldasSeleccionadas ObservableCollection。我在第一个列表的 isChecked 属性的绑定中有一个转换器,它检查元素是否存在于 CeldasSeleccionadas ObservableCollection 中,如果存在,则将 isChecked 属性设置为 true。这是因为如果用户更新视图,选定的值会保持选中状态。

现在,在第二个列表中,即绑定到 CeldasSeleccionadas 的列表中,我有一个按钮可以从 CeldasSeleccionadas ObservableCollection 中删除元素。这工作正常,我唯一的问题是,当我从 CeldasSeleccionadas ObservableCollection 中删除元素时,第一个列表中的 CheckBox 将 isChecked 属性保持为真,转换器不会再次被调用,并且不检查元素是否存在于 CeldasSeleccionadas ObservableCollection .

CeldasSeleccionadas 在 ViewModel 中看起来像这样:

public ObservableCollection<CeldaModel> CeldasSeleccionadas { get; set; } = new ObservableCollection<CeldaModel>();

这是向 CeldasSeleccionadas 添加元素的命令:

private ICommand _SeleccionarCeldaCommand;
public ICommand SeleccionarCeldaCommand {
  get {
    if (_SeleccionarCeldaCommand == null) _SeleccionarCeldaCommand = new RelayCommand(param =>SeleccionarCelda((CeldaModel) param));
    return _SeleccionarCeldaCommand;
  }
}
void SeleccionarCelda(CeldaModel celda) {
  if (CeldasSeleccionadas.Contains(celda)) {
    CeldasSeleccionadas.Remove(celda);
  }
  else {
    CeldasSeleccionadas.Add(celda);
  }
}

这用于删除元素:

private ICommand _BorrarCeldaCommand;
public ICommand BorrarCeldaCommand {
  get {
    if (_BorrarCeldaCommand == null) _BorrarCeldaCommand = new RelayCommand(param =>BorrarCelda((CeldaModel) param));
    return _BorrarCeldaCommand;
  }
}
void BorrarCelda(CeldaModel celda) {
  CeldasSeleccionadas.Remove(celda);
}

转换器如下所示:

public class CheckBoxCeldaConverter : Freezable, IValueConverter
    {
        public CheckBoxCeldaConverter()
        {
            MyCollection = new ObservableCollection<CeldaModel>();
        }

        protected override Freezable CreateInstanceCore()
        {
            return new CheckBoxCeldaConverter();
        }


        public static readonly DependencyProperty MyCollectionProperty =
        DependencyProperty.Register(nameof(MyCollection),
            typeof(ObservableCollection<CeldaModel>), typeof(CheckBoxCeldaConverter),
            new FrameworkPropertyMetadata(null));

        public ObservableCollection<CeldaModel> MyCollection
        {
            get { return GetValue(MyCollectionProperty) as ObservableCollection<CeldaModel>; }
            set { SetValue(MyCollectionProperty, value); }
        }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (MyCollection.Contains(value as CeldaModel))
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

我从 User.Resources 内部的视图中调用它,如下所示:

<UserControl.Resources>
        <local:CheckBoxCeldaConverter MyCollection="{Binding CeldasSeleccionadas}" x:Key="CheckBoxCeldaConverter"/>
</UserControl.Resources>

添加或删除 CeldasSeleccionadas ObservableCollection 中的项目时如何触发转换器?

【问题讨论】:

  • CeldaModel 应该知道“我被选中了”。这使得它变得如此简单:将复选框绑定到 CeldaModel 中的“Selected”布尔值,让您使用命令参数作为 CeldaModel 它自己并使用它来制作您想要的东西。
  • 此外,您的转换器对我来说似乎太乱了,恕我直言。为什么你必须引用“CeldasSeleccionadas”,如果你让 CeldaModel “更智能”,你就不需要它了。
  • 这个方法不错,让我试试吧。
  • 更多! “Celdas”的一个实例和两个不同的 ICollectionView(“todas” y “seleccionadas”),并且您将每个列表视图绑定到您想要的每个视图,只有一个源!更符合 MVVM 并遵循 DBMSish 方式来管理信息,如果你不知道 ICollectionView,让我来介绍你这个奇妙的世界:marlongrech.wordpress.com/2008/11/22/icollectionview-explained 但是你必须考虑你是想要两个列表还是两个视图。这是第一步
  • 这不是我正在寻找的答案,但我认为这比我正在做的要好得多。还要感谢有关 ICollectionViews 的信息!您能否将您的评论作为答案,以便我选择它作为答案?

标签: c# wpf mvvm


【解决方案1】:

阅读所有内容,它比以前更直观和解释。

如果 CeldaModel 知道“我被选中”,代码会更容易,让您将复选框绑定到“已选”布尔属性。

这样你就可以避免转换器。

但我认为还有更好的选择。

你有“结果”。

然后在你的 ViewModel 中创建另外两个属性 ICollectionView

public class MasterViewModel : ViewModelBase //example{

    ObservableCollection<CeldaModel> Resultados{get; set;}

    ICollectionView TodosResultados{get; set;}

    ICollectionView ResultadosSeleccionados {get; set;} //You create ICollectionView and filter it by "Selected" property in CeldaModel

    ...

}

将一个列表视图绑定到“TodosResultados”,另一个绑定到“ResultadosSeleccionados”

如果您已正确绑定所有内容(带有 Selected 的复选框,带有 ICollectionView 的 ListViews),您就不必担心任何事情。一切都会像魅力一样发挥作用。至少在表面上。

无需创建“命令”(除非命令在后面的模型中产生某些东西——不要弄乱 ViewModel)。

警告:如果你只想要“视图”,这很好,如果你想要两个 ObservableCollection 也是有效的。

PS:你应该将“CeldaModel”命名为“CeldaViewModel”吗?在纯 MVVM 中,模型类不得以任何方式与视图交互,即使您将其包含在集合中。

【讨论】:

  • 非常感谢您的帮助!只是一个问题,你指的是什么模型不能与视图交互?在这种情况下,模型与视图交互的方式是什么?
  • 如果你使用的是 CeldaModel 的 ObservableCollection,视图是在与模型交互,除非 CeldaModel 的名称错误,应该是 CeldaViewModel。最“规范”的方法是将 celdamodel 封装在 CeldaViewModel 类中。如果 CeldaModel 来自模型层。
  • CeldaModel 是一种外观类。它具有与原始模型 CELDA 相同的字段,但我添加了更多字段,例如您建议的 ImSelected 字段。
  • 然后使用“CeldaViewModel”名称,恕我直言。
  • 我会接受这个建议!非常感谢您的帮助:)
猜你喜欢
  • 2010-12-19
  • 2011-11-10
  • 1970-01-01
  • 2011-01-23
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-20
相关资源
最近更新 更多