Hallo,
in der Dokumentation zu einer API steht, dass kein Multithreading unterstützt wird. Ich habe jetzt eine Erweiterung programmiert, wo eine Fortschrittsanzeige implementiert ist, weil der Vorgang unter Umständen länger dauern kann. Natürlich ist die Gui eingefroren während der Aktion, so dass der Sinn der Fortschrittanzeige nicht gegeben ist.
Meine Versuche mit Threading und async/await führten allesamt dazu dass das Programm abschmiert. Welche Möglichkeiten habe ich noch, um die Gui zu entkoppeln? Ich verwende das Framework 4.8.1.
Du musst den Target type noch angeben.
<Window.Resources>
<Style TargetType="Button" x:Key="button1">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="LightGray"/>
</Style>
<Style TargetType="Button" x:Key="button2">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Background" Value="LightGray"/>
</Style>
</Window.Resources>
Okay, da sind mehrere Fehler enthalten. Beispielsweise Klassen in Klassen, usw.
Außerdem verwendest Du nicht das MVVM-Pattern.
Hier mal mein Ansatz:
Im ersten Schritt erstellen wir ein UserControl SpielpaarungControl.xaml, das das Aussehen einer Spielpaarung enthält
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>
<Image x:Name="Team1Flagge"
Grid.Column="0"
Height="16"
Width="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Team1.LandFlagge}"/>
<TextBlock x:Name="Team1Name"
Grid.Column="1"
HorizontalAlignment="Left"
Text="{Binding Team1.LandName}"/>
<TextBlock x:Name="MatchSeparator"
Grid.Column="2"
HorizontalAlignment="Center"
Text="vs."/>
<TextBlock x:Name="Team2Name"
Grid.Column="3"
HorizontalAlignment="Right"
Text="{Binding Team2.LandName}"/>
<Image x:Name="Team2Flagge"
Grid.Column="4"
Height="16"
Width="16"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Team2.LandFlagge}"/>
</Grid>
In MainWindow.xaml wird dieses UserControl in die ListView als Vorlage definiert. Zusätzlich definieren wir den DataContext, also eine Klasse, wo MainWindow die benötigten Daten findet.
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<StackPanel>
<TextBlock Text="Spielpaarungen"/>
<ListView x:Name="listView"
Height="auto"
Width="auto"
ItemsSource="{Binding Spielpaarungen}">
<ListView.ItemTemplate>
<DataTemplate>
<local:SpielpaarungControl />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
Es werden noch drei Klassen benötigt - jede Klasse in eine separate Datei. Das sind MainWindowViewModel.cs, Mannschaft.cs und Spielpaarung.cs.
Die Klasse Mannschaft:
public class Mannschaft
{
#region Eigenschaften
public string LandFlagge { get; set; }
public string LandName { get; set; }
#endregion
}
Die Klasse Spielpaarung:
public class Spielpaarung
{
#region Eigenschaften
public Mannschaft Team1 { get; set; }
public Mannschaft Team2 { get; set; }
#endregion
#region Konstruktoren
public Spielpaarung(Mannschaft team1, Mannschaft team2)
{
Team1 = team1;
Team2 = team2;
}
#endregion
}
Die Klasse MainWindowViewModel, das die Logik enthält:
public class MainWindowViewModel
{
#region Felder
private List<Mannschaft> teams;
#endregion
#region Eigenschaften
public List<Spielpaarung> Spielpaarungen { get; set; }
#endregion
#region Konstruktoren
public MainWindowViewModel()
{
Start();
}
#endregion
#region Methoden
private void Start()
{
teams =
[
new Mannschaft()
{
LandName = "Deutschland",
LandFlagge = "Flaggen/FlagDeutschland.png"
},
new Mannschaft()
{
LandName = "Russland",
LandFlagge = "Flaggen/FlagRussland.png"
},
new Mannschaft()
{
LandName = "Polen",
LandFlagge = "Flaggen/FlagPolen.png"
},
new Mannschaft()
{
LandName = "Portugal",
LandFlagge = "Flaggen/FlagPortugal.png"
},
];
Spielpaarungen =
[
new Spielpaarung(teams[0], teams[1]),
new Spielpaarung(teams[2], teams[3])
];
}
#endregion
}
Ergänzend sei noch angemerkt, dass keine Benachrichtigungen erfolgen, wenn sich Eigenschaften ändern. Dafür kannst Du das CommunityToolkit MVVM verwenden oder INotifyPropertyChanged, oder oder oder.
Ich denke mal, dass Du eine Klasse Spielpaarung mit zwei Eigenschaften vom Typ Mannschaft erstellen musst.
Im CellTemplate / Stackpanel dann
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Mannschaft1.LandName}"/>
<Image Source="{Binding Mannschaft1.LandFlagge}" Height="16" Width="16"/>
<TextBlock Text="{Binding Mannschaft2.LandName}"/>
<Image Source="{Binding Mannschaft2.LandFlagge}" Height="16" Width="16"/>
</StackPanel>
Das Thema wurde hier https://stackoverflow.com/questions/12882423/datagrid-disabling-rows-wpf behandelt. Vielleicht hilft Dir das weiter.
Radiobuttons kann man auch stylen. Du hast also die Möglichkeit, das Aussehen nach Deinen Wünschen anzupassen.
Du kannst auch separate Buttons benutzen und eine Logik entwerfen, jeden Button über die Eigenschaft IsEnabled zu aktivieren/deaktivieren.
Mit einer while-Schleife und ReadKey.
char key;
do
{
Console.WriteLine();
Console.Write("Bitte gib + oder - ein ");
ConsoleKeyInfo keyInfo = System.Console.ReadKey();
key = keyInfo.KeyChar;
} while (!key.Equals('+') && !key.Equals('-'));
Das Problem ist, dass der Datapoint für die X/Y-Werte nur double verarbeiten kann. Also kannst Du keine Strings als X-Werte verwenden. Das wird dann alles zu 0 konvertiert und liegt demzufolge übereinander.
Du musst die X-Werte der Eigenschaft AxisLabel eines Datapoint zuweisen und die X-Werte hochzählen.
chart1.Series["Daten"].Points.Add(new DataPoint() { AxisLabel = datumNeu[i], XValue = i, YValues = [data[i]] });
Damit sollte das gewünschte Ergebnis angezeigt werden.
Wie schon weiter oben erwähnt sollte das hier funktionieren.
private void Anzeige(int screenIdx, int xPos, int yPos, int xDim, int yDim)
{
Screen monitor = Screen.AllScreens[screenIdx];
Rectangle screenArea = monitor.WorkingArea;
f3.StartPosition = FormStartPosition.Manual;
f3.BackColor = Color.RebeccaPurple;
f3.Size = new Size(xDim, yDim);
f3.Location = new Point(screenArea.X + xPos, screenArea.Y + yPos);
}
VS sagt Dir ziemlich genau, wo der Fehler liegt.
namespace erstes
{
internal class Program
{
static void Main(string[] args)
{
Funktion.Rechner(10, 20);
Console.WriteLine("1. Seite");
}
}
}
Zweite Datei Funktion.cs
namespace erstes
{
public static class Funktion
{
public static void Rechner(int numba, int numbb)
{
int a = numba;
int b = numbb;
int Result = numba + numbb;
Console.WriteLine("Ergebns ist " + Result);
}
}
}
Das Projekt habe ich aufgegeben, da ich an der Stelle nicht weitergekommen bin.
Hatte auch nur den Zweck etwas rumzuprobieren und mit Blazor vertraut zu werden.
Zitat von Mersaia
die if Anweisung mit der Überprüfung auf Lange, Hoehe, Breite auf 0 um eventuelle Fehlereingaben mit einer Eingabe 0 in Hohe Länge Breite abzufangen? Theoretisch würde ja bspw. nur Länge abfragen auch gehen weil der Anwender bei der Eingabe keine 0 eingeben soll? auch wenn es unsauber ist. es geht nur um das Verständnis.
Ja, die Überprüfung sollte idealerweise ODER-Verknüpft sein. Und das Kriterium sollte nicht == 0 sein sondern <= 0. Dass der Anwender keine 0 eingeben soll, kann ich nicht erkennen.
Ist es das, was Du willst?
case '1':
Console.WriteLine("Neue Kiste erstellen");
for (int index = 0; index < lagerraum.Length; index++)
{
// Prüfe, ob die Kiste definiert ist
if (lagerraum[index].Laenge == 0 && lagerraum[index].Breite == 0 && lagerraum[index].Hoehe == 0)
{
// Lese bei undefinierter Kiste die Werte ein und verlasse die Schleife.
lagerraum[index] = Einlesen(index + 1);
break;
}
}
break;
Ich habe das KI-Modell codegemma befragt und folgenden Snippet als Antwort erhalten:
// Erhalten Sie die letzten beschriebenen Zeile in der angegebenen Spalte
int lastRow = worksheet.Cells[worksheet.Rows.Count, columnNumber].End(Excel.XlDirection.xlUp).Row;
Eine Möglichkeit ist hier beschrieben: https://learn.microsoft.com/de-de/dotnet/desktop/winforms/migration/?view=netdesktop-8.0
Was sagt denn eigentlich Dein Ausbilder? Kannst Du den nicht fragen?
Datei Vendingmachine.cs
public class Vendingmachine
{
#region Fields
/// <summary>
/// Die Getränkefächer
/// </summary>
private readonly List<Slot> slots;
#endregion
#region Constructors
/// <summary>
/// Erstellt eine neue Instanz eines Getränkeautomaten
/// </summary>
public Vendingmachine()
{
slots = [];
}
#endregion
#region Methods
/// <summary>
/// Auffüllen des Getränkeautomaten
/// </summary>
public void Fill()
{
slots.Add(new Slot(new Beverage("Helles Bier", 2.60m), 20, 5));
slots.Add(new Slot(new Beverage("Pils", 3.00m), 25, 5));
slots.Add(new Slot(new Beverage("Schwarzbier", 2.70m), 20, 5));
slots.Add(new Slot(new Beverage("Weizen", 3.20m), 20, 5));
slots.Add(new Slot(new Beverage("Lagerbier", 2.80m), 20, 5));
slots.Add(new Slot(new Beverage("Kellerbier", 2.60m), 20, 5));
slots.Add(new Slot(new Beverage("Bockbier", 3.40m), 20, 5));
slots.Add(new Slot(new Beverage("Zwickl", 2.90m), 20, 5));
RegisterSlots();
}
/// <summary>
/// Gebe den Begrüßungsbildschirm aus
/// </summary>
public void Greet()
{
Console.WriteLine("###############################");
Console.WriteLine("# #");
Console.WriteLine("# Hello Customer! #");
Console.WriteLine("# #");
Console.WriteLine("###############################");
Console.WriteLine("# #");
Console.WriteLine("# {0,3} {1,-13} {2,6} {3,2} #", "Id", "Name", "Price", "No");
Console.WriteLine("# #");
PrintSlotInfo();
}
/// <summary>
/// Gebe die Getränkefächer am Begrüßungsbildschirm aus
/// </summary>
private void PrintSlotInfo()
{
for (int i = 0; i < slots.Count; i++)
{
Console.WriteLine("# {0,2}. {1,-12} {2,6}€ {3,2} #", i + 1, slots[i].Beverage.Name, slots[i].Beverage.Price, slots[i].CurrentLevel);
}
}
/// <summary>
/// Die Getränkefachanwahl
/// </summary>
/// <param name="slotnumber">Die Getränkefachnummer</param>
public void Select(int slotnumber)
{
if (slotnumber > 0 && slotnumber <= slots.Count)
{
int slotIndex = slotnumber - 1;
slots[slotIndex].Select();
}
else if (slotnumber == 0)
{
Checkout();
}
}
/// <summary>
/// Kein weiteres Getränk angewählt
/// deshalb Ausgabe der gewählen Getränke mit
/// </summary>
private void Checkout()
{
decimal total = 0.0m;
string message = string.Empty;
Print(string.Empty);
Print("Checkout:");
foreach (Slot slot in slots)
{
total += slot.Selected * slot.Beverage.Price;
message = slot.GetTotal();
if (!string.IsNullOrEmpty(message))
{
Print(message);
}
slot.ResetSelection();
}
Print(new string('=', 37));
Print(string.Format("{0,36}€", total));
Print(string.Empty);
Print(string.Empty);
Greet();
}
/// <summary>
/// Konsolenausgabe
/// </summary>
/// <param name="message">Die Nachricht die auf der Konsole ausgegeben werden soll</param>
private void Print(string message)
{
Console.WriteLine(message);
}
/// <summary>
/// Abonniere die Getränkefachereignisse
/// </summary>
private void RegisterSlots()
{
RegisterSlotsThreshold();
RegisterSlotsEmpty();
}
/// <summary>
/// Aboniere von jedem Getränkefach das Schwellwertereignis
/// </summary>
private void RegisterSlotsThreshold()
{
foreach (Slot slot in slots)
{
slot.BelowThreshold += OnSlotIsBelowThreshold;
}
}
/// <summary>
/// Aboniere von jedem Getränkefach das Fach leer Ereignis
/// </summary>
private void RegisterSlotsEmpty()
{
foreach (Slot slot in slots)
{
slot.Empty += OnSlotIsEmpty;
}
}
/// <summary>
/// Gebe einen Alam aus, wenn das Getränkefach leer ist
/// </summary>
/// <param name="sender">Das Getränkefach</param>
/// <param name="e">Nicht in Verwendung</param>
private void OnSlotIsEmpty(object? sender, EventArgs e)
{
if (sender is Slot slot)
{
Console.WriteLine($"ALERT: The beer {slot.Beverage.Name} is empty!");
}
}
/// <summary>
/// Gebe einen Alam aus, wenn der Schwellwert unterschritten wird
/// </summary>
/// <param name="sender">Das Getränkefach</param>
/// <param name="e">Nicht in Verwendung</param>
private void OnSlotIsBelowThreshold(object? sender, EventArgs e)
{
if (sender is Slot slot)
{
Console.WriteLine($"ALERT: The beer {slot.Beverage.Name} is running out!");
}
}
#endregion
}
Datei Program.cs
internal class Program
{
static void Main()
{
Vendingmachine vendingmachine = new();
vendingmachine.Fill();
vendingmachine.Greet();
ConsoleKeyInfo pressedKey;
do
{
pressedKey = Console.ReadKey();
if (int.TryParse(pressedKey.KeyChar.ToString(), out int slotNumber))
{
vendingmachine.Select(slotNumber);
}
} while (pressedKey.Key != ConsoleKey.X);
}
}
Aufgabe für Dich: Es werden bei der Konsolenausgabe statt dem € Zeichen nur ? angezeigt. Warum ist das so und wie lässt sich das beheben? Eine zusätzliche Codezeile ist dazu nötig!
Zweite Aufgabe für Dich: Die Konsolenausgaben vereinheitlichen.
Okay! Muss das auf zwei Posts verteilen, da mehr als 8000 Zeichen!
Datei Beverage.cs
public class Beverage
{
/// <summary>
/// Der Getränkename
/// </summary>
public string? Name { get; }
/// <summary>
/// Der Getränkepreis
/// </summary>
public decimal Price { get; }
/// <summary>
/// Erstelle eine neue Instanz einer Getränkesorte
/// </summary>
/// <param name="name">Der Getränkename</param>
/// <param name="price">Der Getränkepreis</param>
public Beverage(string name, decimal price)
{
Name = name;
Price = price;
}
}
Datei Slot.cs
public class Slot
{
#region Properties
/// <summary>
/// Die Getränkesorte
/// </summary>
public Beverage Beverage { get; set; }
/// <summary>
/// Die Kapazität des Getränkefachs
/// </summary>
public int Capacity { get; set; }
/// <summary>
/// Der aktuelle Füllstand des Getränkefachs
/// </summary>
public int CurrentLevel { get; private set; }
/// <summary>
/// Der Schwellwert des Getränkefachs bei dessen Unterschreitung ein Alarm ausgelöst wird
/// </summary>
public int Threshold { get; set; }
/// <summary>
/// Die Anzahl der Getränke
/// </summary>
public int Selected { get; private set; }
#endregion
#region Events
/// <summary>
/// Das Ereignis welches ausgelöst wird wenn der Schwellwert unterschritten wird
/// </summary>
public event EventHandler? BelowThreshold;
/// <summary>
/// Das Ereignis welches ausgelöst wird wenn das Getränkefach leer ist
/// </summary>
public event EventHandler? Empty;
#endregion
#region Constructors
/// <summary>
/// Erstelle eine neue Instanz eines Getränkefaches
/// </summary>
/// <param name="beverage">Die Getränkesorte</param>
/// <param name="capacity">Die Kapazität des Getränkefaches</param>
/// <param name="threshold">Der Schwellwert</param>
public Slot(Beverage beverage, int capacity, int threshold)
{
Beverage = beverage;
Capacity = capacity;
Threshold = threshold;
CurrentLevel = Capacity;
Selected = 0;
}
#endregion
#region Methods
/// <summary>
/// Die Getränkewahl
/// </summary>
public void Select()
{
if (CurrentLevel > 0)
{
Selected++;
CurrentLevel--;
Console.WriteLine($"You selected a {Beverage.Name} for {Beverage.Price}");
if (CurrentLevel < Threshold)
{
BelowThreshold?.Invoke(this, new EventArgs());
}
}
else if (CurrentLevel == 0)
{
Empty?.Invoke(this, new EventArgs());
}
}
/// <summary>
/// Setzt die Getränkewahl zurück
/// </summary>
public void ResetSelection()
{
Selected = 0;
}
/// <summary>
/// Die Getränkeabwahl
/// Nicht implementiert
/// </summary>
public void Unselect()
{
Selected--;
CurrentLevel++;
}
/// <summary>
/// Liefert den Preis von einem Getränk
/// Nicht implementiert
/// </summary>
/// <returns>Der Getränkepreis</returns>
public string GetPrice()
{
return $"Price for {Beverage.Name} is {Beverage.Price:C2}";
}
/// <summary>
/// Liefert den Gesamtpreis von einem Getränk
/// </summary>
/// <returns>Der Gesamtpreis von einem Getränk</returns>
public string GetTotal()
{
string message = string.Empty ;
if (Selected > 0)
{
message = string.Format("{0, 2}x {1,-13} a {2,6}€ = {3,6}€", Selected, Beverage.Name, Beverage.Price, Selected * Beverage.Price);
}
return message ;
}
#endregion
}
Verstehe ich jetzt nicht ganz!
Hast Du keine Möglichkeit, zu Hause die Datei auf einen USB-Stick runterzuladen? Der Name der ZipDatei ist völlig irrelevat und lässt sich auf dem USB-Stick ändern.
Natürlich kann ich hier auch die Codeblöcke reinstellen. Klöppelst Du Dir das dann zusammen?
Servus,
ich habe Dir im Anhang einen Getränkeautomaten mit Klassenstruktur angehängt.
Das Programm ist sicherlich auch nicht perfekt, aber es sollte Dir eine grobe Richtung geben, wie sowas aussehen könnte.
Mit Absicht sind ein paar Methoden nicht umgesetzt (siehe "nicht implementiert" Kommentar).
Die Ausgabefunktionen kann man weiter vereinheitlichen, also Strings erstellen und an die Print-Methode schicken - aber das überlasse ich Dir 😃
Zitat von Abt
Meine persönlichen 50 Cent: seh ich deutschen Code, ist das automatisch für mich schlechter lesbar und ich bekomm Würgen.
Wie meinst Du das?
a) Deutsche programmieren schei..e?
b) Verwendung von deutschen Variablen- und Methodennamen? Da würde ich Dir zustimmen. Das macht man nicht.
Beispielsweise kann man die innere do while Schleife komplett in eine eigene Methode auslagern. In dieser ausgelagerten Methode kannst Du dann auch noch den falschen Key mit integrieren!
Die Antwort lautet vermutlich ja! Warum?
Weil wie die Spaghettis der Code in dieser Methode viel zu lang ist.
Die Methode beinhaltet zu viele Aufgaben.
Besonders der zweite Teil - nach der inneren do while Schleife - schwer verständlich ist.
Kenn mich mit Python null aus, aber müsste es nicht lauten:
if x == 1:
print(multiply())
if x == 2:
print(sub())
if x == 3:
print(multiply())
if x == 4:
print(div())
Hallo, wie funktioniert das richtige Antworten mit Zitat?
Wenn ich auf einen Beitrag mit Zitat antworten will, dann steht der Cursor im Zitatbereich und ich kann ihn nicht unter das Zitat bewegen.
Wenn ich die Enter-Taste betätige, dann wird der Zitat-Bereich nur erweitert.
Zitat von T-Virus
@Caveman
Eigentlich kann man auf das Dictionary komplett verzichten und direkt mit einem Array arbeiten.
Du musst nur über den Index direkt auf das Array zugreifen und dort dann die Einträge hochzählen.
Ja, das stimmt! Ich habe da nicht nachgedacht und aus Gewohnheit das Dictionary verwendet. Mit einem Dictionary kann man auch als Schlüssel einen String verwenden und muss sich dann nicht immer Zahlen merken - Welche Biersorte hat nochmal den Index 4?!
Mit einem Dictionary geht das erheblich einfacher.
internal class Program
{
private static readonly Random random = new();
private static Dictionary<int, int>? bierDictionary;
static void Main(string[] args)
{
CreateDictionary(8);
List<int> gewaehlteBiere = ChooseBeers();
AssignToDictionary(gewaehlteBiere);
Print(gewaehlteBiere);
Console.ReadKey();
}
private static void Print(List<int> gewaehlteBiere)
{
Console.WriteLine("Gewählte Biere: [ {0} ]", string.Join(", ", gewaehlteBiere));
foreach (KeyValuePair<int, int> sorte in bierDictionary)
{
Console.WriteLine($"{sorte.Key} --> {sorte.Value}x");
}
}
private static void AssignToDictionary(List<int> gewaehlteBiere)
{
foreach (int sorte in gewaehlteBiere)
{
bierDictionary[sorte]++;
}
}
private static List<int> ChooseBeers()
{
List<int> gewaehlteListe = [];
for (int i = 0; i < 16; i++)
{
gewaehlteListe.Add(random.Next(1, 9));
}
return gewaehlteListe;
}
private static void CreateDictionary(int anzahlBiersorten)
{
bierDictionary = [];
for (int i = 1; i <= anzahlBiersorten; i++)
{
bierDictionary.Add(i, 0);
}
}
}
Ja, alles gut!
Es scheint irgendwo in der Mitte das Problem zu sein.
Ein tracert mit 250 hops kommt nicht ans Ziel.
C:\Users\Caveman>tracert -4 mycsharp.de
Routenverfolgung zu mycsharp.de [13.107.246.45]
über maximal 30 Hops:
1 1 ms <1 ms <1 ms fritz.box [192.168.22.1]
2 4 ms 3 ms 4 ms p3e9bf275.dip0.t-ipconnect.de [62.155.242.117]
3 7 ms 7 ms 7 ms m-ec4-i.M.DE.NET.DTAG.DE [217.5.66.186]
4 7 ms 9 ms 7 ms et-0-0-61-0.ier01.muc30.ntwk.msn.net [104.44.37.251]
5 * * * Zeitüberschreitung der Anforderung.
6 * * * Zeitüberschreitung der Anforderung.
30 * * * Zeitüberschreitung der Anforderung.
Geht es nur mir so, oder besteht momentan allgemein das Problem, dass die Seiten mehrere Minuten brauchen zum Laden?
static void Main(string[] args)
{
int[] a = [1, 5, 7, 10];
Print(a);
Index startIndex = 1;
Index endIndex = ^0;
int[] b = a[startIndex..endIndex];
Print(b);
int[] c = [2, 4, 6, 8];
Print(c);
startIndex = 0;
endIndex = 2;
int[] d = c[startIndex..endIndex];
Print(d);
Console.ReadKey();
}
private static void Print(int[] array)
{
Console.WriteLine("[{0}]", string.Join(", ", array));
}
Mein Ansatz:
internal class Program
{
private static Dictionary<int, int> dice;
private static readonly Random random = new();
static void Main()
{
bool result;
do
{
Init();
Dice();
Print();
result = Analyze();
if (result)
{
Console.WriteLine("Is valid");
}
else
{
Console.WriteLine("Is not valid");
}
} while (result);
Console.ReadKey();
}
private static void Init()
{
dice = [];
for (int i = 1; i < 7; i++)
{
dice.Add(i, 0);
}
}
private static void Dice()
{
for (int i = 1; i < 7; i++)
{
dice[random.Next(1, 7)]++;
}
}
private static bool Analyze()
{
bool isValid = dice[1] > 0 || dice[5] > 0;
if (!isValid)
{
for (int i = 1; i < 7; i++)
{
if (dice[i] > 2)
{
isValid = true;
break;
}
}
}
return isValid;
}
private static void Print()
{
Console.Write($"[ 1:{dice[1]}, 2:{dice[2]}, 3:{dice[3]}, 4:{dice[4]}, 5:{dice[5]}, 6:{dice[6]} ]: ");
}
}
Servus,
im angehängten Projekt sollte das mit DI klappen.
Jetzt ist der Groschen gefallen, was gemeint war!
Allerdings rufe ich die Methode Create1DModelData() in meinem Beispiel nur einmal auf, nämlich im Konstruktor.
Deshalb ist das nicht zutreffend. Mir ist schon klar, dass ich das bei mehrmaligem Methodenaufruf nicht machen darf.
Hintergrund: Es handelt sich um ein Testprojekt, in dem ich meine ganzen Versuche in eigene Methoden ausgelagert habe. Im Eingangspost habe ich dann die falsche Zeile gelöscht. In dem Testprojekt werden nur Datenstrukturen erzeugt, die im DataGrid angezeigt werden.
@TH69: Das habe ich auch schon durchgelesen. Weiter unten in dem Thread wird das DataGrid2D-Control erwähnt, welches auch eine DataTable im Hintergrund verwendet.
@Alf Ator: Tut mir leid, aber ich weiß überhaupt nicht, was Du mir mitteilen willst. In Deinem Bild wird eine Spalte gezeigt. Wie kommst Du auf - sagen wir mal - 50 Spalten?
Ich möchte im Prinzip eine n,m-Matrix von Model oder meinetwegen CellViewModel an die DataGrid binden.
Edit: Ich habe im Übrigen auch die Antwort von Abt nicht verstanden, obwohl ich mir das mindestens 10x durchgelesen habe.
Nach all dem was ich bis bis jetzt gelesen habe, kann man offenbar keine zweidimensionale Liste an das Datagrid binden.
Ich werde es jetzt erstmal mit einer DataTable als ItemsSource weiter versuchen.
Arrghh, habe im ViewModel die falsche Zeile rausgelöscht beim Bereinigen!
Statt
public ObservableCollection<Model> Items { get; set; }
muss es lauten
public ObservableCollection<Model> ItemsModel1D { get; set; }
Hallo,
ich habe mit OpenXML eine Excedatei mit ca. 5800 Zeilen und 175 Spalten in eine List<List<T>> Struktur eingelesen, wobei T eine Zelle darstellt. T enthält mehrere Properties, aber nur eines davon soll im DataGrid dargestellt werden,
Leider schaffe ich es nicht, diese Datenstruktur an das DataGrid zu binden und Onkel Google ist nicht sehr hilfreich!
Eine List<T> bekomme ich noch wie folgt dargestellt.
Wie muss ich weiter vorgehen, damit ich eine List<List<T>> an das DataGrid binden kann?
public class Model
{
public string? UnImportantValue1 { get; set; }
public string? UnImportantValue2 { get; set; }
public string CellValue { get; set; }
}
public class MainWindowViewModel
{
public ObservableCollection<Model> Items { get; set; }
public ICommand ColumnGeneratingEvent { get; set; }
public MainWindowViewModel()
{
ColumnGeneratingEvent = new CcActionCommand(OnAutoGeneratingColumnExecuted, null);
Create1DModelData();
}
private void Create1DModelData()
{
ItemsModel1D = new ObservableCollection<Model>();
for (int row = 0; row < 10; row++)
{
ItemsModel1D.Add(new Model() { CellValue = $"Row {row} - Col 0", UnImportantValue1 = "Blah", UnImportantValue2 = "Blubb" });
}
}
private void OnAutoGeneratingColumnExecuted(object obj)
{
if (obj is DataGridAutoGeneratingColumnEventArgs eventArgs)
{
eventArgs.Column.Header = "Header";
}
}
}
<!--ItemsModel1D DataGrid-->
<DataGrid x:Name="TestDataGrid"
AlternatingRowBackground="LightSlateGray"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
SelectionUnit="Cell"
SelectionMode="Extended"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserSortColumns="False"
CanUserReorderColumns="False"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
ItemsSource="{Binding ItemsModel1D}">
<DataGrid.Columns>
<DataGridTextColumn Header="Header" Binding="{Binding CellValue}" />
</DataGrid.Columns>
<behav:Interaction.Triggers>
<behav:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=TestDataGrid}" >
<behav:InvokeCommandAction Command="{Binding ColumnGeneratingEvent, Mode=OneWay}" PassEventArgsToCommand="True" />
</behav:EventTrigger>
</behav:Interaction.Triggers>
</DataGrid>
Hallo,
das handhabe ich auch so.
Grundsätzlich wollte ich nicht zu viel ändern, weil
Servus,
wenn ich alles richtig verstanden habe, dann vielleicht so (ungetestet)
public App()
{
Ioc.Default.ConfigureServices(
new ServiceCollection()
.AddSingleton<MainWindow>()
.AddSingleton<MainWindowViewModel>()
.AddTransient<IUserService, UserService>()
.BuildServiceProvider() );
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
mainWindow.DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>();
mainWindow.Show();
}
public MainWindow()
{
InitializeComponent();
}
public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
public RelayCommand AddNameButton { get; set; }
private readonly IUserService userService;
private string _inputName;
public string InputName
{
get { return _inputName; }
set
{
_inputName = value;
OnPropertyChanged();
}
}
public MainWindowViewModel(IUserService userService)
{
this.userService = userService;
AddNameButton = new RelayCommand(() =>
{
Usernames.Clear();
Usernames = userService.GetUsers(InputName);
});
}
}
public ObservableCollection<string> Usernames { get; private set; } = new ObservableCollection<string>();
public UserService()
{
}
public ObservableCollection<string> GetUsers(string name)
{
Usernames.Add(name);
return Usernames;
}
}
Ich habe auf meinen Rechnern keine Virenscanner installiert!
Das funktioniert wahrscheinlich nicht, da der Namespace nicht stimmt. In der Datei Page2.xaml ist local auf WpfAppToolBar.Pages definiert. NavigationSeite2 liegt aber im Namespace WpfAppToolBar.
Eine Änderung in Page2.xaml sollte das Problem beheben.
Statt
<local:NavigationSeite2 Text="Warum geht das nicht?"/>
das hier
<n:NavigationSeite2 Text="Warum geht das nicht?"/>
Match hat doch die Eigenschaft Success.
Match match = pattern.Match(input);
if (match is not null && match.Success )
{
Console.WriteLine("Hat gematcht!");
}
else
{
Console.WriteLine("Hat nicht gematcht!");
}
Tausche den ContentPresenter gegen einen GridViewRowPresenter!
<!--<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />-->
<GridViewRowPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
Hallo,
ich habe für Excel ein AddIn erstellt, das im Wesentlichen nur Tabellenblatter kopieren soll und auf einem neuen Tabellenblatt Formeln einträgt.
Das funktioniert auch alles.
Das AddIn habe ich veröffentlicht und mit dem Setup auf einem anderen Computer installiert. Das AddIn wird Angezeigt, auch wenn das Ribbon nicht so ist wie auf dem Entwicklungscomputer.
Allerdings dauert es mehrere Sekunden, bis der Button als aktiv angezeigt wird. Auch dauert es mehrere Sekunden, bis der Code ausgeführt wird.
Woran kann das liegen?
Ja, es geht darum, dass sich nicht die konsumierenden Klasse um die Aufgaben kümmern müssen, sondern diese Verantwortichkeiten in der jeweiligen Klasse bleibt. Bin aber nicht so der Erklärbär 😃
Habe mal in Anlehnung an deinem Beispiel was gebastelt.
public interface IDoSomething
{
List<string> List { get; set; }
string Sort();
}
public class DoSomething : IDoSomething
{
public List<string> List { get; set; }
public DoSomething()
{
List = new List<string>();
}
public string Sort()
{
return List.ElementAt(1);
}
}
public class DoSomethingElse : IDoSomething
{
public List<string> List { get; set; }
public DoSomethingElse()
{
List = new List<string>();
}
public string Sort()
{
return List.ElementAt(0);
}
}
public class Worker
{
private IDoSomething doSomething;
public List<string> List
{
get { return doSomething.List; }
set { doSomething.List = value; }
}
public Worker(IDoSomething doSomething)
{
this.doSomething = doSomething;
}
public void Add(string item)
{
doSomething.List.Add(item);
}
public string Execute()
{
return doSomething.Sort();
}
}
internal class Program
{
static void Main(string[] args)
{
Worker worker1 = new(new DoSomething());
Worker worker2 = new(new DoSomethingElse());
for (int i = 0; i < 10; i++)
{
worker1.Add($"item {i:D2}");
worker2.Add($"item {i:D2}");
}
Console.WriteLine($"Worker1: Sort => {worker1.Execute()}");
Console.WriteLine($"Worker2: Sort => {worker2.Execute()}");
Console.ReadKey();
}
}
Ich verwende entweder Inno Setup oder die Microsoft Visual Studio Installer Projects 2022
Vielleicht hilft das weiter:
Xaml:
<Window x:Class="MyCSharp125410.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyCSharp125410"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TreeView Name="myTreeView" Grid.Row="0" ItemsSource="{Binding Nodes}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="ExtraBold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<Button x:Name="AddNodeButton"
Grid.Row="1"
Content="Add Child-Node to Node 05"
Command="{Binding AddNodeCommand}"/>
</Grid>
</Window>
Das ViewModel:
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace MyCSharp125410
{
public class MainWindowViewModel
{
public ObservableCollection<Node> Nodes { get; set; }
public ICommand AddNodeCommand { get; private set; }
public MainWindowViewModel()
{
Nodes = new ObservableCollection<Node>
{
new Node() { Name = "Node 01" },
new Node() { Name = "Node 02" },
new Node() { Name = "Node 03" },
new Node() { Name = "Node 04" },
new Node() { Name = "Node 05" },
new Node() { Name = "Node 06" }
};
Nodes[1].Children.Add(new Node() { Name = "Node 02.1" });
AddNodeCommand = new RelayCommand(OnAddNodeExecuted);
}
private void OnAddNodeExecuted()
{
Nodes[4].Children.Add(new Node() { Name = $"Node 05.{Nodes[4].Children.Count + 1}" });
}
}
}
Und ein Node Model:
using System.Collections.ObjectModel;
namespace MyCSharp125410
{
public class Node
{
public string Name { get; set; }
public ObservableCollection<Node> Children { get; set; }
public Node()
{
Children = new ObservableCollection<Node>();
}
}
}
In dem Beispiel habe ich das CommunityToolkit.Mvvm wegen dem RelayCommand noch über Nuget installiert.
Wenn alles zusammengerührt ist, dann sollte bei jedem Klick auf den Button ein neuer Child-Knoten im Node 05 erstellt werden.