public Box(BoxViewModel viewModel, Caret caretPerTree) { this.DataContext = viewModel; this.CaretPerTree = caretPerTree; this.Focusable = true; this.Width = double.NaN; this.ShowGridLines = false; //this.SizeChanged += (sender, e) => { if (this.BoxParent != null) this.BoxParent.InvalidateMeasure(); throw new NotImplementedException("You'd think this is automated in WPF"); }; this.viewModel.CaretPerTree.CaretChangedFocus += OnCaretChanged; OnCaretChanged(this.viewModel.Caret, new PropertyChangedEventArgs(nameof(BoxViewModel.Caret))); this.viewModel.Elements.CollectionChangedElementWise += OnElementsChanged; //trigger adding the initial elements. THe viewModel argument may already have some elements set if (viewModel.Elements.Count != 0) { var e = new NotifyBoxElementsChangedEventArgs(viewModel.Elements, NotifyCollectionChangedAction.Add, viewModel.Elements, 0); OnElementsChanged(viewModel.Elements, e); } this.viewModel.PropertyChanged += OnFocusableChanged; OnFocusableChanged(this.viewModel, new PropertyChangedEventArgs(nameof(BoxViewModel.Modifiable))); this.viewModel.Selections.CollectionChanged += OnSelectionsChanged; if (this.viewModel.Selections.Count != 0) OnSelectionsChanged(this.viewModel.Selections, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, this.viewModel.Selections, 0)); }
//Ok, so this is how this works. //we start with the root view model. It's treated like any other box in the view model. We hook into the box collection changed. when a box composition is added to some box, //the event handler is triggered and registers a mutation associated with that box. A mutation is a lazy change, meaning that whenever that box is requested, the mutation takes place //This entails adding in the box composition to the name instance model and registering an eventhandler that handles subsequent changes to the box composition //(from the moment of implementation the mutation that adds it, not before) TODO: consider the case where the box composition was removed already //In case the box collection changed and added a glyph, again a mutation is added so that whenever the box is requested, the glyph gets added. //The case for the box composition collection changed event handler is pretty symmetric. When a box is added, it registers a mutation with the box composition. //whenever the box composition is requested, the mutation takes effect: it adds the box and hooks into its collection changed event. //removal works in the same manner, adding mutations that remove the boxes/box compositions and their respective handlers (and of all descendents as well). private void onBoxElementsCollectionChanged(object boxElements, NotifyBoxElementsChangedEventArgs e) { var elements = (BoxElementsCollection)boxElements; switch (e.Action) { case NotifyCollectionChangedAction.Add: e.NewItems.VirtualForeach ( (BoxCompositionViewModel newBoxComposition, int i) => this.AddNewBoxCompositionMutation(newBoxComposition, e.NewStartingIndex + i), (Glyph newGlyphRun, int i) => this.AddNewGlyphsMutation(newGlyphRun, e.NewStartingIndex + i, elements.Box) ); break; case NotifyCollectionChangedAction.Remove: this.Without(elements.Box, e.OldStartingIndex, e.OldItems); break; case NotifyCollectionChangedAction.Reset: throw new ArgumentException("Use remove all, since otherwise I cannot access the removed elements"); default: throw new NotImplementedException(); } }