Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Leer code te schrijven voor een aangepaste Panel-klasse , implementeer de methoden ArrangeOverride en MeasureOverride en gebruik de eigenschap Bovenliggend .
Belangrijke API's: Panel, ArrangeOverride,MeasureOverride
In de voorbeeldcode ziet u een implementatie van een aangepast deelvenster, maar we besteden niet veel tijd aan het uitleggen van de indelingsconcepten die van invloed zijn op de manier waarop u een deelvenster kunt aanpassen voor verschillende indelingsscenario's. Als u meer informatie wilt over deze lay-outconcepten en hoe deze van toepassing kunnen zijn op uw specifieke lay-outscenario, raadpleegt u Overzicht van aangepaste XAML-panelen.
Een deelvenster is een object dat een lay-outgedrag biedt voor onderliggende elementen die het bevat, wanneer het XAML-lay-outsysteem wordt uitgevoerd en de gebruikersinterface van uw app wordt weergegeven. U kunt aangepaste panelen definiëren voor de XAML-indeling door een aangepaste klasse te afleiden uit het paneel klasse. Je geeft gedrag voor je paneel door de methoden ArrangeOverride en MeasureOverride te overschrijven, waarbij logica wordt opgegeven waarmee de subelementen worden gemeten en geordend. Dit voorbeeld is afgeleid van Panel. Wanneer u begint vanaf paneel , hebben de methoden ArrangeOverride en MeasureOverride geen begingedrag. Uw code biedt de gateway waarmee kind elementen zichtbaar worden voor het XAML-indelingssysteem en worden weergegeven in de gebruikersinterface. Het is dus erg belangrijk dat uw code rekening houdt met alle onderliggende elementen en de patronen volgt die het indelingssysteem verwacht.
Uw lay-outscenario
Wanneer u een aangepast deelvenster definieert, definieert u een indelingsscenario.
Een lay-outscenario wordt uitgedrukt door:
- Wat het paneel doet wanneer het subelementen bevat
- Wanneer het paneel beperkingen heeft in zijn eigen ruimte
- Hoe de logica van het paneel alle afmetingen, plaatsingen, posities en groottes bepaalt die uiteindelijk resulteren in een weergegeven gebruikersinterface-indeling van kindelementen.
Met dat in gedachten is het BoxPanel hier getoonde voor een bepaald scenario. In het belang van het plaatsen van de code op de voorgrond in dit voorbeeld, zullen we het scenario nog niet in detail uitleggen, maar ons in plaats daarvan concentreren op de benodigde stappen en de codepatronen. Als u eerst meer wilt weten over het scenario, gaat u verder naar "Het scenario voor BoxPanel" en keert u terug naar de code.
Begin met ontleiden vanaf paneel
Begin met het afleiden van een aangepaste klasse uit Panel. De eenvoudigste manier om dit te doen, is waarschijnlijk door een afzonderlijk codebestand voor deze klasse te definiëren met behulp van de contextmenuopties vanNieuwe | toevoegen | voor een project vanuit de Solution Explorer in Microsoft Visual Studio. Geef de klasse (en het bestand) een naam. BoxPanel
Het sjabloonbestand voor een klasse begint niet met veel met-verklaringen, omdat het niet specifiek is voor Windows-toepassingen. Voeg eerst toe met behulp van instructies. Het sjabloonbestand begint ook met een paar met uitspraken die u waarschijnlijk niet nodig hebt en kunnen worden verwijderd. Hier volgt een voorgestelde lijst met met behulp van-instructies waarmee u typen kunt oplossen die u nodig hebt voor typische aangepaste paneelcode:
using System;
using System.Collections.Generic; // if you need to cast IEnumerable for iteration, or define your own collection properties
using Windows.Foundation; // Point, Size, and Rect
using Windows.UI.Xaml; // DependencyObject, UIElement, and FrameworkElement
using Windows.UI.Xaml.Controls; // Panel
using Windows.UI.Xaml.Media; // if you need Brushes or other utilities
Nu u Panel kunt oplossen, maakt u er de basisklasse van BoxPanel. Maak BoxPanel ook openbaar:
public class BoxPanel : Panel
{
}
Definieer op klasseniveau enkele int - en dubbele waarden die worden gedeeld door verschillende van uw logische functies, maar die niet hoeven te worden weergegeven als openbare API. In het voorbeeld worden deze genoemd: maxrc, rowcount, colcount, , cellwidthcellheight, maxcellheight, . aspectratio
Nadat je dit hebt gedaan, ziet het volledige codebestand er als volgt uit (opmerkingen over het gebruik verwijderen, nu je weet waarom we ze hebben):
using System;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
public class BoxPanel : Panel
{
int maxrc, rowcount, colcount;
double cellwidth, cellheight, maxcellheight, aspectratio;
}
Vanaf dit punt laten we u telkens één liddefinitie zien, of dat nu een methode-override is of iets ondersteunends zoals een afhankelijkheidseigenschap. Deze kun je in willekeurige volgorde toevoegen aan het skelet hierboven.
MeasureOverride
protected override Size MeasureOverride(Size availableSize)
{
// Determine the square that can contain this number of items.
maxrc = (int)Math.Ceiling(Math.Sqrt(Children.Count));
// Get an aspect ratio from availableSize, decides whether to trim row or column.
aspectratio = availableSize.Width / availableSize.Height;
// Now trim this square down to a rect, many times an entire row or column can be omitted.
if (aspectratio > 1)
{
rowcount = maxrc;
colcount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
}
else
{
rowcount = (maxrc > 2 && Children.Count <= maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
colcount = maxrc;
}
// Now that we have a column count, divide available horizontal, that's our cell width.
cellwidth = (int)Math.Floor(availableSize.Width / colcount);
// Next get a cell height, same logic of dividing available vertical by rowcount.
cellheight = Double.IsInfinity(availableSize.Height) ? Double.PositiveInfinity : availableSize.Height / rowcount;
foreach (UIElement child in Children)
{
child.Measure(new Size(cellwidth, cellheight));
maxcellheight = (child.DesiredSize.Height > maxcellheight) ? child.DesiredSize.Height : maxcellheight;
}
return LimitUnboundedSize(availableSize);
}
Het noodzakelijke patroon van een MeasureOverride implementatie is de lus door elk element in Panel.Children. Roep altijd de methode Meten aan voor elk van deze elementen. Measure heeft een parameter van het type Size. Wat u hier doorgeeft, is de grootte die uw deelvenster toewijst om beschikbaar te zijn voor dat specifieke onderliggende element. Dus voordat u de lus kunt uitvoeren en begint met het aanroepen van Measure, moet u weten hoeveel ruimte elke cel kan toewijzen. Vanuit de methode MeasureOverride zelf hebt u de availableSize waarde. Dat is de grootte die het bovenliggende element van het paneel gebruikte toen het Measureaanriep, wat de trigger was voor het aanroepen van deze MeasureOverride in de eerste plaats. Dus een typische logica is om een schema te bedenken waarbij elk kind-element de ruimte van de totale availableSizevan het paneel verdeelt. Vervolgens geeft u elke verdeling van grootte door aan Meting van elk onderliggend element.
Hoe BoxPanel de grootte verdeelt, is vrij eenvoudig: het verdeelt zijn ruimte in een aantal dozen dat grotendeels wordt bepaald door het aantal items. De afmetingen van vakken worden bepaald door het aantal rijen en kolommen en de beschikbare grootte. Soms is één rij of kolom van een vierkant niet nodig en wordt het verwijderd, waardoor het paneel een rechthoek wordt in plaats van een vierkant, in termen van de rij:kolomverhouding. Voor meer informatie over hoe deze logica tot stand is gekomen, gaat u verder naar "Het scenario voor BoxPanel".
Wat houdt de goedkeuring van de maatregel in? Het stelt een waarde in voor de alleen-lezen eigenschap DesiredSize voor elk element waarop Measure is aangeroepen. Een DesiredSize waarde is mogelijk belangrijk zodra u bij de arrangeerfase komt, omdat de DesiredSize aangeeft hoe groot het moet of kan zijn bij het rangschikken en de uiteindelijke weergave. Zelfs als u DesiredSize niet in uw eigen logica gebruikt, heeft het systeem het nog steeds nodig.
Het is mogelijk om dit paneel te gebruiken wanneer de hoogtecomponent van availableSize onbeperkt is. Als dat het geval is, heeft het paneel geen bekende hoogte om te verdelen. In dit geval informeert de logica voor de meting elk kind dat deze nog geen begrensde hoogte heeft. Dit doet u door een Grootte door te geven aan de Meting aanroep voor kinderen waarbij Size.Height oneindig is. Dat is legaal. Wanneer Meten wordt aangeroepen, is de logica dat de DesiredSize- als minimum wordt ingesteld op een van de volgende: wat aan Metenis doorgegeven, of de natuurlijke grootte van dat element door factoren zoals expliciet ingestelde Hoogte en Breedte.
Opmerking
De interne logica van StackPanel- vertoont ook dit gedrag: StackPanel geeft een oneindige dimensiewaarde door aan Measure van de onderliggende elementen, wat aangeeft dat er in de oriëntatiedimensie geen beperking is voor deze. StackPanel de grootte ervan doorgaans dynamisch aanpassen aan alle kinderen in een stapel die in die dimensie groeit.
Het paneel zelf kan echter geen Grootte retourneren met een oneindige waarde van MeasureOverride-; die een uitzondering genereert tijdens de indeling. Een deel van de logica is om de maximale hoogte te bepalen die een kind aanvraagt en die hoogte te gebruiken als de celhoogte in het geval dat dit niet al afkomstig is van de eigen groottebeperkingen van het paneel. Hier volgt de helperfunctie LimitUnboundedSize waarnaar in de vorige code is verwezen, die vervolgens die maximale celhoogte gebruikt en gebruikt om het deelvenster een eindige hoogte te geven om terug te keren, en te verzekeren dat cellheight een eindig getal is voordat de rangschikkenpas wordt gestart:
// This method limits the panel height when no limit is imposed by the panel's parent.
// That can happen to height if the panel is close to the root of main app window.
// In this case, base the height of a cell on the max height from desired size
// and base the height of the panel on that number times the #rows.
Size LimitUnboundedSize(Size input)
{
if (Double.IsInfinity(input.Height))
{
input.Height = maxcellheight * colcount;
cellheight = maxcellheight;
}
return input;
}
SchikkenOverschrijven
protected override Size ArrangeOverride(Size finalSize)
{
int count = 1;
double x, y;
foreach (UIElement child in Children)
{
x = (count - 1) % colcount * cellwidth;
y = ((int)(count - 1) / colcount) * cellheight;
Point anchorPoint = new Point(x, y);
child.Arrange(new Rect(anchorPoint, child.DesiredSize));
count++;
}
return finalSize;
}
Het noodzakelijke patroon van een ArrangeOverride implementatie is de lus door elk element in Panel.Children. Roep altijd de methode Schikken aan voor elk van deze elementen.
Merk op dat er niet zoveel berekeningen zijn als in MeasureOverride; Dat is typisch. De grootte van kinderen is al bekend uit de eigen logica van het paneel voor MeasureOverride, of uit de DesiredSize-waarde van elk kind dat is ingesteld tijdens de meetcyclus. We moeten echter nog steeds beslissen waar elk kind in het paneel zal verschijnen. In een gewoon paneel moet elk kind op een andere positie worden weergegeven. Een paneel dat overlappende elementen maakt, is niet wenselijk voor typische scenario's (hoewel het niet uitgesloten is om panelen te maken met doelgerichte overlappingen, als dat echt het beoogde scenario is).
Dit paneel is gerangschikt volgens het concept van rijen en kolommen. Het aantal rijen en kolommen was al berekend (dit was nodig voor de meting). Dus nu dragen de vorm van de rijen en kolommen plus de bekende groottes van elke cel bij aan de logica van het definiëren van een renderpositie (de anchorPoint) voor elk element dat dit paneel bevat. Dat Punt, samen met de Grootte zoals al bekend uit de meting, wordt gebruikt als de twee onderdelen die een Rechthoekmaken.
Rect is het invoertype voor Arrange.
Panelen moeten soms hun inhoud knippen. Als dat gebeurt, is de bijgesneden grootte de grootte die aanwezig is in DesiredSize, omdat de logica van het Meetproces deze instelt als het minimum van wat is doorgegeven aan het Meetprocesof andere factoren van natuurlijke grootte. U hoeft dus meestal niet specifiek te controleren op afkapping tijdens ; de afkapping zal gewoon plaatsvinden door de DesiredSize door te geven aan elke aanroep.
Je hebt niet altijd een telling nodig tijdens het doorlopen van de lus als alle informatie die je nodig hebt voor het bepalen van de renderpositie op een andere manier bekend is. Bijvoorbeeld in de indelingslogica van Canvas maakt de positie in de Kinderen verzameling niet uit. Alle benodigde informatie om elk element in een Canvas- te positioneren, is bekend door de waarden van Canvas.Left en Canvas.Top van de onderliggende elementen te lezen als onderdeel van de ordelogica. De BoxPanel-logica heeft toevallig een telling nodig om te vergelijken met het kolomaantal, zodat duidelijk is wanneer een nieuwe rij moet worden gestart en de y-waarde moet worden verschoven.
Het is typisch dat de invoer eindgrootte en de grootte die u retourneert van een implementatie van ArrangeOverride hetzelfde zijn. Zie voor meer informatie over de reden waarom in de sectie "ArrangeOverride" van het overzicht van aangepaste XAML-panelen .
Een verfijning: het aantal rijen versus kolommen regelen
U zou dit paneel kunnen samenstellen en gebruiken zoals het nu is. We voegen echter nog een verfijning toe. In de zojuist getoonde code plaatst de logica de extra rij of kolom aan de kant die de langste beeldverhouding heeft. Maar voor betere controle over de vormen van cellen kan het wenselijk zijn om een 4x3-set cellen te kiezen in plaats van 3x4, zelfs als de eigen hoogte-breedteverhouding van het paneel 'staand' is. We voegen dus een optionele afhankelijkheidseigenschap toe die de gebruiker van het paneel kan instellen om dat gedrag te beheren. Hier is de definitie van de afhankelijkheidseigenschap, die erg eenvoudig is:
// Property
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
// Dependency Property Registration
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register(nameof(Orientation), typeof(Orientation), typeof(BoxPanel), new PropertyMetadata(null, OnOrientationChanged));
// Changed callback so we invalidate our layout when the property changes.
private static void OnOrientationChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
if (dependencyObject is BoxPanel panel)
{
panel.InvalidateMeasure();
}
}
En hieronder ziet u hoe het gebruik Orientation de meetlogica in MeasureOverride. Het enige wat het echt doet, is veranderen hoe rowcount en colcount zijn afgeleid van maxrc en de ware beeldverhouding, en daardoor zijn er overeenkomstige grootteverschillen voor elke cel. Wanneer Orientation de Verticale (standaard) is, wordt de waarde van de werkelijke hoogte-breedteverhouding omgekeerd voordat deze wordt gebruikt voor het bepalen van het aantal rijen en kolommen voor onze "portret" rechthoekopmaak.
// Get an aspect ratio from availableSize, decides whether to trim row or column.
aspectratio = availableSize.Width / availableSize.Height;
// Transpose aspect ratio based on Orientation property.
if (Orientation == Orientation.Vertical) { aspectratio = 1 / aspectratio; }
Het scenario voor BoxPanel
Het specifieke scenario voor BoxPanel is dat het een paneel is waarin een van de belangrijkste determinanten van hoe de ruimte wordt ingedeeld, is door het aantal onderliggende items te kennen en de bekende beschikbare ruimte voor het paneel in te delen. Panelen zijn van nature rechthoekige vormen. Veel panelen werken door die rechthoekige ruimte in verdere rechthoeken te verdelen; dat is wat Grid doet voor zijn cellen. In het geval van Grid wordt de grootte van de cellen bepaald door de waarden ColumnDefinition en RowDefinition , en elementen declareren de exacte cel waarin ze gaan met de eigenschappen Grid.Row en Grid.Column . Het verkrijgen van een goede indeling van een Raster vereist meestal dat u het aantal kindelementen vooraf kent, zodat er voldoende cellen zijn en elk kindelement zijn gekoppelde eigenschappen instelt om in een eigen cel te passen.
Maar wat als het aantal kinderen dynamisch is? Dat is zeker mogelijk; Met uw app-code kunnen items aan verzamelingen worden toegevoegd als reactie op een dynamische runtime-voorwaarde die u belangrijk genoeg vindt om uw gebruikersinterface bij te werken. Als u gegevensbinding gebruikt voor het maken van back-ups van verzamelingen/bedrijfsobjecten, wordt het verkrijgen van dergelijke updates en het bijwerken van de gebruikersinterface automatisch afgehandeld, dus dat is vaak de voorkeurstechniek (zie Uitgebreide gegevensbinding).
Maar niet alle app-scenario's lenen zich voor gegevensbinding. Soms moet u tijdens runtime nieuwe UI-elementen maken en deze zichtbaar maken.
BoxPanel is voor dit scenario. Een veranderend aantal subitems is geen probleem voor BoxPanel omdat het het aantal subitems in berekeningen gebruikt en zowel de bestaande als de nieuwe subelementen in een nieuwe indeling aanpast zodat ze allemaal passen.
Een geavanceerd scenario voor het verder uitbreiden van BoxPanel (niet hier weergegeven) zou zowel dynamische kinderen kunnen accommoderen als ook de DesiredSize van een kind kunnen gebruiken als een sterkere factor bij het bepalen van de grootte van individuele cellen. In dit scenario kunnen verschillende rij- of kolomgroottes of niet-rastervormen worden gebruikt, zodat er minder 'verspilde' ruimte is. Hiervoor is een strategie vereist voor hoe meerdere rechthoeken van verschillende grootten en hoogte-breedteverhoudingen allemaal in een omsluitende rechthoek passen, zowel voor esthetiek als voor de kleinste maat.
BoxPanel doet dat niet; Het gebruikt een eenvoudigere techniek om ruimte te verdelen. De techniek van BoxPanelis het bepalen van het kleinste kwadraatgetal dat groter is dan het aantal kinderen. Er passen bijvoorbeeld 9 items in een vierkant van 3x3. Voor 10 items is een 4x4 vierkant vereist. U kunt echter vaak items plaatsen, terwijl u nog steeds een rij of kolom van het beginvierkant verwijdert om ruimte te besparen. In het voorbeeld count=10 past dat in een rechthoek van 4x3 of 3x4.
Je vraagt je misschien af waarom het paneel niet in plaats daarvan 5x2 zou kiezen voor 10 items, want dat past netjes bij het artikelnummer. In de praktijk zijn panelen echter formaat als rechthoeken die zelden een sterk georiënteerde hoogte-breedteverhouding hebben. De methode van de kleinste kwadraten is een manier om de formaatlogica te vertekenen zodat deze goed werkt met typische indelingen en niet aanmoedigt dat celvormen ongebruikelijke verhoudingen krijgen.
Verwante onderwerpen
Verwijzing
Concepten
Windows developer