Laden...

Trackbars nivellieren

Erstellt von Binärathlet vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.556 Views
B
Binärathlet Themenstarter:in
26 Beiträge seit 2010
vor 13 Jahren
Trackbars nivellieren

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

731 Beiträge seit 2006
vor 13 Jahren

Hallo Binärathlet,

sollen die einzelnen TrackBars gewichtet sein oder soll sich die Neuverteilung gleichmäßig ergeben?

MfG
wax

B
Binärathlet Themenstarter:in
26 Beiträge seit 2010
vor 13 Jahren

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

K
142 Beiträge seit 2006
vor 13 Jahren

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.

29 Beiträge seit 2009
vor 13 Jahren

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

4.939 Beiträge seit 2008
vor 13 Jahren

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).

B
Binärathlet Themenstarter:in
26 Beiträge seit 2010
vor 13 Jahren

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'.

29 Beiträge seit 2009
vor 13 Jahren

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...

P
28 Beiträge seit 2010
vor 13 Jahren

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

R
103 Beiträge seit 2009
vor 13 Jahren

Ä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 ?

29 Beiträge seit 2009
vor 13 Jahren

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.

1.130 Beiträge seit 2007
vor 13 Jahren

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();

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

29 Beiträge seit 2009
vor 13 Jahren

@ 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...

1.130 Beiträge seit 2007
vor 13 Jahren

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:

  1. für den benutzer, weil ihm seine einstellungen verwurschtelt werden
  2. für den Programmierer: rundungsfehler, er muss auswählen, welche balken wie angepasst werden, ...

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

29 Beiträge seit 2009
vor 13 Jahren

@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

916 Beiträge seit 2008
vor 13 Jahren

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...

1.378 Beiträge seit 2006
vor 13 Jahren

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

B
Binärathlet Themenstarter:in
26 Beiträge seit 2010
vor 13 Jahren

Hallo,

danke für die vielen und anregenden Antworten. Letztendlich habe ich es doch über Gleitkommazahlen und deren Rundung gemacht.

Gruß,
Binärathlet

B
Binärathlet Themenstarter:in
26 Beiträge seit 2010
vor 13 Jahren

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;
        }
    }

1.378 Beiträge seit 2006
vor 13 Jahren

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