DER CODE
sgcUI in einem Avalonia-12-Fenster
Jeder Auszug stammt aus dem Beispiel AvaloniaProAlternative, einer Avalonia-12-App auf net8.0 mit kompilierten Bindungen und FluentTheme, das sich in Release für die Editionen Registered und Community mit 0 Warnungen in der Demo kompilieren lässt und seinen Selbsttest besteht. Hier steht kein Avalonia-Pro-Code: Diese Seite zeigt nur Code, der kompiliert wurde.
Ein XAML-Präfix für jedes Produkt
Jede sgcUI-Avalonia-Assembly ordnet ihre Steuerelemente einem Namespace-URI zu, sodass ein einziges Präfix sgc alle abdeckt, neben dem Avalonia-Standard-Namespace:
MainWindow.axaml<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sgc="https://www.esegece.com/sgcui"
xmlns:vm="using:sgcUIAvaloniaPro"
x:Class="sgcUIAvaloniaPro.MainWindow"
x:DataType="vm:TMainViewModel"
Title="sgcUI next to Avalonia 12: the controls Avalonia Pro does not ship" Width="1240" Height="820">
Nicht auf der Avalonia-Pro-Seite aufgeführt: Scheduler und Kanban über einer Aufgabenliste
Beide Ansichten binden an dieselbe Tasks-Auflistung des View-Modells. TsgcAvaloniaPlannerItemFields ordnet die Mitglieder Ihrer eigenen Aufgabenklasse zu, und ein Ziehen in einer der beiden Ansichten schreibt Begin, End oder Stage in die Aufgabe zurück:
MainWindow.axaml<sgc:TsgcAvaloniaScheduler x:Name="scheduler" ItemsSource="{Binding Tasks}"
ResourcesSource="{Binding People}" SelectedItem="{Binding Selected}">
<sgc:TsgcAvaloniaScheduler.ItemFields>
<sgc:TsgcAvaloniaPlannerItemFields IdField="Key" TitleField="Name" StartField="Begin"
FinishField="End" ResourcesField="Owner" StatusField="Stage"/>
</sgc:TsgcAvaloniaScheduler.ItemFields>
<sgc:TsgcAvaloniaScheduler.ResourceFields>
<sgc:TsgcAvaloniaPlannerResourceFields IdField="Code" NameField="Label"/>
</sgc:TsgcAvaloniaScheduler.ResourceFields>
</sgc:TsgcAvaloniaScheduler>
<sgc:TsgcAvaloniaKanban x:Name="kanban" Grid.Row="1" Margin="0,8,0,0" ColumnSource="pkcList"
ItemsSource="{Binding Tasks}" ResourcesSource="{Binding People}"
SelectedItem="{Binding Selected}">
<sgc:TsgcAvaloniaKanban.ItemFields>
<sgc:TsgcAvaloniaPlannerItemFields IdField="Key" TitleField="Name" StartField="Begin"
FinishField="End" ResourcesField="Owner" StatusField="Stage"/>
</sgc:TsgcAvaloniaKanban.ItemFields>
<sgc:TsgcAvaloniaKanban.ResourceFields>
<sgc:TsgcAvaloniaPlannerResourceFields IdField="Code" NameField="Label"/>
</sgc:TsgcAvaloniaKanban.ResourceFields>
<sgc:TsgcAvaloniaKanban.Columns>
<sgc:TsgcAvaloniaKanbanColumn StatusId="todo" Caption="To do"/>
<sgc:TsgcAvaloniaKanbanColumn StatusId="doing" Caption="In progress"/>
<sgc:TsgcAvaloniaKanbanColumn StatusId="done" Caption="Done"/>
</sgc:TsgcAvaloniaKanban.Columns>
</sgc:TsgcAvaloniaKanban>
Nicht aufgeführt: ein Dashboard aus KPI-Karten und einem Diagramm
Kacheln in einem 12-Spalten-Raster, die jeweils eine KPI-Karte oder ein beliebiges Steuerelement hosten, an das View-Modell gebunden:
MainWindow.axaml<sgc:TsgcAvaloniaDashboard x:Name="dashboard" Columns="12" RowHeight="110">
<sgc:TsgcAvaloniaDashboardTile Title="Revenue" Col="0" Row="0" ColSpan="4" RowSpan="2">
<sgc:TsgcAvaloniaKPICard x:Name="kpiRevenue" Title="Revenue this month" Value="{Binding Revenue}"
Trend="ktUp" FooterText="all channels"/>
</sgc:TsgcAvaloniaDashboardTile>
<sgc:TsgcAvaloniaDashboardTile Title="Orders" Col="4" Row="0" ColSpan="4" RowSpan="2">
<sgc:TsgcAvaloniaKPICard x:Name="kpiOrders" Title="Orders" Value="{Binding Orders}"
Trend="ktUp" FooterText="this month"/>
</sgc:TsgcAvaloniaDashboardTile>
<sgc:TsgcAvaloniaDashboardTile Title="Support" Col="8" Row="0" ColSpan="4" RowSpan="2">
<sgc:TsgcAvaloniaKPICard x:Name="kpiTickets" Title="Open tickets" Value="{Binding Tickets}"
Trend="ktDown" FooterText="help desk"/>
</sgc:TsgcAvaloniaDashboardTile>
<sgc:TsgcAvaloniaDashboardTile Title="Visits per day" Col="0" Row="2" ColSpan="12" RowSpan="3">
<sgc:TsgcAvaloniaChart x:Name="chartVisits">
<sgc:TsgcAvaloniaChartSeries ItemsSource="{Binding Visits}" XMember="Time" YMember="Value"
Title="Visits" Color="#FF16A34A"/>
<sgc:TsgcAvaloniaChart.Axes>
<sgc:TsgcAvaloniaChartAxis Name="X" AxisType="catDateTime"/>
<sgc:TsgcAvaloniaChartAxis Name="Y" Title="Visits"/>
</sgc:TsgcAvaloniaChart.Axes>
</sgc:TsgcAvaloniaChart>
</sgc:TsgcAvaloniaDashboardTile>
</sgc:TsgcAvaloniaDashboard>
Auch in Avalonia Pro: Grid, Diagramm, PDF-Viewer und Rich Text
Wenn Sie ein sich überschneidendes Steuerelement tauschen, ändert sich die Ansicht, und das View-Modell bleibt. Das Grid mit deklarierten Spalten und einem bidirektionalen SelectedItem:
MainWindow.axaml<sgc:TsgcAvaloniaGrid x:Name="grid" x:DataType="vm:TGridViewModel" Margin="8"
ItemsSource="{Binding Customers}" SelectedItem="{Binding Selected}"
KeyMember="Id" AutoGenerateColumns="False">
<sgc:TsgcAvaloniaGridColumn FieldName="Id" Caption="Id" Width="60" DataType="gdtInteger"/>
<sgc:TsgcAvaloniaGridColumn FieldName="Name" Caption="Customer" Width="220"/>
<sgc:TsgcAvaloniaGridColumn FieldName="City" Caption="City" Width="140"/>
<sgc:TsgcAvaloniaGridColumn FieldName="Balance" Caption="Balance" Width="130" DataType="gdtFloat"
DisplayFormat="#,##0.00" Alignment="gaRight"/>
</sgc:TsgcAvaloniaGrid>
Ein Liniendiagramm über einer Datum-Uhrzeit-Achse:
MainWindow.axaml<sgc:TsgcAvaloniaChart x:Name="chartPrice" x:DataType="vm:TChartViewModel" Margin="8">
<sgc:TsgcAvaloniaChartSeries ItemsSource="{Binding Prices}" XMember="Time" YMember="Value"
Title="Price" Color="#FF2563EB"/>
<sgc:TsgcAvaloniaChart.Axes>
<sgc:TsgcAvaloniaChartAxis Name="X" AxisType="catDateTime" Title="Time"/>
<sgc:TsgcAvaloniaChartAxis Name="Y" Title="Price"/>
</sgc:TsgcAvaloniaChart.Axes>
</sgc:TsgcAvaloniaChart>
Ein PDF-Viewer mit Miniaturansichten, gebunden an ein TsgcPDFDocument des View-Modells:
MainWindow.axaml<sgc:TsgcAvaloniaPDFThumbnailView x:Name="pdfThumbs" Viewer="{Binding #pdfViewer}"/>
<sgc:TsgcAvaloniaPDFViewer x:Name="pdfViewer" Grid.Column="1" Margin="8,0,0,0"
Document="{Binding Document}" ZoomMode="zmFitWidth"/>
Das View-Modell baut das Dokument mit sgcPDF, speichert es im Speicher und parst es zurück, so wie ein aus einer Datei gelesenes Dokument den Viewer erreicht:
ViewModels\TOverlapViewModels.cs// a three page A4 report written with sgcPDF, saved to memory and parsed back,
// the way a document read from a file or a stream reaches the viewer
private static TsgcPDFDocument DoBuild()
{
byte[] vBytes;
using (TsgcPDFDocument oWriter = new TsgcPDFDocument())
{
for (int i = 1; i <= CS_PAGES; i++)
{
TsgcPDFPage oPage = oWriter.AddPage(595, 842);
oPage.DrawRectangleFill(0, 772, 595, 70, 0.12, 0.23, 0.54);
oPage.DrawTextColor("Quarterly report, page " + i + " of " + CS_PAGES, 50, 800,
TsgcPDFStandardFont.sfHelveticaBold, 20, 1, 1, 1);
oPage.DrawText("Built in code with sgcPDF (TsgcPDFDocument) and shown by TsgcAvaloniaPDFViewer.", 50,
730, TsgcPDFStandardFont.sfHelvetica, 12);
oPage.DrawText("Search, zoom, thumbnails and text selection work on it like on any PDF file.", 50,
710, TsgcPDFStandardFont.sfHelvetica, 12);
for (int k = 0; k < 6; k++)
oPage.DrawRectangleFill(60 + k * 80, 420, 50, 40 + ((i * 3 + k * 5) % 9) * 25, 0.15, 0.39, 0.92);
oPage.DrawRectangle(40, 40, 515, 712, 1.0);
}
using (MemoryStream oStream = new MemoryStream())
{
oWriter.SaveToStream(oStream);
vBytes = oStream.ToArray();
}
}
TsgcPDFDocument vResult = new TsgcPDFDocument();
vResult.LoadFromBytes(vBytes);
return vResult;
}
Der Rich-Text-Editor, als Markdown-Text gebunden (sgcRichEdit ist nur Registered):
Views\RichEditTab.axaml<sgc:TsgcAvaloniaRichEdit x:Name="richEdit" Margin="8" DocumentFormat="refMarkdown"
DocumentText="{Binding Text}"/>
ViewModels\TOverlapViewModels.cspublic sealed class TRichTextViewModel : TViewModelBase
{
private string FText =
"# Release notes\n\nThe **sgcRichEdit** control is bound to this view model through " +
"*DocumentText* in Markdown.\n\n- Styles, lists and tables\n- Links such as " +
"[www.esegece.com](https://www.esegece.com)\n- HTML, RTF, Markdown and JSON in and out\n";
/// <summary>The document as Markdown (DocumentFormat="refMarkdown"), written back on focus loss.</summary>
public string Text
{
get { return FText; }
set { DoSet(ref FText, value, "Text"); }
}
}
Das Theme bleibt, wo es ist
Die Anwendung behält FluentTheme. Die sgcUI-Steuerelemente folgen seiner Themevariante, hell, dunkel oder der Einstellung des Betriebssystems, und wechseln live mit den Fluent-Steuerelementen:
Program.cspublic override void Initialize()
{
// the existing host theme stays: the sgcUI controls follow its theme variant
Styles.Add(new FluentTheme());
RequestedThemeVariant = ThemeVariant.Default;
}
ViewModels\TOverlapViewModels.cs/// <summary>0 Follow host (RequestedThemeVariant Default), 1 Light, 2 Dark. The sgcUI controls
/// follow the Avalonia theme variant of the FluentTheme host (ThemeFollow utfHost).</summary>
public int ModeIndex
{
get { return FModeIndex; }
set
{
if (value < 0 || value > 2 || !DoSet(ref FModeIndex, value, "ModeIndex"))
return;
sgcUIApplication.ThemeFollow = TsgcUIThemeFollow.utfHost;
sgcUIApplication.SetTheme(TsgcUITheme.utDefault);
if (Application.Current != null)
Application.Current.RequestedThemeVariant = value == 1 ? ThemeVariant.Light :
value == 2 ? ThemeVariant.Dark : ThemeVariant.Default;
}
}