Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Portal
  • |
  • Mitglieder
Beiträge von OXO
Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Okay, überzeugt

Bin trotzdem froh, dass wir diesen Thread hatten! Danke Euch allen, dass Ihr Euch dazugeschalten habt!

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Zitat von Th69
@Oxo: Du hast Enums (immer noch) nicht verstanden!

Wieso meinst Du? :)
Finde schon, dass man bei den Enums auch zwischen Wert und dem eigentlichen Namen unterscheiden können muss - irgendwie zumindest (*kopf durch die wand*)

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Nicht unbedingt als String. Der Enum-Wert mit dem richtigen Text, wäre mir lieber, daher auch ein Typ des Enums als return-Value

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Das hatte ich auch schon. Aber hilft nicht so viel, aus 2 Gründen, zum einen, könnte ja jemand auf die Idee kommen und das ganze Flag umbenennen, dann würde der String-Vergleich auch nicht mehr gehen. Zum anderen, löst sich das nicht in das richtige Flag des Enums auf, obwohl die Namen anders sind, vergleiche sind ja nur die Namen.

Zitat von Th69
Im Debugger siehst du den Aufruf von Enum.GetName (s. bes. unter "Hinweise")!
Bei mehreren Enum-Namen mit gleichem Wert (Value) wird also irgendeiner dieser Namen ausgegeben!

Hhm, dann kann es wohl gar nicht gehen, oder bleibt ein Zufallsprodukt, wie Du schreibst :(

Siehe hier:

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Also das ist schon ein witziges Spielchen und ich sitze auf dem Schlauch.
Wenn ich das so versuche, sehe ich im Debugger zunächst verschiedene Namen in dem Array aus GetNames(..).

In einer foreach-Schleife auch noch bei der "names"-Auflistung. Kommt es aber dann zu der Zuweisung zur Variablen "name" in der foreach, dann steht da plötzlich 2x "Latest" drin


var names = Enum.GetNames(typeof(Versions));
foreach (var name in names)
{
    var parsedValue = (Versions)Enum.Parse(typeof(Versions), name);   // Cast zwar unnötig, aber zur Sicherheit auch ausprobiert
    if (parsedValue == latestVersion && !name.Equals(latestVersion.ToString()))
        return parsedValue;
}

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Eigentlich brauche ich alle Enum-Werte mit demselben int-Wert, wie auch immer ich das per Link sagen muss, und dann eben nicht den, der denselben Namen hat, wie der Name des vorgegebenen Enum-Werts "Latest"

Schaue ich in den Debugger, zeigt der mir bei allem, auch bei GetValues mit OrderBy immer 2x Latest an, was ich schon seltsam finde, ehrlich gesagt.

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Also Abt hatte Recht mit dem was er schreibt.
Die Versionen sind mit bestimmter Hardware verknüpft, die an anderer Stelle über passende Dateien eingepflegt und gewartet werden (das auch noch in verschiedenen Formaten, aber gut). Eine neuere Version wird also erst einmal als Master dort festgestellt. Intern kann man verschiedene nutzen, aber wie man sieht, muss das Enum über diesen Mechanismus dann immer nachgepflegt werden.

Das was ich mit Versions geschrieben hatte, war nur das Grundprinzip, um ein einfaches Beispiel für hier zu haben.

Auch die Kommentare, dass das ganze System Fehleranfällig ist, sehe ich genauso. Aktuell fällt mir nur nicht ein, wie ich sonst aus der Misere kommen könnte, wenn mehrere Stellen beteiligt sind und jeder Seins macht.

Würde sowas wie hier verlässlich funktionieren?
Also sprich, such mir alle Werte, die dem der latestVersion entsprechen und da es bei den Enums ja nur 2 sein können, nimm einfach das letzte aus der Liste.
Oder wird das zufällig ausgegeben dann?


private Versions GetLatestVersionAssigned(Versions latestVersion)
{
    var latestVersionAssigned = Enum.GetValues(typeof(Versions)).Cast<Versions>().Where(version => version == latestVersion).Last();
    return latestVersionAssigned;
}

Thema: Enum-Alternative im String ausgeben
Am im Forum: Grundlagen von C#

Hallo zusammen,

in einem enum gibt es verschiedene Werte in der Art:


enum Versions
{
    V1,
    V2,
    V3,
    Latest = V3,
}

In einem Test wird aus einer Master-Datei die aktuellste Version ermittelt.
Stellt sich raus, dass es bereits eine "V4" gibt, aber im enum "Latest" immer noch die "V3" eingestellt ist, dann wird der Test rot.

Im Assert des Tests wird die ausgelesene "V4" natürlich gegen "Lastest" geprüft.
Stimmen die beiden Versionen nicht überein, gibt NUnit folgendes aus:

Expected: V4
But was: Latest

Ich würde das aber gerne anders ausgeben und dort die Enum-Alternative als String haben:

Expected: V4
But was: V3


Wie könnte ich das hinbekommen?

Thema: Wieso wird meine DataGridViewComboBoxCell im DataGridView grau dargestellt?
Am im Forum: GUI: Windows-Forms

Also für meinen Fall hab ich die besten Ergebnisse doch mit dem TextRenderer. Falls jemand ähnliche "Probleme" hat, hier der Code im DrawItem, der für mich funktioniert:


private void dataGridViewComboBoxCell_DrawItem(object sender, DrawItemEventArgs e)
{
	if (e.Index == -1)
		return;

	e.DrawBackground();
	
	var comboBoxEditingControl = sender as DataGridViewComboBoxEditingControl;
	if (comboBoxEditingControl == null)
		return;

	Brush brush;
	if (comboBoxEditingControl.DroppedDown)
	{
		if ((e.State & DrawItemState.Focus) == DrawItemState.Focus)
		{
			brush = new SolidBrush(System.Drawing.Color.Transparent);
			e.Graphics.FillRectangle(brush, e.Bounds);
		}
		else
		{
			brush = new SolidBrush(System.Drawing.Color.White);
			e.Graphics.FillRectangle(brush, e.Bounds);
		}
	}
	else
	{
		brush = new SolidBrush(System.Drawing.Color.White);
		e.Graphics.FillRectangle(brush, e.Bounds);
	}


	var dataRowView = comboBoxEditingControl.Items[e.Index] as DataRowView;
	if (dataRowView == null)
		return;

	System.Drawing.Color fontColor;
	string itemText = dataRowView.Row.ItemArray[0].ToString();
	if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
	{
		if (comboBoxEditingControl.DroppedDown)
		{
			fontColor = System.Drawing.Color.White;
		}
		else
		{
			fontColor = System.Drawing.Color.Black;
		}
	}
	else
	{
		fontColor = System.Drawing.Color.Black;
	}

	var cellBounds = e.Bounds;
	cellBounds.X -= 2;
	cellBounds.Y -= 2;

	TextRenderer.DrawText(e.Graphics, itemText, 
		dataGridView.DefaultCellStyle.Font, 
		cellBounds, fontColor, 
		TextFormatFlags.NoPrefix | TextFormatFlags.VerticalCenter);
}

Thema: Wieso wird meine DataGridViewComboBoxCell im DataGridView grau dargestellt?
Am im Forum: GUI: Windows-Forms

So, nach Experimentieren bin ich weiter gekommen. Ob das jetzt schön ist, darüber kann man auch geteilter Meinung sein, aber zumindest ein FlatStyle hat ein bisschen was von dem grauen Hintergrund aufgelöst und auch der Pfeil an der ComboBox wird erst einmal nicht mehr per Default angezeigt, sondern erst wenn ich den Fokus in die Zelle setze.


DataGridViewComboBoxCell comboBoxCell = new DataGridViewComboBoxCell();
...
comboBoxCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
comboBoxCell.Style.SelectionBackColor = System.Drawing.Color.White;
comboBoxCell.FlatStyle = FlatStyle.Flat;

Dann waren noch die weiteren Farben und das Highlightings im DropDown ein Problem. Per Default war die Zelle, wenn sie fokussiert war, mit einem blauen Balken und störendem Focus-Rectangle ausgestattet. Mit Selbstzeichnen im DrawItem-Event konnte ich das aufgelösen:


private void dataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
	ComboBox cb = e.Control as ComboBox;
	if (cb != null)
	{
		cb.DropDownStyle = ComboBoxStyle.DropDownList;

		cb.DrawMode = DrawMode.OwnerDrawFixed;
		cb.DrawItem -= this.dataGridViewComboBoxCell_DrawItem;
		cb.DrawItem += this.dataGridViewComboBoxCell_DrawItem;
	}
}


private void dataGridViewComboBoxCell_DrawItem(object sender, DrawItemEventArgs e)
{
	if (e.Index == -1)
		return;

	e.DrawBackground();
	
	var comboBoxEditingControl = sender as DataGridViewComboBoxEditingControl;
	if (comboBoxEditingControl == null)
		return;

	Brush brush;
	if (comboBoxEditingControl.DroppedDown)
	{                
		if ((e.State & DrawItemState.Focus) == DrawItemState.Focus)
		{
			brush = new SolidBrush(e.BackColor);
			e.Graphics.FillRectangle(brush, e.Bounds);
		}
		else
		{
			brush = new SolidBrush(System.Drawing.Color.White);
			e.Graphics.FillRectangle(brush, e.Bounds);
		}
	}
	else
	{
		brush = new SolidBrush(System.Drawing.Color.White);
		e.Graphics.FillRectangle(brush, e.Bounds);
	}
			   

	var dataRowView = comboBoxEditingControl.Items[e.Index] as DataRowView;
	if (dataRowView == null)
		return;

	string itemText = dataRowView.Row.ItemArray[0].ToString();
	if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
	{
		if (comboBoxEditingControl.DroppedDown)
		{
			brush = Brushes.White;
		}
		else
		{
			brush = Brushes.Black;
		}
	}
	else
	{
		brush = Brushes.Black;
	}
				
	var font = new System.Drawing.Font("Calibri", 11);
	e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
	//e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
	e.Graphics.DrawString(itemText, font, brush, e.Bounds.X-1, e.Bounds.Y-1);
}

Wo ich noch verrückt werde, ist tatsächlich das Zeichnen des Textes mittetls DrawString.
Der AntiAlias bzw. AntiAliasGridFit haben zwar Veränderungen zum Positiven gebracht, aber der Text im fokussierten Zustand schaut trotzdem in der Vergrößerung anders aus. Im einen scheint es einen farbigen Schatten zu geben, wo im anderen die Farben auch fehlen. Ob das wirklich der Unterschied ist, oder was anderes, kann ich nicht sagen. Der Benutzer sieht, dass es zwischen Fokussiertem und nicht Fokussierten Zustand eine Änderung im Text gibt.

Ich hab schon mal ausprobiert, den Text mit dem TextRenderer.DrawString(...) zu zeichnen, aber auch da schaut das etwas komisch aus.

Habt Ihr mir noch Tipps, was ich machen kann, damit die Schrift in der selektierten DataGridViewComboBoxCell, der nicht selektierten und auch den anderen Zellen gleich gerendert werden? Hab ich da noch einen Einflussfaktor, oder muss ich mit selber Zeichnen damit leben?

Thema: Wieso wird meine DataGridViewComboBoxCell im DataGridView grau dargestellt?
Am im Forum: GUI: Windows-Forms

Muss gestehen, das hab ich jetzt leider noch nicht verstanden.

"Unused Spaced" habe ich in den Properties des DataGridView oder der DataGridViewComboBoxCell leider nicht gefunden. (?)

Die BackgroundColor-Eigenschaft des DataGridView hatte ich eigentlich schon auf White gesetzt, aber hat leider nicht den gewünschten Effekt auf die DataGridViewComboBoxCell gebracht. Die ComboBoxCell darunter bleibt leider grau und nicht weiß.

Thema: Wieso wird meine DataGridViewComboBoxCell im DataGridView grau dargestellt?
Am im Forum: GUI: Windows-Forms

Hallo zusammen,

ich hab in einem DataGridView eine Spalte mit DataGridViewComboBoxCell's


dataGridView.CellBorderStyle = DataGridViewCellBorderStyle.Single;
dataGridView.DefaultCellStyle.BackColor = System.Drawing.Color.White;
dataGridView.DefaultCellStyle.SelectionBackColor = System.Drawing.Color.White;
dataGridView.DefaultCellStyle.SelectionForeColor = System.Drawing.Color.Black;
dataGridView.BackgroundColor = System.Drawing.Color.White;
dataGridView.SelectionMode = DataGridViewSelectionMode.CellSelect;
dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;


DataGridViewComboBoxCell comboBoxCell = new DataGridViewComboBoxCell();
...
comboBoxCell.Style.BackColor = System.Drawing.Color.White;
comboBoxCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
comboBoxCell.Style.SelectionBackColor = System.Drawing.Color.White;

Mich wundert es, warum per Default meine ComboBox so grau angezeigt wird, obwohl ich überall mit Weiß im Hintergrund arbeite. Auch der Innenraum stellt sich mir beim Ändern des Backgrounds an anderer Stelle nicht auf weiß.

Die Optik aus dem Screenshot links fände ich auch ganz schön, aber egal wo ich Properties ändere, das ändert sich nicht in so ne Optik. Was sollte ich denn da noch setzen, dass so ne schöne Optik raus kommt?

Ebenfalls würde mir gefallen, wenn nach dem Klick in eine Zelle, der Pfeil des DropDowns nach rechts in die Nachbarzelle verschoben ist (also das gesamte DropDown breiter). Finde, das gibt nen schönen optischen Effekt. Ich hab schon mit DisplayEditingControl der Cell etc. im CellPainting-Ereginis herum experimentiert aber es gelingt mir nicht, das zu machen, außerdem flackert es dabei im GridView extrem.

Wie könnte ich das DropDown denn Breiter malen, so dass es aus der Zelle rechts noch ein Stück raus ragt bzw. auch schön in der Zelle selbst eingepasst ist. Aktuell wirkt es ja so, als ob es leicht nach unten verschoben ist, wenn es den Fokus hat? Wo müsste ich schrauben, damit ich da was beeinflussen kann?

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Zitat von Th69
Das kannst du über NuGet zu deinem Projekt hinzufügen: SourceGrid 4.4.0

Ahh, danke für den Hinweis! Da hatte ich noch gar nicht geschaut.

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Zitat von Th69
Zeichne doch besser den Doppel-Rahmen nur innerhalb einer Zelle (an Excel solltest du dich nicht 1:1 orientieren).

Also Du meinst, die Zelle nach innen verkleinern? Aktuell lasse ich ja den Innenraum der Zelle gleich groß und male die Linien nach außen hin
Zitat von Th69
Hast du dir denn jetzt schon mal das SourceGrid angeschaut?

Ja, auf die Seite bin ich gegangen und habe es mir auch mal heruntergeladen, aber war ehrlich gesagt nicht in der Lage, das zum Laufen zu bringen (https://archive.codeplex.com/?p=sourcegrid). Fertig gebaute Releases habe ich nicht gesehen, um es gleich zu benutzen. Dann wollte ich das über die *.sln öffnen (mit VS 2017 und auch VS 2019), aber da kommen Fehler.

Wie hast Du das gebaut bzw. zum Laufen bekommen?

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

In der Zwischenzeit hatte ich das Malen schon mal fertig gemacht, aber ja, es gibt komische Effekte, wie Du sie beschrieben hast.

Im Screenshot sieht man links wie der Effekt ist, wenn ich auf eine Zelle klicke. Schaut alles sauber aus soweit. Rechts sieht man, was passiert, wenn ich mit den Cursor-Tasten herum navigiere.

Kann man diesen von Dir beschriebenen Effekt nicht los werden?
Auch beim ersten Laden des DataGridViews sind Zellen noch nicht gemalt und etwas zerhexelt. Denke mir immer, die Profi-Tools (wie z.B. Excel) müssen es ja auch irgendwie sauber zeichnen.


if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex - 1 && e.RowIndex == dataGridView.CurrentCell.RowIndex - 1)
{
	// Cell left to selected cell row above
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (SolidBrush solidBrush = new SolidBrush(System.Drawing.Color.Red))
	{
		e.Graphics.FillRectangle(solidBrush, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y + e.CellBounds.Height - 2, 1, 1);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex && e.RowIndex == dataGridView.CurrentCell.RowIndex)
{
	// Selected Cell
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 3))
	{
		Rectangle rect = e.CellBounds;
		rect.X -= 1;
		rect.Y -= 1;
		e.Graphics.DrawRectangle(pen, rect);
	}

	using (Pen pen = new Pen(System.Drawing.Color.White, 1))
	{
		// Top, Bottom
		e.Graphics.DrawLine(pen, e.CellBounds.X - 2, e.CellBounds.Y - 1, e.CellBounds.X + e.CellBounds.Width, e.CellBounds.Y - 1);
		e.Graphics.DrawLine(pen, e.CellBounds.X - 2, e.CellBounds.Y + e.CellBounds.Height - 1, e.CellBounds.X + e.CellBounds.Width, e.CellBounds.Y + e.CellBounds.Height - 1);
	}

	using (Pen pen = new Pen(System.Drawing.Color.White, 1))
	{
		// Left
		e.Graphics.DrawLine(pen, e.CellBounds.X - 1, e.CellBounds.Y - 2, e.CellBounds.X - 1, e.CellBounds.Y + e.CellBounds.Height);
	}

	using (Pen pen = new Pen(System.Drawing.Color.White, 1))
	{
		// Right
		e.Graphics.DrawLine(pen, e.CellBounds.X + e.CellBounds.Width - 1, e.CellBounds.Y - 2, e.CellBounds.X + e.CellBounds.Width - 1, e.CellBounds.Y + e.CellBounds.Height);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex + 1 && e.RowIndex == dataGridView.CurrentCell.RowIndex)
{
	// Cell right to selected cell, same row
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 2))
	{
		e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Y, e.CellBounds.X, e.CellBounds.Y + +e.CellBounds.Height);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex - 1 && e.RowIndex == dataGridView.CurrentCell.RowIndex + 1)
{
	// Cell left to selected cell, row below
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (SolidBrush solidBrush = new SolidBrush(System.Drawing.Color.Red))
	{
		e.Graphics.FillRectangle(solidBrush, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y, 1, 1);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex && e.RowIndex == dataGridView.CurrentCell.RowIndex + 1)
{
	// Cell below selected cell, same column
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 2))
	{
		e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Y, e.CellBounds.X + e.CellBounds.Width, e.CellBounds.Y);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex + 1 && e.RowIndex == dataGridView.CurrentCell.RowIndex + 1)
{
	// Cell right to selected cell, row below
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (SolidBrush solidBrush = new SolidBrush(System.Drawing.Color.Red))
	{
		e.Graphics.FillRectangle(solidBrush, e.CellBounds.X, e.CellBounds.Y, 1, 1);
	}

	using (SolidBrush solidBrush = new SolidBrush(System.Drawing.Color.White))
	{
		e.Graphics.FillRectangle(solidBrush, e.CellBounds.X - 1, e.CellBounds.Y, 1, 1);
	}

	using (SolidBrush solidBrush = new SolidBrush(System.Drawing.Color.White))
	{
		e.Graphics.FillRectangle(solidBrush, e.CellBounds.X, e.CellBounds.Y - 1, 1, 1);
	}

	e.Handled = true;
}

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Hier der Effekt, wenn man die Zellen rechts daneben und darunter zeichnen lässt.
Das einzige was da noch fehlt, sind praktisch die Punkte der Dicke 1 in der rechten/linken ecke der Zelle, die links bzw. rechts unterhalb der Spalte vor der selektierten Spalte gezeichnet werden.

Also schon lustig das alles und ich glaube aktuell, dass mit meiner Vermutung richtig liege, dass das Grid von links oben nach rechts unten gezeichnet wird, und dass es daher bei den Zellen oberhalb bzw. links davon bis zur selben Row möglich war in diese Zellen rein zu malen und bei denen rechts bzw. darunter das Gemalte einfach später dann wieder übermalt wird, wenn diese Zellen gezeichnet werden.


if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex + 1 && e.RowIndex == dataGridView.CurrentCell.RowIndex)
{
	// Painting for Cell right next to the selected cell
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 2))
	{
		e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Y, e.CellBounds.X, e.CellBounds.Y + +e.CellBounds.Height);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex && e.RowIndex == dataGridView.CurrentCell.RowIndex)
{
	// Painting for selected Cell
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 3))
	{
		Rectangle rect = e.CellBounds;
		rect.X -= 1;
		rect.Y -= 1;
		e.Graphics.DrawRectangle(pen, rect);
	}

	e.Handled = true;
}
else if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex && e.RowIndex == dataGridView.CurrentCell.RowIndex + 1)
{
	// Painting for cell below selected Cell
	e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

	using (Pen pen = new Pen(System.Drawing.Color.Red, 2))
	{
		e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Y, e.CellBounds.X + e.CellBounds.Width, e.CellBounds.Y);
	}

	e.Handled = true;
}

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Das ist zwar richtig, aber das war erst mal Absicht und mich wundert es, dass es nicht nach unten raus gezeichnet wird. Es müsste ja unten über die Bottom-Linie raus laufen.

Die ersten Versuche mit DrawLine sind zwar besser, aber auch rechts raus wird die untere Linie nicht über den Rand in die Zelle rechts rein gezeichnet, obwohl ich da mal mit + 100 nen richtig langen Strich rein gezeichnet hab (hier mal für den Test mit roter Linie)


// Painting for selected Cells
e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

// Top-Line
using (Pen pen = new Pen(System.Drawing.Color.Black, 3))
{
	int offset_X = 3;
	int offset_Y = -2;
	e.Graphics.DrawLine(pen, e.CellBounds.X + offset_X, e.CellBounds.Y + offset_Y, e.CellBounds.X - offset_X + e.CellBounds.Width, e.CellBounds.Y + offset_Y);                    
}

// Bottom-Line
using (Pen pen = new Pen(System.Drawing.Color.Black, 3))
{
	int offset_X = 3;
	int offset_Y = -2;
	e.Graphics.DrawLine(pen, e.CellBounds.X + offset_X, e.CellBounds.Y + offset_Y + e.CellBounds.Height, e.CellBounds.X + offset_X + e.CellBounds.Width, e.CellBounds.Y + offset_Y + e.CellBounds.Height);
}

using (Pen pen = new Pen(System.Drawing.Color.Black, 2))
{
	int offset_X = 3;
	int offset_Y = -2;
	e.Graphics.DrawLine(pen, e.CellBounds.X + offset_X, e.CellBounds.Y + offset_Y + e.CellBounds.Height, e.CellBounds.X + offset_X + e.CellBounds.Width, e.CellBounds.Y + offset_Y + e.CellBounds.Height);

	e.Graphics.DrawLine(pen, e.CellBounds.X + 1, e.CellBounds.Y - 2, e.CellBounds.X + 1, e.CellBounds.Y + e.CellBounds.Height + 2);
	e.Graphics.DrawLine(pen, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y - 2, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y + e.CellBounds.Height + 2);
}

using (Pen pen = new Pen(System.Drawing.Color.Red, 1))
{
	int offset_X = -3;
	int offset_Y = -1;
	// Top, Bottom
	e.Graphics.DrawLine(pen, e.CellBounds.X + offset_X, e.CellBounds.Y + offset_Y, e.CellBounds.X + e.CellBounds.Width + 2, e.CellBounds.Y + offset_Y);
	e.Graphics.DrawLine(pen, e.CellBounds.X + offset_X, e.CellBounds.Y + offset_Y + e.CellBounds.Height, e.CellBounds.X + e.CellBounds.Width + 100, e.CellBounds.Y + offset_Y + e.CellBounds.Height);
}

using (Pen pen = new Pen(System.Drawing.Color.Red, 1))
{
	int offset_X = -3;
	int offset_Y = -2;
	// Left, Right
	e.Graphics.DrawLine(pen, e.CellBounds.X, e.CellBounds.Y + offset_Y, e.CellBounds.X, e.CellBounds.Y - offset_Y + e.CellBounds.Height);
	e.Graphics.DrawLine(pen, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y + offset_Y, e.CellBounds.X + e.CellBounds.Width - 2, e.CellBounds.Y + e.CellBounds.Height + 2);
}

e.Handled = true;

Gibt es dafür eine Erklärung, dass das abgeschnitten wird? Liegt das zufällig daran, weil die nachfolgenden Zellen rechts und dann nach unten hin, alle ja auch durch das CellPainting laufen und damit dann irgendwas über meine Styles überschrieben wird?

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Zitat von Jompikumpi
Aus deinem Bild entnehme ich, dass die Startkoordinate (X,Y) nicht passt.

Ein bisschen Verschieben und die Größe anpassen, dann sollte es gut sein.

Das dachte ich auch, aber leider nicht ganz. Denn ändere ich mal nur beim äußeren Rechteck die Y-Koordinate ab, dass es nach unten geht und belasse das Rechteck bei, schneidet der das am unteren Rand der Zelle einfach ab, als ob die Zelle selbst der Begrenzer ist und es da dann gekürzt wird:


// Painting for selected Cells
e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

using (Pen pen = new Pen(System.Drawing.Color.Black, 4))
{
	Rectangle rect = e.CellBounds;
	rect.Y += 2;
	rect.Width -= 2;
	rect.Height -= 2;
	e.Graphics.DrawRectangle(pen, rect);
}

//using (Pen pen = new Pen(System.Drawing.Color.White, 2))
//{
//    Rectangle rect = e.CellBounds;
//    //rect.X += 2;
//    //rect.Y += 2;
//    rect.Width -= 1;
//    rect.Height -= 1;
//    e.Graphics.DrawRectangle(pen, rect);
//}

e.Handled = true;

Erwartet hätte ich, dass sich das ganze Rechteck über die Zelle drüber legt.

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Die Koordinaten und die Dicke passen prinzipiell glaub. Nur, das Problem ist, dass im Screenshot in der Mitte ein weißer Strich/Bereich frei liegt und das bekomme ich irgendwie nicht hin.

Welchen Brush müsste ich denn dazu verwenden? Der Effekt beim Hatch-Brush war nur, dass meine ganze Linie innen gestrichelt war, aber nicht so in dem Effekt "Aussen-/Innen-Linie"


// Painting for selected Cells
e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

using (Pen pen = new Pen(System.Drawing.Color.Black, 4))
{
	Rectangle rect = e.CellBounds;
	rect.Width -= 2;
	rect.Height -= 2;
	e.Graphics.DrawRectangle(pen, rect);
}

using (Pen pen = new Pen(System.Drawing.Color.White, 2))
{
	Rectangle rect = e.CellBounds;
	rect.Width -= 2;
	rect.Height -= 2;
	e.Graphics.DrawRectangle(pen, rect);
}

e.Handled = true;

Also irgendwie rechts drüben und unten will es nicht in die anderen Zellen rüber ragen.

Ein rect.Width += 2; rect.Height += 2; scheint es aber auch nicht wirklich zu bringen.

Thema: Wie kann ich im im DataGridView eine Doppel-Linien-Umrandung zeichnen?
Am im Forum: GUI: Windows-Forms

Hallo,

ich würde gerne im DataGridView eine Doppel-Linien-Umrandung, wie im Screenshot in der aktuell selektierten Zelle zeichnen.

An sich dachte ich, dass ich das über einen Hatch-Brush realisieren kann, diesen dann dem Pen übergeben, aber das hat leider nicht so hingehauen, daher erst einmal wieder raus genommen.

Was mach ich denn beim Zeichnen falsch, dass das nur ein dicker Rahmen, statt Doppel-Linie ist?


private void dataGridView_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
	if (e.ColumnIndex == dataGridView.CurrentCell.ColumnIndex && e.RowIndex == dataGridView.CurrentCell.RowIndex)
	{                
		// Painting for selected Cells
		e.Paint(e.CellBounds, DataGridViewPaintParts.All & ~DataGridViewPaintParts.Border);

		using (Pen pen = new Pen(System.Drawing.Color.Red, 4))
		{
			Rectangle rect = e.CellBounds;
			rect.Width -= 2;
			rect.Height -= 2;
			e.Graphics.DrawRectangle(pen, rect);
		}

		e.Handled = true;
	}
}

Thema: Fehler: 'Microsoft.ACE.OLEDB.12.0'-Provider ist nicht auf dem lokalen Computer registriert"
Am im Forum: Office-Technologien

Zitat von Abt
Der Fehler war schon hunderte Male im Forum. Daher bitte das nächste Mal zuerst die Forensuche nutzen. Danke ;-)

Hallo Abt,

die hatte ich schon gelesen und gesehen, dass es am x86/x64 vermutlich auch hier liegt. Vielleicht einfach die Frage, ob es eine elegante Lösung gibt.

Ich habe es nun so gelöst, dass ich auch das *.xlsx über das OpenDocument SDK einlese


using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Data;
...

private DataTable ReadExcelSheetToDataTable(string pathExcelFile)
{
	DataTable dt = new DataTable();
	
	dt.Columns.Add("Column 1");
	dt.Columns.Add("Column 2");
	dt.Columns.Add("Column 3");
	dt.Columns.Add("Column 4");
	dt.Rows.Add();  // First of all add an Empty Row

	using (SpreadsheetDocument doc = SpreadsheetDocument.Open(pathExcelFile, false))
	{
		// Read the first Sheet
		Sheet sheet = doc.WorkbookPart.Workbook.Sheets.GetFirstChild<Sheet>();
		Worksheet worksheet = (doc.WorkbookPart.GetPartById(sheet.Id.Value) as WorksheetPart).Worksheet;
		IEnumerable<Row> rows = worksheet.GetFirstChild<SheetData>().Descendants<Row>();
		
		int counter = 0;
		foreach (Row row in rows)
		{
			counter = counter + 1;

			// Skip first 3 rows as header
			if (counter ≥ 3)
			{                        
				dt.Rows.Add();
				int i = 0;
				foreach (Cell cell in row.Descendants<Cell>())
				{
					dt.Rows[dt.Rows.Count - 1][i] = GetCellValue(doc, cell);
					i++;
				}
			}
		}

	}

	return dt;
}

private string GetCellValue(SpreadsheetDocument doc, Cell cell)
{
	string cellValueText = cell.CellValue.InnerText;
	Double value = Double.Parse(cellValueText, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture) / 100;

	if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
	{
		return doc.WorkbookPart.SharedStringTablePart.SharedStringTable.ChildElements.GetItem(int.Parse(cellValueText)).InnerText;
	}
	return value.ToString();
}

Thema: Fehler: 'Microsoft.ACE.OLEDB.12.0'-Provider ist nicht auf dem lokalen Computer registriert"
Am im Forum: Office-Technologien

Hallo zusammen,

leider hakelt es bei mir und meiner Anwendung wohl am laufenden Band.

Ich möchte in meiner Anwendung die Google OR Tools verwenden, wofür ich wohl meine Anwendung auf x64 für den Compile umstellen muss.

Mit der Anwendung wollte ich aber auch Daten aus einer Excel-Tabelle per OleDbConnection auslesen. Das klappt aber mit einem Compile auf x64 wohl nicht mehr und beim Versuch Daten aus der Excel auszulesen kommt die Fehlermeldung:

Der 'Microsoft.ACE.OLEDB.12.0'-Provider ist nicht auf dem lokalen Computer registriert" beim Import einer Excel-Datei.

Ich habe Excel 2007 und auch eine Excel-Datei, die damit erstellt ist. Unter x86 bzw. 32-Bit klappt zwar das Einlesen der Excel-Tabelle ohne Probleme, aber die Google OR Tools wollen damit dann nicht mehr.

Wie bekomme ich denn diese Beiden Welten am besten verheiratet?

Thema: Wie kann ich Werte im DataGridView abhängig von einer ComboBox verändern?
Am im Forum: GUI: Windows-Forms

Hhm, ich hoffe, ich hab das dann nicht zu kompliziert gemacht. Wirkt irgendwie nicht so prickelnd bis jetzt:


private void dataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
	DataGridView dataGridView = sender as DataGridView;
	if (dataGridView != null && e.ColumnIndex ≥ 0 && e.RowIndex ≥ 0)
	{
		var comboBoxCell = dataGridView[e.ColumnIndex, e.RowIndex] as DataGridViewComboBoxCell;
		if (comboBoxCell != null)
		{
			var dt = comboBoxCell.DataSource as DataTable;
			if (dt != null)
			{
				var dtSelectedRows = dt.Select().Where(rowValue => rowValue.ItemArray[0].ToString() == comboBoxCell.Value.ToString()).CopyToDataTable();
				if (dtSelectedRows.Rows != null && dtSelectedRows.Rows.Count == 1)
				{
					dataGridView[1, e.RowIndex].Value = dtSelectedRows.Rows[0].ItemArray[1];
					dataGridView[2, e.RowIndex].Value = dtSelectedRows.Rows[0].ItemArray[2];
					dataGridView[3, e.RowIndex].Value = dtSelectedRows.Rows[0].ItemArray[3];
				}
			}
		}
	}
}

Was mich auch gerade noch etwas verwundert, dieses Ereignis kommt erst, nachdem ich von meiner Zelle mit der DropDownBox den Fokus auf eine andere Zelle setze und nicht schon, wenn ein Wert aus der DropDownBox selektiert wurde und diese damit dann zuklappt.

Thema: Wie kann ich Werte im DataGridView abhängig von einer ComboBox verändern?
Am im Forum: GUI: Windows-Forms

Hallo zusammen,

ich habe ein DataGridView mit 10 Spalten. Dann hab ich eine DataTable dt mit Werten bestehend aus 4 Spalten. Die Werte der ersten Spalte (Primary Key) werden in einer DataGridViewComboBoxCell im DataGridView zur Auswahl angeboten und sind wie unten per DataSource, DisplayMember und ValueMember angebunden.

Nachdem ein Wert aus DataGridViewComboBoxCell ausgewählt wurde, sollen in 3 anderen Spalten in meinem DataGridView die entsprechend damit verknüpften Werte aus der DataTable angezeigt werden.


DataGridViewComboBoxCell artikel = new DataGridViewComboBoxCell();
artikel.DataSource = dt;
artikel.DisplayMember = dt.Columns[0].ColumnName; // "Artikel";
artikel.ValueMember = dt.Columns[0].ColumnName; // "Artikel";

Wie muss ich denn die 3 anderen Spalten anbinden, damit der Filter-Mechanismus über die DataGridViewComboBoxCell richtig funktioniert?

Thema: Wie kann ich einen Ausschnitt einer Excel Datei in einem Control anzeigen?
Am im Forum: GUI: Windows-Forms

Hallo Th69,

es handelt sich hier um eine Datei aus Exel 2007. Diese liegt schon im *.xlsx Format vor. Das eigentliche "Problem" ist aktuell fast nur die schöne Anzeige.
Ein riesen Freund war ich nie vom DataGridControl und es zeigt sich auch hier wieder, dass es auf diesem Wege sehr viel manuelles Nacharbeiten mit vielen Kleinigkeiten ist. Wenn man in Excel auf "Entf" drückt ist der Wert aus einer Zelle gelöscht. Solche Dinge muss man im DataGridControl alle manuell und händisch nachbauen. Genauso mit den Linien und Umrandungen überall. Auch die DataGridViewComboBoxCell ist so ein Ding, wenn die integriert ist. Da ist der Mechanismus so, dass man 2x klicken muss, damit das Menü aufspringt. Ebenso ist das Farbschema nicht natürlich in das weiß der anderen Zellen integriert, sondern muss auch manuell nachjustiert werden.

Wie gesagt, sehr viel Handarbeit, um nur das Grund-Look-and-Feel von Excel zu haben.
Die Daten hole ich mir in die ComboBox per OLE DB mittels Microsoft.ACE.OLEDB.12.0 und weise diese DataTable dann per DataSource der DataGridViewComboBoxCell mit einem DisplayMember und ValueMember zu.

Thema: Wie kann ich einen Ausschnitt einer Excel Datei in einem Control anzeigen?
Am im Forum: GUI: Windows-Forms

Hallo zusammen,

ich habe schon ein paar Beiträge durchgelesen, bei denen beschrieben wurde, wie man eine Excel-Datei in einem Control innerhalb einer WinForms-Anwendung anzeigen können soll (z.B. mit dem WebBrowser oder anderen ActiveX).

Leider haben diese Varianten bei mir nicht so richtig funktioniert und entweder wurde die Datei immer extern geöffnet oder aber, die Microsoft Controls zur Anbindung standen gar nicht zur Auswahl, obwohl Excel 2007 installiert ist.

Mir ging es eigentlich darum, dass ich etwas vom DataGridView weg kommen kann. Beim DataGridView muss man wirklich immer sehr viel händisch zusammenprogrammieren, um eine Grundfunktionalität und Optik zu bekommen, die Excel schon von Haus aus gut macht.

Ich möchte im Grund einfach einen Ausschnitt aus meiner Excel-Tabelle anzeigen und darin Werte auslesen und setzen (ohne die Excel-Datei wieder zu speichern).

In der Excel-Tabelle gibt es aktuell ein DropDown-Menü bei dem Werte aus einem anderen Datenblatt eingeblendet werden. Diese Werte für die DropDown-Box wollte ich in der WinForms-Anwendung auch aus einer ganz anderen Excel-Tabelle auslesen und in dem angezeigten Ausschnitt in den DropDown-Boxen zur Verfügung haben.

Die Frage ist, worüber würde man das heutzutage (VS 2019) realisieren und gibt es noch Controls, mit denen man wirklich einen Teil der Excel-Tabelle einblenden kann, oder ist diese Funktionalität komplett von der Bildfläche verschwunden und ich muss es wirklich alles händisch über das DataGridView machen?

Thema: Wie löse ich das Rucksack Problem mit mehreren Rucksäcken?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Ich hab es jetzt mit meiner eigenen Lösung doch aufgegeben.

Der Google OR Tools Solver kann mit einem "SCIP"-Solver in Windeseile lösen. Die Lösung kommt sogar deutlich schneller, als mit dem Excel-Solver. Evtl. hab ich bei Excel durch Setzen eines Hakens noch etwas suboptimal konfiguriert, wenn das hier so schnell geht.

Die Konfiguration ist als Neuling allerdings etwas gewöhnungsbedürftig, aber wenn man die mal raus hat und wie man Gleichungen bzw. Constraints für den Solver bastelt, dann macht er wirklich gute Arbeit.

Frage ist nur noch, ob diese OR Tools überhaupt auf meinem iPhone und mit Xamarin zum Laufen zu bringen sind... Hürden über Hürden. Ansonsten muss ich doch wieder auf meine manuell programmierte Variante gehen..

Thema: Wie löse ich das Rucksack Problem mit mehreren Rucksäcken?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Beim Überfliegen der Artikel sehe ich das ehrlich gesagt auch so.

Im Grunde wäre es mir wichtig, dass ich eine Lösung auf eine kleine Handy-App bringe. Für mich gedacht, von daher kann ich mit leicht höheren Laufzeiten leben, aber natürlich auch nicht übermäßig, ansonsten wird es auch eine zähe angelegenheit.

Ist Dir zufällig ein guter frei verwendbarer Solver bekannt, den man für solche Berechnungen hernehmen könnte?

Thema: Wie löse ich das Rucksack Problem mit mehreren Rucksäcken?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von ErfinderDesRades
Tja, ich würds gerne probieren, deinen Code zu verbessern - etwa durch Backtracking.
Allein ich sehe ihn nicht...?

Hier mal der bisherige Code (natürlich noch nicht optimal aufbereitet). Ist erst mal über einen Button einer Form zum Anstarten und am Ende wird noch kurz die sortierte Liste ausgegeben:


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;

namespace SolverAlgorithm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Creates a value matrix in ascending order with the calculated value for each Quantity
        /// from its minimum to its maximum quantity
        /// </summary>
        /// <param name="weights">Proportions on each Rucksack</param>
        /// <param name="minimum">Minimum Quantity for this Element</param>
        /// <param name="maximum">Maximum Quantity for this Element</param>
        /// <returns>An array containing the calculated values (weight * quantity) and the quantity itself (e.g. [{0.22, 0.01, 0.34, 1}, {0.44, 0.02, 0.68, 2}, ..])</returns>
        private double[,] CalculateValueMatrixAscending(double[] weights, int minimum, int maximum)
        {
            int numberOfValues = Math.Abs(maximum - minimum);
            double[,] valueMatrix = new double[numberOfValues + 1, 4]; // Rucksack 1, Rucksack 2, Rucksack 3, Quantity

            for (int quantity = minimum, index = 0; quantity ≤ maximum; quantity++, index++)
            {
                for (int j = 0; j < 3; j++)
                {
                    valueMatrix[index, j] = quantity * weights[j];
                }

                valueMatrix[index, 3] = quantity;       // Quantity for which the value was calculated
            }

            return valueMatrix;
        }

        /// <summary>
        /// Creates a value matrix in descending order with the calculated value for each Quantity
        /// from its minimum to its maximum quantity
        /// </summary>
        /// <param name="weights">Proportions on each Rucksack</param>
        /// <param name="minimum">Minimum Quantity for this Element</param>
        /// <param name="maximum">Maximum Quantity for this Element</param>
        /// <returns>An array containing the calculated values (weight * quantity) and the quantity itself (e.g. [{0.22, 0.01, 0.34, 1}, {0.44, 0.02, 0.68, 2}, ..])</returns>
        private double[,] CalculateValueMatrixDescending(double[] weights, int minimum, int maximum)
        {
            int numberOfValues = Math.Abs(maximum - minimum);
            double[,] valueMatrix = new double[numberOfValues + 1, 4]; // Rucksack 1, Rucksack 2, Rucksack 3, Quantity

            for (int quantity = maximum, index = 0; quantity ≥ minimum; quantity--, index++)
            {
                for (int j = 0; j < 3; j++)
                {
                    valueMatrix[index, j] = quantity * weights[j];
                }

                valueMatrix[index, 3] = quantity;       // Quantity for which the value was calculated
            }

            return valueMatrix;
        }

        private void CalculateOptimalQuantities(int recursionDepth, double[][,] elements, double sumElement1_CurrentRecursionDepth, double sumElement2_CurrentRecursionDepth, double sumElement3_CurrentRecursionDepth, RucksackRestrictions rucksackRestrictions, LimitedSortedList sortedListOptimaleQuantities, OptimalQuantities optimalQuantitiesOverRecursion)
        {
            // Stop recursion at the lowest level (last Matrix)
            if (recursionDepth > elements.GetUpperBound(0))
                return;

            double[,] elementCurrentRecursionDepth = elements[recursionDepth];
            int numRows = elementCurrentRecursionDepth.GetUpperBound(0);  // extra variable to have the opportunity to manipulate it

            for (int row = 0; row ≤ numRows; row++)
            {
                // Save the quantity over the entire recursion hierarchy to
                // have the "Optimal Quantities" to the corresponding error value later on
                optimalQuantitiesOverRecursion.OptimalElementQuantities[recursionDepth] = (int)elementCurrentRecursionDepth[row, 3];      // Quantity of current line in the Matrix

                // Create sum for Columns (each column is a Rucksack) for each Element Quantity
                // of the current Matrix Combination
                var sumRucksack1 = sumElement1_CurrentRecursionDepth + elementCurrentRecursionDepth[row, 0];        // Rucksack 1
                var sumRucksack2 = sumElement2_CurrentRecursionDepth + elementCurrentRecursionDepth[row, 1];        // Rucksack 2
                var sumRucksack3 = sumElement3_CurrentRecursionDepth + elementCurrentRecursionDepth[row, 2];        // Rucksack 3

                // Determine if allowed maximum capacity of a Rucksack is exceeded and end recursion
                // Don't proceed next row/quantity as it would be even higher (when ascending order is used)
                if (sumRucksack1 > rucksackRestrictions.CapacityRucksack1 || sumRucksack2 > rucksackRestrictions.CapacityRucksack2 || sumRucksack3 > rucksackRestrictions.CapacityRucksack3)
                    return;

                CalculateOptimalQuantities(recursionDepth + 1, elements, sumRucksack1, sumRucksack2, sumRucksack3, rucksackRestrictions, sortedListOptimaleQuantities, optimalQuantitiesOverRecursion);

                // We are at the bottom of the recursion and on our last element,
                // so we can create an ErrorValue as a Distance to the allowed Maximum capacity
                // over all Rucksacks
                if (recursionDepth == elements.GetUpperBound(0))
                {
                    // The lower the better and the better the rucksacks are filled
                    var errorValue = (rucksackRestrictions.CapacityRucksack1 - sumRucksack1) + (rucksackRestrictions.CapacityRucksack2 - sumRucksack2) + (rucksackRestrictions.CapacityRucksack3 - sumRucksack3);

                    // This would be an Over-Optimization of the ErrorValue and
                    // basically should occur. So we end the recursion in this case
                    if (errorValue < 0)
                        return;

                    //// For a descending case
                    //// We iterate from the highest Quantity to the lowest Quantity and
                    //// save a value for the first time. After that save the following
                    //// x values to just show these values to the user
                    //int newEndIndex = row + sortedListOptimalQuantities.Capacity;
                    //numRows = newEndIndex ≤ row ? newEndIndex : row;

                    // Here we save the optimal Quantities we've collected
                    // over the levels of the recursion and with the calculated
                    // error value on the bottom of the recursion.
                    // Do a shallow copy to have a unique reference of the OptimalQuantity
                    // to save.                    
                    OptimalQuantities optimaleMengeToSave = new OptimalQuantities(optimalQuantitiesOverRecursion.OptimalElementQuantities.Length);
                    for (int i = 0; i < optimalQuantitiesOverRecursion.OptimalElementQuantities.Length; i++)
                    {
                        optimaleMengeToSave.OptimalElementQuantities[i] = optimalQuantitiesOverRecursion.OptimalElementQuantities[i];
                    }
                    optimaleMengeToSave.ErrorValue = errorValue;
                    optimaleMengeToSave.SumRucksack1 = sumRucksack1;
                    optimaleMengeToSave.SumRucksack2 = sumRucksack2;
                    optimaleMengeToSave.SumRucksack3 = sumRucksack3;

                    sortedListOptimaleQuantities.Add(optimaleMengeToSave);
                }
            }
        }

        private void cmdStartCalculation_Click(object sender, EventArgs e)
        {
#if DEBUG
            Stopwatch timer = new Stopwatch();
            timer.Start();
#endif            
            double[] weights_Element1 = new double[3] { 0.21, 0.53, 0.14 };
            double[] weights_Element2 = new double[3] { 0.841, 0.011, 0.011 };
            double[] weights_Element3 = new double[3] { 0, 0, 0.93 };
            double[] weights_Element4 = new double[3] { 0.14, 0.067, 0.56 };
            double[] weights_Element5 = new double[3] { 0.122, 0.003, 0.04 };
            double[] weights_Element6 = new double[3] { 0.003, 0.006, 0.114 };
            double[] weights_Element7 = new double[3] { 0.004, 0.003, 0.104 };

            int[] boundaries_Element1 = new int[2] { 0, 30 };       // Original boundaries: 0 - 30
            int[] boundaries_Element2 = new int[2] { 1, 30 };       // Original boundaries: 1 - 30
            int[] boundaries_Element3 = new int[2] { 5, 5 };        // Original boundaries: 5 - 5
            int[] boundaries_Element4 = new int[2] { 20, 150 };     // Original boundaries: 20 - 150
            int[] boundaries_Element5 = new int[2] { 100, 500 };    // Original boundaries: 100 - 500
            int[] boundaries_Element6 = new int[2] { 50, 100 };     // Original boundaries: 50 - 100
            int[] boundaries_Element7 = new int[2] { 50, 150 };     // Original boundaries: 50 - 150

            var valueMatrix_Element1 = CalculateValueMatrixAscending(weights_Element1, boundaries_Element1[0], boundaries_Element1[1]);
            var valueMatrix_Element2 = CalculateValueMatrixAscending(weights_Element2, boundaries_Element2[0], boundaries_Element2[1]);
            var valueMatrix_Element3 = CalculateValueMatrixAscending(weights_Element3, boundaries_Element3[0], boundaries_Element3[1]);
            var valueMatrix_Element4 = CalculateValueMatrixAscending(weights_Element4, boundaries_Element4[0], boundaries_Element4[1]);
            var valueMatrix_Element5 = CalculateValueMatrixAscending(weights_Element5, boundaries_Element5[0], boundaries_Element5[1]);
            var valueMatrix_Element6 = CalculateValueMatrixAscending(weights_Element6, boundaries_Element6[0], boundaries_Element6[1]);
            var valueMatrix_Element7 = CalculateValueMatrixAscending(weights_Element7, boundaries_Element7[0], boundaries_Element7[1]);

            IList<double[,]> elements = new List<double[,]>();
            elements.Add(valueMatrix_Element1);
            elements.Add(valueMatrix_Element2);
            elements.Add(valueMatrix_Element3);
            elements.Add(valueMatrix_Element4);
            elements.Add(valueMatrix_Element5);
            elements.Add(valueMatrix_Element6);
            //elements.Add(valueMatrix_Eleent7);


            // Maybe implement a check here, if there could be a calculation at all (e.g. if in ascending order the minimum is already an overflow)
            // ...


            LimitedSortedList limitedSortedListOptimalQuantities = new LimitedSortedList(5);
            RucksackRestrictions rucksackRestrictions = new RucksackRestrictions(40, 20, 45);
            var elementArray = elements.ToArray();

            // Create all Matrix-Combinations line by line and do the calculations
            var firstElement = elements[0];
            for (int i = 0; i ≤ firstElement.GetUpperBound(0); i++)
            {
                OptimalQuantities optimalQuantities = new OptimalQuantities(elements.Count);
                optimalQuantities.OptimalElementQuantities[0] = (int)firstElement[i, 3];

                double sumElement1_CurrentRecursionDepth = firstElement[i, 0];
                double sumElement2_CurrentRecursionDepth = firstElement[i, 1];
                double sumElement3_CurrentRecursionDepth = firstElement[i, 2];

                CalculateOptimalQuantities(1, elementArray, sumElement1_CurrentRecursionDepth, sumElement2_CurrentRecursionDepth, sumElement3_CurrentRecursionDepth, rucksackRestrictions, limitedSortedListOptimalQuantities, optimalQuantities);
            }

#if DEBUG
            timer.Stop();
            Debug.WriteLine($"Time Taken for Calculation: {timer.Elapsed.Minutes}m {timer.Elapsed.Seconds}s {timer.Elapsed.Milliseconds}ms");
#endif      

            // Just a simple output in Debug Console
            foreach (var optimalQuantity in limitedSortedListOptimalQuantities)
            {
                Debug.WriteLine($"{optimalQuantity}");
            }
        }
    }
}


using System.Collections;
using System.Collections.Generic;

namespace SolverAlgorithm
{
    class LimitedSortedList : IList<OptimalQuantities>
    {
        private IList<OptimalQuantities> limitedList = new List<OptimalQuantities>();

        public LimitedSortedList(int capacity)
        {
            this.Capacity = capacity;
        }



        public int Capacity { get; private set; }

        public OptimalQuantities this[int index] { get => limitedList[index]; set => throw new System.NotImplementedException(); }

        public int Count => limitedList.Count;

        public bool IsReadOnly => limitedList.IsReadOnly;

        public void Add(OptimalQuantities item)
        {
            int positionToInsert = limitedList.Count;
            for (int i = 0; i < limitedList.Count; i++)
            {
                if (item.CompareTo(limitedList[i]) < 0)
                {
                    positionToInsert = i;
                    break;
                }
            }

            limitedList.Insert(positionToInsert, item);

            if (limitedList.Count > this.Capacity)
                limitedList.RemoveAt(limitedList.Count - 1);
        }

        public void Clear()
        {
            limitedList.Clear();
        }

        public bool Contains(OptimalQuantities item)
        {
            return limitedList.Contains(item);
        }

        public void CopyTo(OptimalQuantities[] array, int arrayIndex)
        {
            limitedList.CopyTo(array, arrayIndex);
        }

        public IEnumerator<OptimalQuantities> GetEnumerator()
        {
            return limitedList.GetEnumerator();
        }

        public int IndexOf(OptimalQuantities item)
        {
            return limitedList.IndexOf(item);
        }

        public void Insert(int index, OptimalQuantities item)
        {
            throw new System.NotImplementedException();
        }

        public bool Remove(OptimalQuantities item)
        {
            return limitedList.Remove(item);
        }

        public void RemoveAt(int index)
        {
            limitedList.RemoveAt(index);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return limitedList.GetEnumerator();
        }
    }
}


using System;

namespace SolverAlgorithm
{
    class OptimalQuantities : IEquatable<OptimalQuantities>, IComparable<OptimalQuantities>
    {
        readonly int[] optimalElementQuantities;

        public OptimalQuantities(int numProducts)
        {
            this.optimalElementQuantities = new int[numProducts];
        }
        
        /// <summary>
        /// Optimal Quantities from first Element to last Element that has to be used
        /// </summary>
        public int[] OptimalElementQuantities { get => this.optimalElementQuantities; }
        public double ErrorValue { get; set; }

        public double SumRucksack1 { get; set; }
        public double SumRucksack2 { get; set; }
        public double SumRucksack3 { get; set; }


        public override string ToString()
        {
            return $"ErrorValue: {ErrorValue}; Sum Rucksack 1: {SumRucksack1}, Sum Rucksack 2: {SumRucksack2}, Sum Rucksack 3: {SumRucksack3}";
        }

        public int CompareTo(OptimalQuantities other)
        {
            // A null value means that this object is greater.
            if (other == null)
                return 1;
            else
                return this.ErrorValue.CompareTo(other.ErrorValue);
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            OptimalQuantities objAsOptimaleMenge = obj as OptimalQuantities;
            if (objAsOptimaleMenge == null) return false;
            else return Equals(objAsOptimaleMenge);
        }        

        public bool Equals(OptimalQuantities other)
        {
            if (other == null)
                return false;

            return  (other.ErrorValue == this.ErrorValue) && 
                    (other.SumRucksack1 == this.SumRucksack1) && 
                    (other.SumRucksack2 == this.SumRucksack2) && 
                    (other.SumRucksack3 == this.SumRucksack3);
        }
    }
}


namespace SolverAlgorithm
{
    class RucksackRestrictions
    {
        public int CapacityRucksack1 { get; }
        public int CapacityRucksack2 { get; }
        public int CapacityRucksack3 { get; }

        public RucksackRestrictions(int capacityRucksack1, int capacityRucksack2, int capacityRucksack3)
        {
            this.CapacityRucksack1 = capacityRucksack1;
            this.CapacityRucksack2 = capacityRucksack2;
            this.CapacityRucksack3 = capacityRucksack3;
        }
    }
}



Den Versuch/Ansatz mit Partitionierung der Matrizen, ähnlich binärer Suche, hab ich mal raus gelassen.

Zumindest über die Schleife für die erste Matrix wollte ich gerne eine Parallel.For(...) probieren. Kannst aber ja vergessen mit der LimitedSortedList.

Ohne diese Spaßbremse "LimitedSortedList" läuft sehr schnell auch der Speicher voll. Da kannst dem Untergang zusehen.

Wie gesagt, ich wollte das gerne auch noch auf mein Handy bringen, so dass Performance und Speicher schon auch echte Beschränkungen sind.

Thema: Wie löse ich das Rucksack Problem mit mehreren Rucksäcken?
Am im Forum: Basistechnologien und allgemeine .NET-Klassen

Zitat von ErfinderDesRades
Rekursiv alle Kombinationen durchgehen und dabei die (bisher) beste merken.
Bei der rekursiven Vertiefung immer abbrechen, wenn der aktuelle Wert bereits schlechter ist als der bislang gefundene beste Wert ?

Also bei meinen Input-Daten mit 6 Elementen läuft die Berechnung mittels Rekursion in etwa 40 - 45s durch.

 
            double[] weightsElement1 = new double[3] { 0.21, 0.53, 0.14 };
            double[] weightsElement2 = new double[3] { 0.841, 0.011, 0.011 };
            double[] weightsElement3 = new double[3] { 0, 0, 0.93 };
            double[] weightsElement4 = new double[3] { 0.14, 0.067, 0.56 };
            double[] weightsElement5 = new double[3] { 0.122, 0.003, 0.04 };
            double[] weightsElement6 = new double[3] { 0.003, 0.006, 0.114 };
            //double[] weightsElement7 = new double[3] { 0.004, 0.003, 0.104 };

            int[] boundariesElement1 = new int[2] { 0, 30 };
            int[] boundariesElement2 = new int[2] { 1, 30 };
            int[] boundariesElement3 = new int[2] { 5, 5 };
            int[] boundariesElement4 = new int[2] { 20, 150 };
            int[] boundariesElement5 = new int[2] { 100, 500 };
            int[] boundariesElement6 = new int[2] { 50, 100 };
            //int[] boundariesElement7 = new int[2] { 50, 150 };

           ...

Ich möchte evtl. später daraus noch ne kleine App mittels Xamarin machen, so dass man das irgendwie auch auf nem iPhone zum Laufen bekommt. Da ich plane, vielleicht bis zu 8 Elemente zur Konfiguration zu erlauben, sollte ich schon nochmal was in Sachen Performance verbessern.
Excel bekommt das bei größeren Mengen mit seinem Solver effizienter hin.

Wenn ich wüsste, wie ich das Speichern in meiner LimitedSortedList parallel richtig verwalten kann, könnte ich eine Variante mit Parallel.For(...) oder ähnlichem noch probieren. Bei mir wird das ja aktuell auf einem Core nur gemacht, denke ich mal und der Flaschenhals ist tatsächlich dann das Einfügen in die LimitedSortedList. Lasse ich die Sortierung und Begrenzung weg, dann bekomme ich schnell eben den Speicher voll und es raucht auch ab. Für ein Handy ist das ganze damit eh so gestorben.

Ich weiß nicht, wasn man am Handy an Wartezeit für so eine Berechnung akzeptieren kann, aber das muss doch auch irgendwie noch besser gehen. Der Excel-Solver bekommt das doch auch besser hin.