Laden...

In MeasureOverride Controls erzeugen und in ArrangeOverride platzieren

Erstellt von Quaneu vor 7 Jahren Letzter Beitrag vor 7 Jahren 1.726 Views
Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren
In MeasureOverride Controls erzeugen und in ArrangeOverride platzieren

Hallo zusammen,

ich habe eine Matrix (CustomControl) gebastelt, die "beliebig" große Matrizen darstellen kann. Dazu muss diese aber in MeasureOverride() die MatrixElemente anlegen:


Children.Clear();

...

RowHeaderControl rowHeaderControl = new RowHeaderControl { DataContext = Matrix.Rows[rowIndex], RowIndex = currentRowIndex, ColumnIndex = 0 };
Children.Add(rowHeaderControl);
rowHeaderControl.Measure(InfinitySize);

Children ist ein Property der Klasse, die von Panel ableitet.

Dadurch schaffe ich es, dass nur so viele Elemente wie nötig anzeigt werden. Jetzt frage ich mich aber, ob dies Probleme verursachen kann. Z.B. habe ich folgendes DataTemplate:


<DataTemplate DataType="{x:Type viewModels:MatrixElementViewModel}">
	<StackPanel >
		<TextBlock Text="{Binding Name}" Margin="2" />
		<TextBlock Text="{Binding Description}" Margin="2" />
		<StackPanel.Style>
			<Style>
				<Style.Triggers>
					<DataTrigger Binding="{Binding Path=Type}" Value="{x:Static viewModels:MatrixElementType.None}">
						<Setter Property="StackPanel.Background" Value="LightGreen" />
					</DataTrigger>
					<EventTrigger RoutedEvent="FrameworkElement.MouseEnter">
						<EventTrigger.Actions>
							<BeginStoryboard>
								<Storyboard>
									<DoubleAnimation Duration="0:0:0.300" Storyboard.TargetProperty="Opacity" To="0.2" />
									<ThicknessAnimation Storyboard.TargetProperty="Margin" Duration="0:0:1.5"
											FillBehavior="HoldEnd" From="1,1,1,1" To="28,14,28,14" />
								</Storyboard>
							</BeginStoryboard>
						</EventTrigger.Actions>
					</EventTrigger>
				</Style.Triggers>		
			</Style>
		</StackPanel.Style>
	</StackPanel>
</DataTemplate>

Wenn ich nun die Maus über ein Element bewege, ändert sich die Opacity wie gewünscht, aber Margin ändert sich nicht. Er kommt in MeasureOverride() und ArrangeOverride() aber bei Measure() hat sich die DesiredSize nicht geändert.

Schöne Grüße
Quaneu

5.655 Beiträge seit 2006
vor 7 Jahren

Hi Quaneu,

ich glaube nicht, daß die MeasureOverride-Methode dafür geeignet ist, dort neue Controls zu erstellen. Abgesehen davon, wird dabei wahrscheinlich die Größe und Anordnung der Elemente überschrieben und ist daher nicht per Animation änderbar.

Insgesamt ist das eine sehr merkwürdige Vorgehensweise. Welche Anforderungen hast du denn an das Layout, daß die normalen Layout-Elemente wie Grids nicht ausreichen?

Christian

Weeks of programming can save you hours of planning

Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren

Hallo Christian,

die Anforderung lautet: Stelle sehr große Matrizen dar. D.h. mind. 1000x1000, es muss zwar nicht in jeder Zelle etwas stehen, aber wenn ich 10000 gefüllte Zellen habe ist es normal.
Wenn ich jetzt ein Grid nehme und alles reinpacke gehe ich OutOfMemory oder es braucht ewig.
Daher will ich nur sovielmal zeichnen wie auf den Bilschirm passen. D.h. in MeasureOverride "measure" ich nun Zelle für Zelle bis der Platz voll ist. Somit werden nur sovielmal Controls erzeugt und angezeigt wie wirklich auf den Bildschirm passen.
Wenn ich doch z.B. an eine Listbox eine Liste mit 1000000 ViewModels binde, werden doch auch nicht 1000000 UserControls erzeugt, sonder eben nur z.B. 50. Dasselbe wollte ich nun auch machen bzw. probieren.

Schöne Grüße
Quaneu

5.655 Beiträge seit 2006
vor 7 Jahren

Es gibt sicherlich bessere Wege, um ans Ziel zu kommen. Auf jeden Fall würde ich darauf verzichten, im Code-behind immer neue Controls zu erstellen.

Besser wäre es, die Logik (also welche Daten angezeigt werden sollen) im ViewModel unterzubringen. Du kannst ja eine bestimmte Anzahl von Zeilen und Spalten in der View darstellen, und die gerade sichtbaren Daten im ViewModel ermitteln und binden.

Eine andere Möglichkeit wäre, ein DataGrid zu verwenden. Das unterstützt bereits Virtualisierung, d.h. es werden nur die gerade sichtbaren Daten geladen. Wenn das nicht zu deinen Anforderungen paßt, google mal nach "wpf virtualizing grid" oder sowas.

Christian

Weeks of programming can save you hours of planning

Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren

Besser wäre es, die Logik (also welche Daten angezeigt werden sollen) im ViewModel unterzubringen. Du kannst ja eine bestimmte Anzahl von Zeilen und Spalten in der View darstellen, und die gerade sichtbaren Daten im ViewModel ermitteln und binden.

Um die Größe von einem "Viemodel" bzw. dessen UserControl zu bekommen muss man auf dem UserControl Measure() aufrufen. Dies funktioniert aber nur wenn es dem Panel hinzugefügt wird. Daher kann ich diese Logik nicht ins ViewModel packen.

Ich werde mal googeln. Danke für den Hinweis. Ich frage mich nur wie es z.B. das Datgrid oder die ListBox macht. Werde mir wohl mal den SourceCode anschauen 😃.

Aber vielen Dank für deine Hilfe.

5.655 Beiträge seit 2006
vor 7 Jahren

Um die Größe von einem "Viemodel" bzw. dessen UserControl zu bekommen muss man auf dem UserControl Measure() aufrufen.

Eine andere Möglichkeit wäre, die Höhe und Breite des Controls an entsprechende Eigenschaften des ViewModels zu binden, und bei einer Änderung den anzuzeigenden Bereich neu zu berechnen.

Aber warum sollte man das Rad neu erfinden, wenn der Framework diese Funktionalität bereits implementiert...?

Christian

Weeks of programming can save you hours of planning

1.378 Beiträge seit 2006
vor 7 Jahren

Hallo Quaneu,

wenn dein Matrixcontrol nur ein Layout-Container sein soll, dann wärs schöner, wenn er sich nur ums Layout kümmert. Die Visuelle Darstellung würde ich dann entweder per DataTemplates ganz ausserhalb oder in einer Zwischenschicht realisieren.

Bsp:

MatrixLayout wie im Beispiel hier beschrieben: How to create a Custom Layout Panel in WPF

Darüber ein MatrixControl, welches dann aus einem ItemsControl mit MatrixLayout als ItemsPanelTemplate verwendet sowie aus einem custom ItemTemplate besteht. Passend dazu brauchst du dann natürlich attached Properties (wie Grid.Row) um den Child-Elementen die richtige Zeile/Spalte zuzuweisen (oder halt irgendwie automatisch setzen), welche dann vom MatrixLayout verwendet wird um die richtigen Controls zu platzieren.

Aja bzgl. Virtualisierung die in Listbox oder DataGrid stattfindet, hier ein Beispiel: WPF: Custom Virtualizing Panel For TreeView

Du müsstest dein MatrixLayout also von VirtualizingPanel erben lassen.

Lg, XXX

Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren

Hallo xxxprod,
Hallo MrSparkle,

also meine Matrix ist ein Panel bzw. erbt davon, d.h. es ist ein Layout-Container. Die ViewModels werden über DataTemplates dargestellt und somit weiß ein ViewModel gar nicht wie groß es sein soll. Und jede Zelle kann unterschiedlich groß sein. Daher kann ich auch nicht vor berechnen. Und ich will auch nicht das die ViewModels die größe vorgeben. Sie sollen alle soviel Platz bekommen wie sie brauche, oder man stellt im Style die Width oder Height ein.
Daher wusste ich mir eben nicht anders zu helfen als in MeasureOverride() meine "Berechnung" vorzunehmen. Es klappt auch ganz gut, eine 1000x1000 schaffe ich in 200ms. Aber ich weiß eben nicht ob man es so machen darf 😦.

Schöne Grüße
Quaneu

j
641 Beiträge seit 2007
vor 7 Jahren

Poste doch mal den Code deines Controls (wenn möglich)!

cSharp Projekte : https://github.com/jogibear9988

742 Beiträge seit 2005
vor 7 Jahren

Ich finde das ist überhaupt nicht die Aufgabe des ViewModels sondern die richtige Aufgabe des Controls. Im ViewModel sollte es auch kein großes Problem sein, 1.000.000 Einträge zu halten. Je nach Daten hast du ja nur 3-4 MB.

Für mich geht das in Richtung VirtualizingPanel. https://uhimaniavwp.codeplex.com/

Wenn das gut funktioniert brauchst du gar kein CustomControl mehr, du kannst natürlich davon erben, insbesondere wenn du ein eigenes Format für VM's hast (ähnlich zu ObservableCollection).

Btw: In der Doku zu VirtualizingPanel schreibt MS auch, dass es keinen Sinn macht, Zustände im Container(= Item) zu speichern (z.B. IsExpanded). Macht ja auch Sinn, weil der Container für ein anderes Datenobjekt wiederverwendet werden kann.

Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren

@jogibear9988:
Muss ich erst frage.

Also soweit ich das gestern verstanden habe (es war schon sehr spät 😃). Werden in einem VirtualizingPanel auch in MeasureOverride() Controls erzeugt, jedoch eben auch wiederverwendet. Ich werfe immer alles weg und geniere neu. Ich probiere dies jetzt mal auch.

Quaneu Themenstarter:in
685 Beiträge seit 2008
vor 7 Jahren

Juhu... Sobald ich die Controls wiederverwende klappt es wunderbar.
Jetzt schaue ich mir aber trotzdem mal an wie das mit dem VirtualizingPanel geht. Klingt nämlich sehr interessant. Und vielleicht ist es ja auch schneller 😃.

Vielen Dank für die Hilfe 😃

Grüße
Quaneu