Controls, Startup&Driver improvements
This commit is contained in:
@@ -10,7 +10,7 @@ public abstract class ContentView<T>: View<T>, IContentRenderer
|
||||
ContentRendererMethod = DefaultContentRender;
|
||||
}
|
||||
public IView? Content { get; set; }
|
||||
public Action<Position> ContentRendererMethod { get; set; }
|
||||
public Action<Position, Size> ContentRendererMethod { get; set; }
|
||||
|
||||
private void DefaultContentRender(Position position) => Content?.Render(position);
|
||||
private void DefaultContentRender(Position position, Size size) => Content?.Render(position, size);
|
||||
}
|
||||
248
src/Library/TerminalUI/Controls/Grid.cs
Normal file
248
src/Library/TerminalUI/Controls/Grid.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using TerminalUI.Extensions;
|
||||
using TerminalUI.Models;
|
||||
using TerminalUI.ViewExtensions;
|
||||
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
public class Grid<T> : View<T>
|
||||
{
|
||||
private const int ToBeCalculated = -1;
|
||||
private readonly ObservableCollection<IView> _children = new();
|
||||
public ReadOnlyObservableCollection<IView> Children { get; }
|
||||
public GridChildInitializer<T> ChildInitializer { get; }
|
||||
public ObservableCollection<RowDefinition> RowDefinitions { get; } = new();
|
||||
public ObservableCollection<ColumnDefinition> ColumnDefinitions { get; } = new();
|
||||
|
||||
public object? ColumnDefinitionsObject
|
||||
{
|
||||
get => ColumnDefinitions;
|
||||
set
|
||||
{
|
||||
if (value is IEnumerable<ColumnDefinition> columnDefinitions)
|
||||
{
|
||||
ColumnDefinitions.Clear();
|
||||
foreach (var columnDefinition in columnDefinitions)
|
||||
{
|
||||
ColumnDefinitions.Add(columnDefinition);
|
||||
}
|
||||
}
|
||||
else if (value is string s)
|
||||
{
|
||||
SetColumnDefinitions(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object? RowDefinitionsObject
|
||||
{
|
||||
get => RowDefinitions;
|
||||
set
|
||||
{
|
||||
if (value is IEnumerable<RowDefinition> rowDefinitions)
|
||||
{
|
||||
RowDefinitions.Clear();
|
||||
foreach (var rowDefinition in rowDefinitions)
|
||||
{
|
||||
RowDefinitions.Add(rowDefinition);
|
||||
}
|
||||
}
|
||||
else if (value is string s)
|
||||
{
|
||||
SetRowDefinitions(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Grid()
|
||||
{
|
||||
ChildInitializer = new GridChildInitializer<T>(this);
|
||||
Children = new ReadOnlyObservableCollection<IView>(_children);
|
||||
_children.CollectionChanged += (o, e) =>
|
||||
{
|
||||
if (Attached)
|
||||
{
|
||||
if (e.NewItems?.OfType<IView>() is { } newItems)
|
||||
{
|
||||
foreach (var newItem in newItems)
|
||||
{
|
||||
newItem.Attached = true;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override Size GetRequestedSize() => throw new NotImplementedException();
|
||||
|
||||
protected override void DefaultRenderer(Position position, Size size)
|
||||
{
|
||||
//TODO: Optimize it, dont calculate all of these only if there is Auto value(s)
|
||||
var columns = ColumnDefinitions.Count;
|
||||
Span<int> allWidth = stackalloc int[columns * RowDefinitions.Count];
|
||||
Span<int> allHeight = stackalloc int[columns * RowDefinitions.Count];
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
var childSize = child.GetRequestedSize();
|
||||
var positionExtension = child.GetExtension<GridPositionExtension>();
|
||||
var x = positionExtension?.Column ?? 0;
|
||||
var y = positionExtension?.Row ?? 0;
|
||||
|
||||
allWidth.SetToMatrix(childSize.Width, x, y, columns);
|
||||
allHeight.SetToMatrix(childSize.Height, x, y, columns);
|
||||
}
|
||||
|
||||
Span<int> columnWidths = stackalloc int[columns];
|
||||
Span<int> rowHeights = stackalloc int[RowDefinitions.Count];
|
||||
|
||||
for (var i = 0; i < columnWidths.Length; i++)
|
||||
{
|
||||
if (ColumnDefinitions[i].Type == GridUnitType.Pixel)
|
||||
{
|
||||
columnWidths[i] = ColumnDefinitions[i].Value;
|
||||
}
|
||||
else if (ColumnDefinitions[i].Type == GridUnitType.Star)
|
||||
{
|
||||
columnWidths[i] = ToBeCalculated;
|
||||
}
|
||||
else
|
||||
{
|
||||
var max = 0;
|
||||
for (var j = 0; j < RowDefinitions.Count; j++)
|
||||
{
|
||||
max = Math.Max(max, allWidth.GetFromMatrix(i, j, columns));
|
||||
}
|
||||
|
||||
columnWidths[i] = max;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < rowHeights.Length; i++)
|
||||
{
|
||||
if (RowDefinitions[i].Type == GridUnitType.Pixel)
|
||||
{
|
||||
rowHeights[i] = RowDefinitions[i].Value;
|
||||
}
|
||||
else if (RowDefinitions[i].Type == GridUnitType.Star)
|
||||
{
|
||||
rowHeights[i] = ToBeCalculated;
|
||||
}
|
||||
else
|
||||
{
|
||||
var max = 0;
|
||||
for (var j = 0; j < columns; j++)
|
||||
{
|
||||
max = Math.Max(max, allHeight.GetFromMatrix(j, i, columns));
|
||||
}
|
||||
|
||||
rowHeights[i] = max;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
var childSize = child.GetRequestedSize();
|
||||
var positionExtension = child.GetExtension<GridPositionExtension>();
|
||||
var x = positionExtension?.Column ?? 0;
|
||||
var y = positionExtension?.Row ?? 0;
|
||||
|
||||
var width = columnWidths[x];
|
||||
var height = rowHeights[y];
|
||||
|
||||
var left = 0;
|
||||
var top = 0;
|
||||
|
||||
for (var i = 0; i < x; i++)
|
||||
{
|
||||
left += columnWidths[i];
|
||||
}
|
||||
|
||||
for (var i = 0; i < y; i++)
|
||||
{
|
||||
top += rowHeights[i];
|
||||
}
|
||||
|
||||
child.Render(new Position(left, top), new Size(width, height));
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRowDefinitions(string value)
|
||||
{
|
||||
var values = value.Split(' ');
|
||||
RowDefinitions.Clear();
|
||||
|
||||
foreach (var v in values)
|
||||
{
|
||||
if (v == "Auto")
|
||||
{
|
||||
RowDefinitions.Add(RowDefinition.Auto);
|
||||
}
|
||||
else if (v.EndsWith("*"))
|
||||
{
|
||||
var starValue = int.Parse(v[0..^1]);
|
||||
RowDefinitions.Add(RowDefinition.Star(starValue));
|
||||
}
|
||||
else if (int.TryParse(v, out var pixelValue))
|
||||
{
|
||||
RowDefinitions.Add(RowDefinition.Pixel(pixelValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid row definition: " + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetColumnDefinitions(string value)
|
||||
{
|
||||
var values = value.Split(' ');
|
||||
ColumnDefinitions.Clear();
|
||||
|
||||
foreach (var v in values)
|
||||
{
|
||||
if (v == "Auto")
|
||||
{
|
||||
ColumnDefinitions.Add(ColumnDefinition.Auto);
|
||||
}
|
||||
else if (v.EndsWith("*"))
|
||||
{
|
||||
var starValue = int.Parse(v[0..^1]);
|
||||
ColumnDefinitions.Add(ColumnDefinition.Star(starValue));
|
||||
}
|
||||
else if (int.TryParse(v, out var pixelValue))
|
||||
{
|
||||
ColumnDefinitions.Add(ColumnDefinition.Pixel(pixelValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid column definition: " + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override TChild AddChild<TChild>(TChild child)
|
||||
{
|
||||
child = base.AddChild(child);
|
||||
_children.Add(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
public override TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||
where TDataContext : default
|
||||
{
|
||||
child = base.AddChild(child, dataContextMapper);
|
||||
_children.Add(child);
|
||||
return child;
|
||||
}
|
||||
}
|
||||
24
src/Library/TerminalUI/Controls/GridChildInitializer.cs
Normal file
24
src/Library/TerminalUI/Controls/GridChildInitializer.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
public record ChildWithDataContextMapper<TSourceDataContext, TTargetDataContext>(IView<TTargetDataContext> Child, Func<TSourceDataContext?, TTargetDataContext?> DataContextMapper);
|
||||
|
||||
public class GridChildInitializer<T> : IEnumerable<IView>
|
||||
{
|
||||
private readonly Grid<T> _grid;
|
||||
|
||||
public GridChildInitializer(Grid<T> grid)
|
||||
{
|
||||
_grid = grid;
|
||||
}
|
||||
|
||||
public void Add(IView<T> item) => _grid.AddChild(item);
|
||||
|
||||
public void Add<TDataContext>(ChildWithDataContextMapper<T, TDataContext> item)
|
||||
=> _grid.AddChild(item.Child, item.DataContextMapper);
|
||||
|
||||
public IEnumerator<IView> GetEnumerator() => _grid.Children.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
23
src/Library/TerminalUI/Controls/GridProperties.cs
Normal file
23
src/Library/TerminalUI/Controls/GridProperties.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
|
||||
public enum GridUnitType
|
||||
{
|
||||
Auto,
|
||||
Pixel,
|
||||
Star
|
||||
}
|
||||
|
||||
public record struct RowDefinition(GridUnitType Type, int Value)
|
||||
{
|
||||
public static RowDefinition Auto => new(GridUnitType.Auto, 0);
|
||||
public static RowDefinition Pixel(int value) => new(GridUnitType.Pixel, value);
|
||||
public static RowDefinition Star(int value) => new(GridUnitType.Star, value);
|
||||
}
|
||||
|
||||
public record struct ColumnDefinition(GridUnitType Type, int Value)
|
||||
{
|
||||
public static ColumnDefinition Auto => new(GridUnitType.Auto, 0);
|
||||
public static ColumnDefinition Pixel(int value) => new(GridUnitType.Pixel, value);
|
||||
public static ColumnDefinition Star(int value) => new(GridUnitType.Star, value);
|
||||
}
|
||||
@@ -7,10 +7,20 @@ namespace TerminalUI.Controls;
|
||||
public interface IView : INotifyPropertyChanged, IDisposableCollection
|
||||
{
|
||||
object? DataContext { get; set; }
|
||||
Action<Position> RenderMethod { get; set; }
|
||||
IApplicationContext? ApplicationContext { get; init;}
|
||||
int? MinWidth { get; set; }
|
||||
int? MaxWidth { get; set; }
|
||||
int? Width { get; set; }
|
||||
int? MinHeight { get; set; }
|
||||
int? MaxHeight { get; set; }
|
||||
int? Height { get; set; }
|
||||
bool Attached { get; set; }
|
||||
Size GetRequestedSize();
|
||||
IApplicationContext? ApplicationContext { get; set; }
|
||||
List<object> Extensions { get; }
|
||||
|
||||
Action<Position, Size> RenderMethod { get; set; }
|
||||
event Action<IView> Disposed;
|
||||
void Render(Position position);
|
||||
void Render(Position position, Size size);
|
||||
}
|
||||
|
||||
public interface IView<T> : IView
|
||||
@@ -28,4 +38,9 @@ public interface IView<T> : IView
|
||||
|
||||
TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>, new();
|
||||
|
||||
TChild AddChild<TChild>(TChild child) where TChild : IView<T>;
|
||||
|
||||
TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Buffers;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using DeclarativeProperty;
|
||||
using TerminalUI.Models;
|
||||
|
||||
@@ -14,6 +15,23 @@ public class ListView<TDataContext, TItem> : View<TDataContext>
|
||||
private object? _itemsSource;
|
||||
private ListViewItem<TItem>[]? _listViewItems;
|
||||
private int _listViewItemLength;
|
||||
private int _selectedIndex = 0;
|
||||
private int _renderStartIndex = 0;
|
||||
private Size _requestedItemSize = new(0, 0);
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get => _selectedIndex;
|
||||
set
|
||||
{
|
||||
if (_selectedIndex != value)
|
||||
{
|
||||
_selectedIndex = value;
|
||||
OnPropertyChanged();
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object? ItemsSource
|
||||
{
|
||||
@@ -64,13 +82,53 @@ public class ListView<TDataContext, TItem> : View<TDataContext>
|
||||
|
||||
public Func<ListViewItem<TItem>, IView?> ItemTemplate { get; set; } = DefaultItemTemplate;
|
||||
|
||||
protected override void DefaultRenderer(Position position)
|
||||
public override Size GetRequestedSize()
|
||||
{
|
||||
if (_listViewItems is null || _listViewItems.Length == 0)
|
||||
return new Size(0, 0);
|
||||
|
||||
|
||||
var itemSize = _listViewItems[0].GetRequestedSize();
|
||||
_requestedItemSize = itemSize;
|
||||
return itemSize with {Height = itemSize.Height * _listViewItems.Length};
|
||||
}
|
||||
|
||||
protected override void DefaultRenderer(Position position, Size size)
|
||||
{
|
||||
var listViewItems = InstantiateItemViews();
|
||||
var deltaY = 0;
|
||||
foreach (var item in listViewItems)
|
||||
if (listViewItems.Length == 0) return;
|
||||
|
||||
var requestedItemSize = _requestedItemSize;
|
||||
|
||||
var itemsToRender = listViewItems.Length;
|
||||
var heightNeeded = requestedItemSize.Height * listViewItems.Length;
|
||||
var renderStartIndex = _renderStartIndex;
|
||||
if (heightNeeded < size.Height)
|
||||
{
|
||||
item.Render(position with {PosY = position.PosY + deltaY++});
|
||||
var maxItemsToRender = (int) Math.Floor((double) size.Height / requestedItemSize.Height);
|
||||
if (SelectedIndex < renderStartIndex)
|
||||
{
|
||||
renderStartIndex = SelectedIndex - 1;
|
||||
}
|
||||
else if (SelectedIndex > renderStartIndex + maxItemsToRender)
|
||||
{
|
||||
renderStartIndex = SelectedIndex - maxItemsToRender + 1;
|
||||
}
|
||||
|
||||
if(renderStartIndex < 0)
|
||||
renderStartIndex = 0;
|
||||
else if (renderStartIndex + maxItemsToRender > listViewItems.Length)
|
||||
renderStartIndex = listViewItems.Length - maxItemsToRender;
|
||||
|
||||
_renderStartIndex = renderStartIndex;
|
||||
}
|
||||
|
||||
var deltaY = 0;
|
||||
for (var i = renderStartIndex; i < itemsToRender && i < listViewItems.Length; i++)
|
||||
{
|
||||
var item = listViewItems[i];
|
||||
item.Render(position with {Y = position.Y + deltaY}, requestedItemSize);
|
||||
deltaY += requestedItemSize.Height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,13 @@ namespace TerminalUI.Controls;
|
||||
|
||||
public class ListViewItem<T> : ContentView<T>
|
||||
{
|
||||
protected override void DefaultRenderer(Position position)
|
||||
public override Size GetRequestedSize()
|
||||
{
|
||||
if (Content is null) return new Size(0, 0);
|
||||
return Content.GetRequestedSize();
|
||||
}
|
||||
|
||||
protected override void DefaultRenderer(Position position, Size size)
|
||||
{
|
||||
if (ContentRendererMethod is null)
|
||||
{
|
||||
@@ -16,6 +22,6 @@ public class ListViewItem<T> : ContentView<T>
|
||||
+ DataContext?.GetType().Name);
|
||||
}
|
||||
|
||||
ContentRendererMethod(position);
|
||||
ContentRendererMethod(position, size);
|
||||
}
|
||||
}
|
||||
24
src/Library/TerminalUI/Controls/Rectangle.cs
Normal file
24
src/Library/TerminalUI/Controls/Rectangle.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using TerminalUI.Color;
|
||||
using TerminalUI.Models;
|
||||
|
||||
namespace TerminalUI.Controls;
|
||||
|
||||
public partial class Rectangle<T> : View<T>
|
||||
{
|
||||
[Notify] private IColor? _fill;
|
||||
public override Size GetRequestedSize() => new(Width ?? 0, Height ?? 0);
|
||||
|
||||
protected override void DefaultRenderer(Position position, Size size)
|
||||
{
|
||||
var s = new string('█', Width ?? size.Width);
|
||||
ApplicationContext?.ConsoleDriver.SetBackgroundColor(Fill ?? new Color.ConsoleColor(System.ConsoleColor.Yellow, ColorType.Background));
|
||||
ApplicationContext?.ConsoleDriver.SetForegroundColor(Fill ?? new Color.ConsoleColor(System.ConsoleColor.Yellow, ColorType.Foreground));
|
||||
var height = Height ?? size.Height;
|
||||
for (var i = 0; i < height; i++)
|
||||
{
|
||||
ApplicationContext?.ConsoleDriver.SetCursorPosition(position with {Y = position.Y + i});
|
||||
ApplicationContext?.ConsoleDriver.Write(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using TerminalUI.Color;
|
||||
using TerminalUI.Extensions;
|
||||
using TerminalUI.Models;
|
||||
|
||||
@@ -27,7 +28,9 @@ public partial class TextBlock<T> : View<T>
|
||||
RerenderProperties.Add(nameof(Background));
|
||||
}
|
||||
|
||||
protected override void DefaultRenderer(Position position)
|
||||
public override Size GetRequestedSize() => new(Text?.Length ?? 0, 1);
|
||||
|
||||
protected override void DefaultRenderer(Position position, Size size)
|
||||
{
|
||||
var driver = ApplicationContext!.ConsoleDriver;
|
||||
var renderContext = new RenderContext(position, Text, _foreground, _background);
|
||||
|
||||
@@ -10,8 +10,30 @@ public abstract partial class View<T> : IView<T>
|
||||
{
|
||||
private readonly List<IDisposable> _disposables = new();
|
||||
[Notify] private T? _dataContext;
|
||||
public Action<Position> RenderMethod { get; set; }
|
||||
public IApplicationContext? ApplicationContext { get; init; }
|
||||
[Notify] private int? _minWidth;
|
||||
[Notify] private int? _maxWidth;
|
||||
[Notify] private int? _width;
|
||||
[Notify] private int? _minHeight;
|
||||
[Notify] private int? _maxHeight;
|
||||
[Notify] private int? _height;
|
||||
private bool _attached;
|
||||
|
||||
public bool Attached
|
||||
{
|
||||
get => _attached;
|
||||
set
|
||||
{
|
||||
if (_attached == value) return;
|
||||
_attached = value;
|
||||
if (value)
|
||||
{
|
||||
AttachChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
public List<object> Extensions { get; } = new();
|
||||
public Action<Position, Size> RenderMethod { get; set; }
|
||||
public IApplicationContext? ApplicationContext { get; set; }
|
||||
public event Action<IView>? Disposed;
|
||||
protected List<string> RerenderProperties { get; } = new();
|
||||
|
||||
@@ -20,22 +42,27 @@ public abstract partial class View<T> : IView<T>
|
||||
RenderMethod = DefaultRenderer;
|
||||
((INotifyPropertyChanged) this).PropertyChanged += Handle_PropertyChanged;
|
||||
}
|
||||
public abstract Size GetRequestedSize();
|
||||
|
||||
protected virtual void AttachChildren()
|
||||
{
|
||||
}
|
||||
|
||||
private void Handle_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is not null
|
||||
&& (e.PropertyName == nameof(IView.DataContext)
|
||||
if (e.PropertyName is not null
|
||||
&& (e.PropertyName == nameof(IView.DataContext)
|
||||
|| RerenderProperties.Contains(e.PropertyName)
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
ApplicationContext?.EventLoop.RequestRerender();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void DefaultRenderer(Position position);
|
||||
protected abstract void DefaultRenderer(Position position, Size size);
|
||||
|
||||
public void Render(Position position)
|
||||
public void Render(Position position, Size size)
|
||||
{
|
||||
if (RenderMethod is null)
|
||||
{
|
||||
@@ -47,16 +74,27 @@ public abstract partial class View<T> : IView<T>
|
||||
+ DataContext?.GetType().Name);
|
||||
}
|
||||
|
||||
RenderMethod(position);
|
||||
RenderMethod(position, size);
|
||||
}
|
||||
|
||||
public TChild CreateChild<TChild>() where TChild : IView<T>, new()
|
||||
{
|
||||
var child = new TChild
|
||||
{
|
||||
DataContext = DataContext,
|
||||
ApplicationContext = ApplicationContext
|
||||
};
|
||||
var child = new TChild();
|
||||
return AddChild(child);
|
||||
}
|
||||
|
||||
public TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>, new()
|
||||
{
|
||||
var child = new TChild();
|
||||
return AddChild(child, dataContextMapper);
|
||||
}
|
||||
|
||||
public virtual TChild AddChild<TChild>(TChild child) where TChild : IView<T>
|
||||
{
|
||||
child.DataContext = DataContext;
|
||||
child.ApplicationContext = ApplicationContext;
|
||||
|
||||
var mapper = new DataContextMapper<T>(this, d => child.DataContext = d);
|
||||
AddDisposable(mapper);
|
||||
child.AddDisposable(mapper);
|
||||
@@ -64,14 +102,12 @@ public abstract partial class View<T> : IView<T>
|
||||
return child;
|
||||
}
|
||||
|
||||
public TChild CreateChild<TChild, TDataContext>(Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>, new()
|
||||
public virtual TChild AddChild<TChild, TDataContext>(TChild child, Func<T?, TDataContext?> dataContextMapper)
|
||||
where TChild : IView<TDataContext>
|
||||
{
|
||||
var child = new TChild
|
||||
{
|
||||
DataContext = dataContextMapper(DataContext),
|
||||
ApplicationContext = ApplicationContext
|
||||
};
|
||||
child.DataContext = dataContextMapper(DataContext);
|
||||
child.ApplicationContext = ApplicationContext;
|
||||
|
||||
var mapper = new DataContextMapper<T>(this, d => child.DataContext = dataContextMapper(d));
|
||||
AddDisposable(mapper);
|
||||
child.AddDisposable(mapper);
|
||||
|
||||
Reference in New Issue
Block a user