diff --git a/Dashboard/Controls/Container.cs b/Dashboard/Controls/Container.cs index 04ea2cf..d3710a4 100644 --- a/Dashboard/Controls/Container.cs +++ b/Dashboard/Controls/Container.cs @@ -1,6 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; using Dashboard.Drawing; using Dashboard.Events; using Dashboard.Layout; @@ -8,21 +10,21 @@ using Dashboard.Pal; namespace Dashboard.Controls { - public class Container : Control, IList, ILayoutContainer + public class Container : Control, IList, ILayoutContainer, INotifyCollectionChanged { private readonly List _controls = new List(); + public ContainerLayoutInfo ContainerLayout { get; } = new ContainerLayoutInfo(); + public int Count => _controls.Count; public bool IsReadOnly => false; - public ContainerLayoutInfo ContainerLayout { get; } = new ContainerLayoutInfo(); - ILayoutItem IReadOnlyList.this[int index] => _controls[index]; public event EventHandler? ChildAdded; public event EventHandler? ChildRemoved; - + public event NotifyCollectionChangedEventHandler? CollectionChanged; public Control this[int index] { @@ -30,6 +32,11 @@ namespace Dashboard.Controls set => _controls[index] = value; } + public Container() + { + ContainerLayout.PropertyChanged += OnContainerLayoutChanged; + } + protected override void ValidateLayout() { if (!IsLayoutEnabled || IsLayoutValid) @@ -82,16 +89,17 @@ namespace Dashboard.Controls SetParent(this, item); _controls.Add(item); - ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item)); + OnChildAdded(item); } public void Clear() { foreach (Control control in this) { - ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, control)); + OnChildRemoved(control, false); } _controls.Clear(); + OnCollectionChanged(NotifyCollectionChangedAction.Reset); } public bool Contains(Control item) @@ -109,7 +117,7 @@ namespace Dashboard.Controls if (!_controls.Remove(item)) return false; - ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, item)); + OnChildRemoved(item); return true; } @@ -123,17 +131,64 @@ namespace Dashboard.Controls SetParent(this, item); _controls.Insert(index, item); - ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item)); + OnChildAdded(item); } public void RemoveAt(int index) { Control child = _controls[index]; _controls.RemoveAt(index); - ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, child)); + OnChildRemoved(child); } + protected override void OnOwnerChanged(Form value) + { + base.OnOwnerChanged(value); + foreach (Control control in this) + { + SetOwner(control, value); + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + Clear(); + } + + ContainerLayout.PropertyChanged -= OnContainerLayoutChanged; + } + + protected virtual void OnChildAdded(Control item) + { + SetOwner(item, Owner ?? null); + ChildAdded?.Invoke(this, new ContainerChildAddedEventArgs(this, item)); + + OnCollectionChanged(NotifyCollectionChangedAction.Add); + } + + protected virtual void OnChildRemoved(Control control, bool notifyCollectionChanged = true) + { + SetOwner(control, null!); + ChildRemoved?.Invoke(this, new ContainerChildRemovedEventArgs(this, control)); + + if (notifyCollectionChanged) + OnCollectionChanged(NotifyCollectionChangedAction.Remove); + } + + protected virtual void OnCollectionChanged(NotifyCollectionChangedAction action) + { + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action)); + } + + private void OnContainerLayoutChanged(object? sender, PropertyChangedEventArgs ea) + { + OnPropertyChanged(nameof(ContainerLayout)); + } } public class ContainerChildAddedEventArgs(Container parent, Control child) : EventArgs diff --git a/Dashboard/Controls/Control.cs b/Dashboard/Controls/Control.cs index e1fd9ce..85feb06 100644 --- a/Dashboard/Controls/Control.cs +++ b/Dashboard/Controls/Control.cs @@ -13,27 +13,33 @@ namespace Dashboard.Controls { public class Control : IEventListener, ILayoutItem, IDisposable { - private Form? _owner = null; - - public string? Id { get; set; } + public string? Id + { + get; + set => SetField(ref field, value); + } public Form Owner { - get => _owner ?? throw NoOwnerException; + get ; protected set { - _owner = value; OnOwnerChanged(value); + SetField(ref field, value); } - } + } = null!; - public Control? Parent { get; private set; } = null; + public Control? Parent + { + get; + private set => SetField(ref field, value); + } = null; public bool Disposed { get; private set; } public virtual Box2d ClientArea => Layout.ComputedBox; - public bool IsFocused => _owner?.FocusedControl == this; + public bool IsFocused => Owner?.FocusedControl == this; - public Brush Background { get; set; } = new SolidColorBrush(Color.Transparent); - public Brush BorderBrush { get; set; } = new SolidColorBrush(Color.Black); + public Brush Background { get; set => SetField(ref field, value); } = new SolidColorBrush(Color.Transparent); + public Brush BorderBrush { get; set => SetField(ref field, value); } = new SolidColorBrush(Color.Black); public LayoutInfo Layout { get; } = new LayoutInfo(); public bool IsLayoutEnabled { get; private set; } = true; @@ -49,6 +55,11 @@ namespace Dashboard.Controls public event EventHandler? Disposing; public event EventHandler? Resized; + public Control() + { + Layout.PropertyChanged += LayoutChanged; + } + public virtual Vector2 CalculateIntrinsicSize() { return Vector2.Zero; @@ -74,7 +85,9 @@ namespace Dashboard.Controls { if (Disposed) return; + Disposed = true; + Layout.PropertyChanged -= LayoutChanged; Dispose(disposing); @@ -134,23 +147,13 @@ namespace Dashboard.Controls FocusLost?.Invoke(sender, EventArgs.Empty); } - internal static void InvokeFocusGained(Form form, Control control) - { - control.OnFocusGained(form); - } - - internal static void InvokeFocusLost(Form form, Control control) - { - control.OnFocusLost(form); - } - protected virtual void OnResize() { Resized?.Invoke(this, EventArgs.Empty); InvalidateLayout(); } - private void OnOwnerChanged(Form value) + protected virtual void OnOwnerChanged(Form value) { OwnerChanged?.Invoke(this, EventArgs.Empty); } @@ -188,6 +191,26 @@ namespace Dashboard.Controls PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } + private void LayoutChanged(object? sender, PropertyChangedEventArgs ea) + { + OnPropertyChanged(nameof(Layout)); + } + protected static Exception NoOwnerException => new Exception("No form owns this control"); + + internal static void InvokeFocusGained(Form form, Control control) + { + control.OnFocusGained(form); + } + + internal static void InvokeFocusLost(Form form, Control control) + { + control.OnFocusLost(form); + } + + private protected static void SetOwner(Control item, Form owner) + { + item.Owner = owner; + } } } diff --git a/Dashboard/Controls/Form.cs b/Dashboard/Controls/Form.cs index bcee628..103cf3b 100644 --- a/Dashboard/Controls/Form.cs +++ b/Dashboard/Controls/Form.cs @@ -1,6 +1,5 @@ using System; using System.Drawing; -using System.Numerics; using Dashboard.Drawing; using Dashboard.Events; using Dashboard.Pal; @@ -10,20 +9,18 @@ namespace Dashboard.Controls { public class Form : Container, IForm { - private string? _title = "Untitled Form"; public IWindow Window { get; } - public Image? WindowIcon { get; set; } - public string? Title + public string Title { - get => _title; + get; set { - _title = value; - Window.Title = _title ?? ""; + field = value; + Window.Title = field ?? ""; } - } + } = "Untitled Form"; public Control? FocusedControl { get; private set; } = null; public override Box2d ClientArea @@ -37,19 +34,11 @@ namespace Dashboard.Controls { Window = window; Window.Form = this; - Window.Title = _title; + Window.Title = Title; Window.EventRaised += WindowEventRaised; Background = new SolidColorBrush(Color.SlateGray); } - private void WindowEventRaised(object? sender, EventArgs e) - { - if (e is ResizeEventArgs) - { - InvalidateLayout(); - } - } - public void Focus(Control control) { if (FocusedControl != null) @@ -108,5 +97,20 @@ namespace Dashboard.Controls break; } } + + protected override void OnChildAdded(Control item) + { + base.OnChildAdded(item); + + SetOwner(item, this); + } + + private void WindowEventRaised(object? sender, EventArgs e) + { + if (e is ResizeEventArgs) + { + InvalidateLayout(); + } + } } }