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;
                }
                }
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        // 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);
        }
Beispiel #4
0
        // 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);
        }
Beispiel #5
0
        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;
        }
Beispiel #6
0
        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;
        }
Beispiel #7
0
        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;
        }
Beispiel #8
0
        // 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");
                        }
                        }
                    }
                }
            }
        }