当集合中的属性更改时在转换器上触发“ConvertBack”?

Posted

技术标签:

【中文标题】当集合中的属性更改时在转换器上触发“ConvertBack”?【英文标题】:Triggering "ConvertBack" on a converter when a property in a collection changes? 【发布时间】:2021-07-22 15:13:07 【问题描述】:

我正在 MVVM WPF 应用程序中使用转换器从标记的枚举中创建一个“即时”复选框列表。绑定的属性是一个 int,它表示一个标记的枚举。这是我的转换器,它从 int 转换为 ObservableDictionary

public class AccessListConverter : IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    
        int realValue;
        switch (value)
        
            case int intValue:
                realValue = intValue;
                break;
            case AccessEnum enumValue:
                realValue = (int)enumValue;
                break;
            default:
                return Binding.DoNothing;
         

        List<AccessEnum> accessComponents = Enum.GetValues(typeof(AccessEnum)).Cast<AccessEnum>().Where(r => ((AccessEnum)realValue & r) == r).Select(r => r).ToList();
        ObservableDictionary<int, bool> accessDictionary = new ObservableDictionary<int, bool>(Enum.GetValues(typeof(AccessEnum)).Cast<int>().Select(i => i).ToDictionary(i => i, i => accessComponents.Contains((AccessEnum) i)));
        accessDictionary.Remove(0);
        return accessDictionary;
    
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    
        if (!(value is Dictionary<int, bool> intDict))
        
            throw new NotImplementedException();
        
        return intDict.Sum(x => x.Value ? x.Key : 0);
    

关键是用户可以选择为属性启用哪些枚举。我用转换器设计会有意义,但是当勾选/未被勾选复选框时,我遇到了触发转换器的“Convectback”方法。

这是我在 Xaml 中的绑定:

<ItemsControl ItemsSource="Binding Access, UpdateSourceTrigger=PropertyChanged, Converter=StaticResource AccessListConverter" Margin="87,0,10,0">
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel CanVerticallyScroll="False" Margin="0"/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <CheckBox VerticalAlignment="Center" Margin="6,0,0,0" Content="Binding Key, Converter=StaticResource AccessStringConverter, UpdateSourceTrigger=PropertyChanged" 
                    IsChecked="Binding Value, UpdateSourceTrigger=PropertyChanged" />
    </DataTemplate>
</ItemsControl.ItemTemplate>

现在我们进入 Observable Dictionary 这就是我的 OBservableDictionary 实现的样子(不相关的部分被删除,其余部分缩小):

 [Serializable]
public class ObservableDictionary<TKey, TValue> : 
    ObservableCollection<ObservableKeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>

    public ObservableDictionary() 
    public ObservableDictionary(Dictionary<TKey, TValue> dictionary) : this()
    
        foreach (TKey key in dictionary.Keys)  Add(key, dictionary[key]); 
    
    public ObservableDictionary(ObservableDictionary<TKey, TValue> dictionary) : this()
    
        foreach (TKey key in dictionary.Keys)  Add(key, dictionary[key]); 
    

    public void Add(TKey key, TValue value)
    
        if (ContainsKey(key))  throw new ArgumentException("The dictionary already contains the key"); 
        Add(new ObservableKeyValuePair<TKey, TValue>()  Key = key, Value = value );
    

    public ICollection<TKey> Keys => (from i in ThisAsCollection() select i.Key).ToList();
    public ICollection<TValue> Values => (from i in ThisAsCollection() select i.Value).ToList();
    public TValue this[TKey key]
    
        get  if (!TryGetValue(key, out TValue result)) throw new ArgumentException("Key not found");  return result;  
        set  if (ContainsKey(key))  GetKvpByTheKey(key).Value = value;  else  Add(key, value);  
    

然后我实现了一个collectionChanged代码sn-p表单***

void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    
        if (e.NewItems != null) foreach (object item in e.NewItems) ((INotifyPropertyChanged) item).PropertyChanged += item_PropertyChanged; 
        if (e.OldItems != null) foreach (object item in e.OldItems)  ((INotifyPropertyChanged) item).PropertyChanged -= item_PropertyChanged;
    
    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    
        NotifyCollectionChangedEventArgs a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
        OnCollectionChanged(a);
    

并将CollectionChanged += TrulyObservableCollection_CollectionChanged; 添加到空的构造函数中。

但没有骰子。经过数小时的搜索,我仍然一无所获。 ConvertBack 永远不会被触发。我认为这可能是因为集合本身实际上没有改变,但我该如何强制更新?非常感谢任何帮助:)

编辑: 我可能已经以这条评论的形式找到了我自己问题的答案:https://***.com/a/59637445/3390519我会试一试!

【问题讨论】:

您是否尝试在 ItemsSource 绑定上添加 Mode=TwoWay?正如 MSDN 所述:默认情况下它是 OneWay 绑定。并阅读remarks。 好主意!不幸的是,没有工作。 好的,我再次阅读了您的问题,我认为您从错误的角度来处理这个问题。您需要一个标准的 ObservableCollection 类型的元组或自定义类,它具有 IsSelected 属性,然后您的 VM 将知道哪些项目被选中,哪些未选中,显然您也将拥有一个具有枚举值的属性。将使您的大部分代码更小,并且减少对自定义集合的干扰。 我可以尝试,但我不知道如何解决它?它仍然不会通知转换器。 ObservableDictionary 已经只是一个 ObservableCollection,那么有什么区别呢?我真的很想在视图模型中没有集合并坚持使用 int 属性。这就是这个问题的重点,但我想这是一个很好的备份解决方案:) 【参考方案1】:

我通过更改搜索参数解决了自己的问题!我没有搜索“当集合中的属性更改时在转换器上触发“ConvertBack”的解决方案?”,而是开始搜索“wpf - 将标志枚举转换为复选框列表”的解决方案,并找到了一个不错的解决方案!它不是动态的(枚举值必须手动更新),但对于我的目的来说已经足够了:) https://***.com/a/59637445/3390519

感谢您抽出时间 XAMIMAX :)

【讨论】:

以上是关于当集合中的属性更改时在转换器上触发“ConvertBack”?的主要内容,如果未能解决你的问题,请参考以下文章

更改集合视图的 isHidden 属性不适用于搜索栏取消按钮

启动时在 Anylogic 中更改 GIS 路由

JavaScript事件

使用箭头键浏览结果时在 jQuery UI 自动完成上触发“选择”事件

当输入的值以编程方式更改时触发更改事件

页面加载时触发CSS转换属性[重复]