public TabButton(DockControl content) : this() { DockControl = content; DockControl.PropertyChanged += WeakRef.MakeWeak(HandlePropertyChanged, h => DockControl.PropertyChanged -= h); void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(DockControl.TabText): { NotifyPropertyChanged(nameof(TabText)); NotifyPropertyChanged(nameof(TabToolTip)); break; } case nameof(DockControl.TabToolTip): { NotifyPropertyChanged(nameof(TabToolTip)); break; } case nameof(DockControl.TabIcon): { NotifyPropertyChanged(nameof(TabIcon)); break; } } } }
public void WeakCollectionChangedEventHandlers() { int count = 0; var list = new List <ObservableCollection <string> >(); list.Add(new ObservableCollection <string>()); list.Add(new ObservableCollection <string>()); list.Add(new ObservableCollection <string>()); foreach (var obs in list) { obs.CollectionChanged += WeakRef.MakeWeak(HandleCollectionChanged, h => obs.CollectionChanged -= h); void HandleCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { ++count; } } list[0].Add("Zero"); list[1].Add("One"); list[2].Add("Two"); Assert.True(count == 3); // Observables collected here list.Clear(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true); Assert.True(count == 3); }
// Notes: // - Links the camera of two scenes without adding GC references // - View3dControls do not use these directly, they are a helper for higher level code. public ViewLink(View3dControl source, View3dControl target, ELinkCameras cam = ELinkCameras.None) { Source = new WeakReference <View3dControl>(source); Target = new WeakReference <View3dControl>(target); CamLink = cam; // Watch for navigation events on 'source' and apply them to 'target' source.Window.MouseNavigating += WeakRef.MakeWeak <View3d.MouseNavigateEventArgs>(OnSourceNavigation, h => source.Window.MouseNavigating -= h); }
// Notes: // - Links the camera or axis range of two charts without adding a GC references. // - ChartControls do not use these directly, they are a helper for higher level code. public ChartLink(ChartControl source, ChartControl target, ELinkCameras cam = ELinkCameras.None, ELinkAxes axis = ELinkAxes.None) : base(source.Scene, target.Scene, cam) { Source = new WeakReference <ChartControl>(source); Target = new WeakReference <ChartControl>(target); AxisLink = axis; // Watch for axis range changes source.ChartMoved += WeakRef.MakeWeak <ChartControl.ChartMovedEventArgs>(OnSourceChartMoved, h => source.ChartMoved -= h); }
void WeakEventHandlersShootTargets(Gun gun, List <string> collected, List <string> hit) { Target?bob = new Target("bob", collected, hit); gun.Firing += WeakRef.MakeWeak(bob.OnFiring, eh => gun.Firing -= eh); gun.Bang += WeakRef.MakeWeak <Gun>(bob.OnHit, eh => gun.Bang -= eh); gun.Fired += WeakRef.MakeWeak <Gun.FiredArgs>(bob.OnFired, eh => gun.Fired -= eh); gun.Shoot(); Assert.True(hit.Contains("Don't Shoot")); Assert.True(hit.Contains("bob")); Assert.True(hit.Contains("Bang!")); hit.Clear(); bob = null; }
void WeakActionsShootTargets(Gun gun, List <string> collected, List <string> hit) { Target?bob = new Target("bob", collected, hit); Target?fred = new Target("fred", collected, hit); gun.Bang += WeakRef.MakeWeak <Gun>(bob.OnHit, h => gun.Bang -= h); gun.Bang += fred.OnHit; gun.Shoot(); Assert.True(hit.Contains("bob")); Assert.True(hit.Contains("fred")); hit.Clear(); bob = null; fred = null; }
void WeakEventPropChangedHandlersShootTargets(Gun gun, List <string> collected, List <string> hit, List <string> prop_change) { Target?bob = new Target("bob", collected, hit); bob.PropertyChanged += WeakRef.MakeWeak(HandlePropChanged, h => bob.PropertyChanged -= h); void HandlePropChanged(object sender, PropertyChangedEventArgs e) => prop_change.Add($"{bob?.Name} {e.PropertyName}"); gun.Bang += WeakRef.MakeWeak <Gun>(bob.OnHit, eh => gun.Bang -= eh); gun.Shoot(); Assert.True(hit.Contains("bob")); Assert.True(prop_change.Contains("bob Dead")); hit.Clear(); prop_change.Clear(); bob = null; }
// Notes: // - TextAnchor references an offset (a position between two characters). It automatically updates // when text is inserted/removed in the document. // - TextAnchor only adds a weak reference to document.Change so unsubscribing isn't needed and // anchors wont keep a Document from being collected. // - To track a segment, use AnchorSegment which implements ISegment using two text anchors. // - Use TextDocument.CreateAnchor to create an anchor from an offset. // - Anchor movement is ambiguous if text is inserted exactly at the anchor's location. Does the anchor // stay before the inserted text, or does it move after it? The property MovementType is used to determine // which of these options the anchor will choose. // // Example: // auto anchor = document.CreateAnchor(offset); // ChangeMyDocument(); // int newOffset = anchor.Offset; internal TextAnchor(TextDocument document, int offset, AnchorMapping.EMove move) { Offset = offset; Movement = move; document.ChangeInternal += WeakRef.MakeWeak <DocumentChangeEventArgs>(HandleDocChanged, h => document.ChangeInternal -= h); void HandleDocChanged(object?sender, DocumentChangeEventArgs e) { if (!(sender is TextDocument doc)) { throw new Exception("sender should be a TextDocument"); } if (e.After && !IsDeleted) { // If the anchor is before the change, ignore if (Offset < e.Offset) { } // If the anchor is after the removed text, adjust by the difference in inserted - removed else if (Offset >= e.Offset + e.TextRemoved.Length) { Offset += e.TextInserted.Length - e.TextRemoved.Length; } // Otherwise, the anchor is within the removed text else { switch (Movement) { case AnchorMapping.EMove.Default: { // If the anchor is at the insertion offset, move to after the inserted text if (Offset == e.Offset) { Offset += e.TextInserted.Length - e.TextRemoved.Length; } // Otherwise, delete the anchor else { Delete(); } break; } case AnchorMapping.EMove.BeforeInsertion: { // Move the anchor to the insertion offset Offset += e.Offset; break; } case AnchorMapping.EMove.AfterInsertion: { // Move the anchor to after the insertion Offset += e.TextInserted.Length - e.TextRemoved.Length; break; } default: { throw new Exception("Unknown anchor movement type"); } } } } } }