Hallo zusammen,
ich sitze gerade an einem Problem im Zusammenhang mit C# und TrackBars, bei dem die Lösung wohl näher liegt, als es mir derzeit erscheint.
Ich habe eine Menge an Trackbars auf einer Form. Jeder dieser TrackBars hat einen Wertebereich von 0 - 100 und die Gesamtsumme aller Trackbarwerte soll auch 100 ergeben (sprich: es handelt sich um eine prozentuale Gewichtung von Eigenschaften).
Wenn nun der Benutzer nun eingreift und einen Wert eines TrackBars ändert, möchte ich die anderen automatisch so anpassen, dass die Gesamtsumme aller TrackBars wiederum 100 ergibt.
Jetzt meine Frage, wie ich das am besten löse.
Und schon einmal vielen Dank für die Antworten
Hallo Binärathlet,
sollen die einzelnen TrackBars gewichtet sein oder soll sich die Neuverteilung gleichmäßig ergeben?
MfG
wax
Hallo Wax,
die nicht manuell manipulierten TrackBars sollen ihren Wert so anpassen, dass der Gesamtwert aller TrackBars wiederum 100 ergibt. Ich möchte keine gänzliche Neuberechnung im Sinne einer gleichmäßigen Neuverteilung des Restwertes, sondern eine Anpassung je nach dem aktuellen Wert.
einfaches Beispiel:
Ich habe drei Trackbars, der erste hat den Wert 50, der zweite 20 und der dritte 30.
Jetzt erhöht der Benutzer den Wert des ersten um 10 auf 60, demnach müssen die anderen beiden jeweils um 5 sinken, so dass der zweite den Wert 15 erhält, der dritte 25.
Problem dabei ist, dass die Anzahl von TrackBars erst zur Laufzeit festgelegt wird und ich somit einen möglichst allgemein gehaltenen Algorithmus brauche.
Gruß,
Binärathlet
Hallo,
im einfachsten Fall kannst du deine TrackBars einer System.Collections.Generic.List<TrackBar> hinzufügen. Außerdem würde ich für alle TrackBars den gleichen EventHandler verwenden.
Flexibler bist du, wenn du ein eigenes Control von TrackBar ableitest und deine eigene Funktionalität hinzufügst(z.B. eine Eigenschaft "Weight" für die Gewichtung). Hierfür kann man dann auch eine eigene Auflistungsklasse schreiben.
Grüße, Koller.
Hallo!
Wenn ich das aus deinem Beispiel richtig verstanden habe kannst du doch folgendes machen:
Speichere deine Trackbars in einem Array.
ggf.:
List<Trackbar>
Wenn sich jetzt bei einer der Bars der Wert ändert stellst du den Betrag der Änderung fest. Diesen teilst du einfach durch die Anzahl aller vorhandenen Trackbars und weist diesen die Wertänderung zu. Schon hast du eine relativ einfach Anpassung.
Natürlich musst du beim Anpassen darauf achten, dass du beim Durchlaufen des Arrays überprüfst ob es sich um die "manuell geänderte" Bar handelt. Diese musst du dann entsprechend auslassen.
Gruß dev
Das Problem sind nur die möglichen Rundungsfehler, d.h. wenn es drei andere TrackBars gibt und die vierte um 1 erhöht (oder erniedrigt) wird, so müssten die anderen drei ja nur um 1/3 erniedrigt (bzw. erhöht) werden. Oder aber nur einer der drei wird um 1 verändert (aber welcher?).
Also entweder intern mit Fließkommazahlen (bzw. hier besser decimal) arbeiten (anstatt direkt mit TrackBar.Value) oder z.B. nur eine Veränderung um X zulassen (wobei X dann die Anzahl der restlichen TrackBars wäre).
Hallo again,
erst einmal danke für die Anregungen bisher!
Die TrackBars verwalte ich intern bereits in einem Array, wie DeViL666.on vorgeschlagen hat, schon allein, um sie einfach ansprechbar zu machen.
Mit der Restwertfeststellung und der dahingehenden Anpassung habe ich schon experimentiert. Die Differenz durch den Rundungsfehler habe ich dann randomisiert einem der Balken zugewiesen.
Allerdings ist die dabei die Frage, bei welchen Events fange ich den Anfangswert des TrackBars und wann den Endwert ab. Fange ich den Endwert mit dem Event Scroll ab, so ergibt die Differenz ja selten mehr als 1, sprich, ich kann nur mit der randomisierten Zuweisung arbeiten, was zu nicht wirklich guten Ergebnissen führt.
Arbeite ich mit ValueChanged, so geschieht eine Anpassung der anderen TrackBars nicht wirklich in 'Echtzeit'.
mach dir statt einen Array ein Dictionary bei dem du zu jeder trackbar den aktuellen wert speicherst.
wenn dann "gescrollt" wird kannst du immer die differenz zu diesem wert berechnen und verteilen...
Hallo,
möglicherweise wäre es eine Lösung die Bars immer von Oben nach Unten zu verändern statt zufällig.
Nehmen wir an du hast 3 Bars im Ursprungszustand (Alle in der Mitte)
Bar#1:||||||||||
Bar#2:||||||||||
Bar#3:||||||||||
und bewegst die Erste davon.
Dann bekommt Bar#2 Eins abgezogen.
Bar#1:||||||||||| (+1)
Bar#2:||||||||| (-1)
Bar#3:||||||||||
Erhöhst du nun Bar#1 nochmal um Eins, bekommt Bar#3 auch einen abgezogen:
Bar#1:|||||||||||| (+1)
Bar#2:|||||||||
Bar#3:||||||||| (-1)
Verringerst du Bar#1 wieder, wird in der selben Reihenfolge nacheinander auf die anderen Bars wieder drauf addiert. Vielleicht auch in umgekehrter Reihenfolge.
Bar#1:||||||||||| (-1)
Bar#2:|||||||||| (+1)
Bar#3:|||||||||
Ich hoffe, ich habe dein Problem richtig verstanden, und ich hoffe du hast meine Idee verstanden.
Grüße
Philipp
Änderung um den Wert X bei einer Trackbar
verursacht Y = X / (AnzTrackbars-1)
Änderung bei allen anderen Trackbars (natürlich mit unweigerlichen Rundungsfehlern)
Meinst Du sowas ?
Philipps Vorschlag lässt sich vorallem gut umsetzten, da du ja schon ein Array hast in dem die Trackbars gespeichert sind. Du kannst also einfach vom Aktuellen auf das Nächste zugreifen und dort den Wert anpassen.
Ich empfehle die werte garnicht direkt in den trackbars zu verändern (das wird sehr umständlich und nervig für den benutzer) sondern den benutzer einstellen zu lassen, was er will. Hinterher kann man sich dann werte so zurechtrechnen, das die summe 100 ergibt und die verhältnisse der werte zueinander den benutzereingaben entsprechen.
Damit man auf eine summe von 100 kommt muss man dann lediglich alle trackbarwerte summieren und dann jeden einzelnen durch die summe teilen. Dann ist die summe immer 1. entsprechend jeden wert wieder mit 100 multiplizieren, um auf 100 zu kommen.
Code sieht dann so aus.
Trackbar[] bars=
{
trackbar1,
trackbar2,
........
};
double sum=0;
foreach(Trackbar t in bars)
sum+=t.Value;
double[] results=bars
.Select((t)=> t.Value*100/sum )
.ToArray();
@ floste: er wollte es ja aber gerne "live" haben.. die Einstellungen nachträglich zu gewichten ist natürlich möglich - aber das ist ja dann kein "live"-Anzeige mehr...
Wenn es nur um eine anzeige geht, kann man versuchen, diese als grünen balken im hintergrund der trackbars anzuzeigen. ich bleibe dabei: die werte ansich zu manipulieren ist eine blöde idee. Möglich zwar schon, aber sehr unpraktisch in den meisten fällen:
@floste
Würd ich mich dann anschließen. Es ist aus Usersicht wohl wirklich besser, wenn sich die Einstellungen nicht "zufällig" verändern. Das wäre wohl er kontraproduktiv..
Gruß dev
Ich sehe Floste's Vorschlag Zielführend. Im Hintergrund der Trackbar einen Grünen Balken Zeichnen, der die Position anzeigt sodass die Summe 100 ergeben würde. Ist nicht weiter kompliziert und der User wird nicht irritiert wenn sich auf einmal die anderen Trackbars bewegen.
Again what learned...
Ich würd die Werte auch als gleitkommazahl nebenher abspeichern und zum setzen der Trackbarvalues nur auf int runden.
Und auch würde mir eine gewichtete Verteilung besser gefallen als einfach nur Change/Trackbars abzuziehen.
Hier eine kleine Spielerei von mir:
class Program
{
static List<decimal> values = new List<decimal>();
static int index = 0;
static void Main(string[] args)
{
values.Add(50);
values.Add(30);
values.Add(20);
WriteValues();
ConsoleKeyInfo key;
while ((key = Console.ReadKey()).Key != ConsoleKey.Escape)
{
if (key.Key == ConsoleKey.LeftArrow && index > 0)
index--;
else if (key.Key == ConsoleKey.RightArrow && index < values.Count - 1)
index++;
else if (key.Key == ConsoleKey.UpArrow)
ChangeValue(1, index);
else if (key.Key == ConsoleKey.DownArrow)
ChangeValue(-1, index);
WriteValues();
}
}
private static void WriteValues()
{
Console.Clear();
Console.WriteLine("Current Index: " + index);
Console.WriteLine("-----------------------------------------------------");
Console.WriteLine("Press Left or Right to change index.");
Console.WriteLine("Press Up or Down to increment or decrement the value.");
Console.WriteLine("Press Escape to exit the program.");
Console.WriteLine("-----------------------------------------------------");
for (int i = 0; i < values.Count; i++)
{
Console.Write((i > 0 ? "+" : "") + Math.Round(values[i], 0));
}
Console.WriteLine("=" + values.Sum().ToString("##,##"));
}
private static void ChangeValue(decimal change, int index)
{
decimal restBefore = 100 - values[index];
decimal restAfter = restBefore - change;
decimal percentage = restAfter / restBefore;
for (int i = 0; i < values.Count; i++)
{
if (i == index)
values[index] += change;
else
values[i] *= percentage;
}
}
}
Lg XXX
Hallo,
danke für die vielen und anregenden Antworten. Letztendlich habe ich es doch über Gleitkommazahlen und deren Rundung gemacht.
Gruß,
Binärathlet
Hallo xxxprod,
funktioniert soweit ganz wunderprächtig, danke, eine Frage allerdings noch zu deiner Methode: was mache ich, im Fall restBefore = 0 ??
private static void ChangeValue(decimal change, int index)
{
decimal restBefore = 100 - values[index];
decimal restAfter = restBefore - change;
decimal percentage = restAfter / restBefore;
for (int i = 0; i < values.Count; i++)
{
if (i == index)
values[index] += change;
else
values[i] *= percentage;
}
}
Hallo Binärathlet,
auf die Schnelle ist mir nur folgende Lösung eingefallen:
decimal percentage = (restBefore == 0) ? ((1 + restAfter) / (1 + restBefore)) : (restAfter / restBefore);
Es geht ja nur darum das Verhältnis zu ermitteln.
Lg XXX