| File: System\Windows\Forms\DataBinding\RelatedCurrencyManager.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.ComponentModel; namespace System.Windows.Forms; /// <summary> /// Represents the child version of the System.Windows.Forms.ListManager /// that is used when a parent/child relationship exists in a System.Windows.Forms.DataSet. /// </summary> internal class RelatedCurrencyManager : CurrencyManager { private BindingManagerBase _parentManager; private PropertyDescriptor _fieldInfo; private static List<BindingManagerBase> IgnoreItemChangedTable { get; } = []; internal RelatedCurrencyManager(BindingManagerBase parentManager, string dataField) : base(dataSource: null) { Bind(parentManager, dataField); } [MemberNotNull(nameof(_parentManager))] [MemberNotNull(nameof(_fieldInfo))] internal void Bind(BindingManagerBase parentManager, string dataField) { Debug.Assert(parentManager is not null, "How could this be a null parentManager."); // Unwire previous BindingManagerBase UnwireParentManager(_parentManager); _parentManager = parentManager; _fieldInfo = parentManager.GetItemProperties().Find(dataField, ignoreCase: true)!; if (_fieldInfo is null || !typeof(IList).IsAssignableFrom(_fieldInfo.PropertyType)) { throw new ArgumentException(string.Format(SR.RelatedListManagerChild, dataField)); } finalType = _fieldInfo.PropertyType; // Wire new BindingManagerBase WireParentManager(_parentManager); ParentManager_CurrentItemChanged(parentManager, EventArgs.Empty); } private void UnwireParentManager(BindingManagerBase? bmb) { if (bmb is not null) { bmb.CurrentItemChanged -= ParentManager_CurrentItemChanged; if (bmb is CurrencyManager currencyManager) { currencyManager.MetaDataChanged -= ParentManager_MetaDataChanged; } } } private void WireParentManager(BindingManagerBase bmb) { if (bmb is not null) { bmb.CurrentItemChanged += ParentManager_CurrentItemChanged; if (bmb is CurrencyManager currencyManager) { currencyManager.MetaDataChanged += ParentManager_MetaDataChanged; } } } internal override PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[]? listAccessors) { PropertyDescriptor[] accessors; if (listAccessors is not null && listAccessors.Length > 0) { accessors = new PropertyDescriptor[listAccessors.Length + 1]; listAccessors.CopyTo(accessors, 1); } else { accessors = new PropertyDescriptor[1]; } // Set this accessor (add to the beginning) accessors[0] = _fieldInfo; // Get props return _parentManager.GetItemProperties(accessors); } /// <summary> /// Gets the properties of the item. /// </summary> public override PropertyDescriptorCollection GetItemProperties() { return GetItemProperties(listAccessors: null); } /// <summary> /// Gets the name of the list. /// </summary> internal override string GetListName() { string name = GetListName([]); if (name.Length > 0) { return name; } return base.GetListName(); } /// <summary> /// Gets the name of the specified list. /// </summary> protected internal override string GetListName(ArrayList? listAccessors) { if (listAccessors is null) { return string.Empty; } listAccessors.Insert(0, _fieldInfo); return _parentManager.GetListName(listAccessors); } private void ParentManager_MetaDataChanged(object? sender, EventArgs e) { // Propagate MetaDataChanged events from the parent manager OnMetaDataChanged(e); } private void ParentManager_CurrentItemChanged(object? sender, EventArgs e) { if (IgnoreItemChangedTable.Contains(_parentManager)) { return; } int oldlistposition = listposition; // we only pull the data from the controls into the backEnd. we do not care about keeping the lastGoodKnownRow // when we are about to change the entire list in this currencymanager. try { PullData(); } catch (Exception ex) { OnDataError(ex); } if (_parentManager is CurrencyManager currencyManager) { if (currencyManager.Count > 0) { // Parent list has a current row, so get the related list from the relevant property on that row. SetDataSource(_fieldInfo.GetValue(currencyManager.Current)); listposition = (Count > 0 ? 0 : -1); } else { // APPCOMPAT: bring back the Everett behavior where the currency manager adds an item and // then it cancels the addition. // // really, really hocky. // will throw if the list in the curManager is not IBindingList // and this will fail if the IBindingList does not have list change notification. read on.... // when a new item will get added to an empty parent table, // the table will fire OnCurrentChanged and this method will get executed again // allowing us to set the data source to an object with the right properties (so we can show // metadata at design time). // we then call CancelCurrentEdit to remove the dummy row, but making sure to ignore any // OnCurrentItemChanged that results from this action (to avoid infinite recursion) currencyManager.AddNew(); try { IgnoreItemChangedTable.Add(currencyManager); currencyManager.CancelCurrentEdit(); } finally { if (IgnoreItemChangedTable.Contains(currencyManager)) { IgnoreItemChangedTable.Remove(currencyManager); } } } } else { // Case where the parent is not a list, but a single object SetDataSource(_fieldInfo.GetValue(_parentManager.Current)); listposition = (Count > 0 ? 0 : -1); } if (oldlistposition != listposition) { OnPositionChanged(EventArgs.Empty); } OnCurrentChanged(EventArgs.Empty); OnCurrentItemChanged(EventArgs.Empty); } }