Hallo zusammen.
Gibt es eine Möglichkeit automatisch abgeschnittene Texte in WPF Applications von aussen zu erkennen?
Was meine ich damit:
Ging es vielleicht auch damit?
private Size MeasureString(string candidate)
{
var formattedText = new FormattedText(
candidate,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(this.textBlock.FontFamily, this.textBlock.FontStyle, this.textBlock.FontWeight, this.textBlock.FontStretch),
this.textBlock.FontSize,
Brushes.Black,
new NumberSubstitution(),
1);
return new Size(formattedText.Width, formattedText.Height);
}
Würde das reichen um vom gehoverten Control und dem eingegebenen Text ausrechnen zu können, wieviele Zeichen tatsächlich angezeigt wurden und wieviele eingegeben wurden?
Ich hoffe, dass das so verständlich ist.
Nochmal kurz ganz anders ausgedrückt:
Es geht darum, dass wir einfach ermitteln können, wie lang ein Text sein kann, in einem entsprechenden Control, damit er nicht abgeschnitten werden kann.
Dazu wird ein Programm benötigt, dass dies von aussen ermitteln kann.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Es gibt Programme, die WPF-Anwendungen von "außen" untersuchen und live bearbeiten können.
Früher habe ich dafür den WPF Inspector genutzt, aber den gibt's leider nicht mehr.
Vor kurzem habe ich stattdessen SnoopWpf verwendet und das hat sehr gut funktioniert.
Schau dir mal den Source an, da kannst Du dir sicher einige Tricks abschauen.
Aber ich vermute mal, dass das eine ziemlich komplizierte Angelegenheit werden kann.
Oder das Tool bietet eine API, das habe ich mir nicht angeschaut.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
Hallo Palladin007.
Danke für diese Idee. SnoopWpf hab ich mir sogar tatsächlich schon angeschaut, bevor ich hier die Frage gepostet hatte.
Ich denke, was funktionieren könnte, ist folgender Ansatz:
Möglicherweise geht das so:
var hwndList = (IntPtr)(int)(listWindow.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty));
var listRect = (Rect)listWindow.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Weiss eigentlich jemand, wie man das in der eigenen App machen würde?
Egal welche Control man anzeigt, die Text(e) auf dem Bildschirm anzeigt?
Damit könnte man vielleicht auch Rückschlüsse ziehen, wie man das von aussen mit Windows Automation ermittelt.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Hallo
Ich habe mal bei einem WPF-Projekt eine TextBox eingesetzt, die bei zu langem Text diesen abschneidet und durch "..." ersetzt. Dieses ist auf CodeProject zu finden. Vielleicht hilft Dir das ja.
mfG
Thomas
Für mich klingt das nicht so, als wäre es hier eine Option, das Projekt zu bearbeiten 🙂
Aber klar, wenn es nur darum geht, Texte schön abgeschnitten darzustellen, der Source da ist und man ihn bearbeiten kann/darf, dann ist natürlich eine angepasste TextBox das Optimum.
Ich hab damit genau 0 Erfahrung und kann auch nur wieder zu Spoon verweisen ^^
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
@Thomas.at
Ja, das Problem ist, dass das Konzept bei der Übersetzung nicht richtig durchdacht wurde.
Und das mit den Ellipsen ist zwar eine gute Idee und wird teilweise so gemacht.
Warum ich das so vorgeschlagen hatte, die abgeschnitten Texte (truncated texts) so zu finden, durch Berechnung oder was auch immer es sonst noch für Möglichkeiten geben möge,
ist, dass eben in anderen Sprachen, wenn die Ellipsis benutzt wird (also die "…"-Funktion), dann wird in anderen Sprachen zu viel abgeschnitten.
Z. B. kann in Deutsch/Englisch Notalarm/Emergency Alarm geschrieben werden, nur wenn dann in Spanisch angezeigt wird Alarma de Emergencia
und man nur Alarma de … sieht, dann kann das zu Problemen führen. Normalerweise hätte ich gesagt: Dann vergrössert doch einfach die Controls.
Da die ganze Applikation aber nachher auf einem Panel läuft, mit 1366 x 720 als Auflösung, ist halt teilweise der Platz auf manchen Bildschirmen sehr beschränkt.
Leider scheint sich halt darüber keiner vorher Gedanken gemacht zu haben. Wie so oft…
Ausserdem dachte ich könnte das allgemein eine gute Funktion eines Testtools sein.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
@Palladin007
Klar, übrigens hab ich mir das mal genauer angeschaut, was Controls ermitteln angeht, lohnt es sich sowohl
SnoopWpf
und auch
Accessibility Insights
anzuschauen für WPF unter Windows 10 usw.
Denn das zeigt auch die ganzen Boxes mit Focus auf dem gehoverten Control an.
Hab gerade geschaut, man kann auch nachschauen, wie es das macht.
🙂 Das sollte auf jeden Fall helfen sehr nahe an die Lösung zu kommen, die Controls zu ermitteln.
Mit dem Abschneiden, muss dann immer noch geschaut werden. 🙂
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Warte mal ... Du könntest die TextBoxen bearbeiten?
Warum dann dieser irre Aufwand?
Sowas kann man problemlos global an alle TextBoxen anhängen.
Wie genau man das nun macht, müsste man sich nochmal im Detail anschauen (es gibt sicher fertige Lösungen), aber allein das suchen der "Problemfälle" sollte mit einer AttachedProperty einfach sein. Die kann man mit einem globalen Style überall setzen und dann den Text und die Maße überwachen lassen - und mit den Daten tust Du dann Dinge.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
@Palladin007
Wofür steht eigentlich die 007 bei Dir? Seriennummer Skateboard? James Bond? Geburtstag in spezieller Form? Deine Serien ID. ^^
Ich könnte, wenn ich hier teil vom Softwareteam wäre, bzw. nicht nur die Textcontrols. Denn das wäre genau, das was ich machen würde und den Vorschlag habe ich auch schon eingebracht.
Nur... leider bin ich hier "nur" im Test-Team. Deswegen werden meine Vorschläge hier auch nicht entgegengenommen. Und dann fragt sich halt das Software-Team, wie sie die ganzen abgeschnittenen Texte finden sollten.
Deswegen kam ich aus dem Testerkontext her auf die Idee: Ich könnte ja alle Controls durchgehen und dann schauen.
Leider ist die Softwarequalität so schlecht, dass selbst wenn ich mein eigenes Programm einhängen würde, das nicht garantiert ist, dass das funktioniert.
Denn die App wird quasi "vorsichtig hochgecrasht", sorry aber anders kann ich das bei der Softwarequalität nicht mehr bezeichnen.
Denn es entstehen erst mal ein paar hundert Exceptions, bis Host und Exe laufen. LOL.
Jetzt weiss auch, warum dieser IRRE Aufwand. Denn abgeschnittene Texte wird es immer wieder geben und ich will den Aufwand für mich automatisieren.
Hab keinen Bock, das immer wieder händisch durchzukontrollieren.
Jetzt kennst Du den Hintergrund. 🙂
Hab mir eh neulich schon anhören dürfen: Du findest immer so komische Bugs.
Meine Antwort dazu:
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Hallo
@dr4g0n76
Ich habe den Link eigentlich deshalb gepostet, vielleicht kannst Du hier Dir etwas abschauen, wie da die Berechnung gemacht wird, ob der Text gekürzt werden muss oder nicht. Daraus müsste sich doch dann ergeben, ob der Text ins Feld passt oder nicht.
mfG
Thomas
Mir kam gerade noch eine Idee ...
Die Main-Methode wird bei WPF ja generiert und besteht eigentlich aus nur drei Zeilen:
public static void Main()
{
var app = new App();
app.InitializeComponent();
app.Run();
}
Starte die Anwendung nicht "normal", sondern lade die Assembly zur Laufzeit in dein eigenes Programm.
Dann suchst Du nach der Application-Ableitung (hier "App"), instanziiere sie und führe damit den Code oben aus.
Damit hast Du ein Programm, das dynamisch WPF-Anwendungen nachladen und im eigenen Prozess gekapselt ausführen kann.
Du musst nur die Threads richtig einstellen (STA, ggf. noch mehr), dann ist das kein Problem - hab ich vor einigen Wochen mal für ein anderes Ziel gemacht, dann aber wieder verworfen.
Wenn die Anwendung läuft, hast Du durch die Application-Instanz direkten Zugriff auf alles, inklusive der Fenster, dem Visual-Tree, den Resourcen, etc.
Auf der Basis kannst Du ohne viel Debugger-Magie nach allem suchen, was Du so brauchst.
Wenn bei euch die Main-Methode nicht nur aus den drei Zeilen oben besteht, wird's schwieriger 😉
Aber auch da hätte ich eine Idee, z.B. könntest Du eine zweite Main2-Methode zur Laufzeit im RAM generieren (System.Reflection.Emit) und dann ausführen.
Du musst dabei größtenteils nur den Code von der originalen Main-Methode rüber schreiben - bis auf das "new App()", das lässt Du weg und verwendest stattdessen einen Parameter.
So kannst Du dann wieder die Instanz erzeugen, in einem eigenen Thread der Main2-Methode übergeben und die Daten überwachen.
PS:
Mein erster Versuch nach 15 Minuten:
var appFilePath = @"<...>\bin\Debug\net6.0-windows\WpfApp1.dll";
var appAssembly = Assembly.LoadFile(appFilePath);
var appType = appAssembly.GetTypes()
.Single(t =>
{
var type = t.BaseType;
while (type is not null && type.FullName != "System.Windows.Application")
type = type.BaseType;
return type is not null;
});
var appInstance = (Application)Activator.CreateInstance(appType)!;
appType.GetMethod("InitializeComponent")!.Invoke(appInstance, null);
appInstance.Run();
In der csproj muss <UseWpf>true</UseWpf> stehen.
Er schafft es bis zum "Run", dann vermisst er die baml-Resourcen, aber das kann man sicher auch zurecht tricksen ^^.
Wofür steht eigentlich die 007 bei Dir? Seriennummer Skateboard? James Bond? Geburtstag in spezieller Form? Deine Serien ID. ^^ Keine Ahnung - ist ewig alt und ich hab's nie geändert.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
[STAThread]
static void Main(string[] args)
{
var appFilePath = @"<...>\WpfApp1.dll";
var app = LoadWpfApplicationFrom(appFilePath);
app.LoadCompleted += (s, e) =>
{
Console.WriteLine("Loaded");
foreach (Window window in app.Windows)
{
if (window == app.MainWindow)
Console.Write("Main-");
Console.WriteLine("Window: " + window.Title);
}
};
app.Exit += (s, e) => Console.WriteLine("Exit");
app.Run();
}
private static Application LoadWpfApplicationFrom(string filePath)
{
var appAssembly = Assembly.LoadFile(filePath);
// Find Application-Implementation
var appType = appAssembly.GetTypes()
.Single(t =>
{
var type = t.BaseType;
while (type is not null && type.FullName != "System.Windows.Application")
type = type.BaseType;
return type is not null;
});
// Set ResourceAssembly
const BindingFlags BindingFlags_PrivateStatic = BindingFlags.Static | BindingFlags.NonPublic;
typeof(BaseUriHelper).GetProperty("ResourceAssembly", BindingFlags_PrivateStatic)!.SetValue(null, appAssembly);
typeof(Application).GetField("_resourceAssembly", BindingFlags_PrivateStatic)!.SetValue(null, appAssembly);
// Create and initialize Application-Implementation
var app = (Application)Activator.CreateInstance(appType)!;
appType.GetMethod("InitializeComponent")!.Invoke(app, null);
return app;
}
Mehr schlecht als recht hin gerotzt, aber es tut, was es soll.
Wenn Du die App in einem anderen Thread startest (STA nicht vergessen), kannst Du auch nebenher andere Dinge machen. Bedenke aber aber, dass außerhalb des UI-Threads nicht viel erlaubt ist.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
Leider wird bei uns kein App Object benutzt, wie ich gerade gesehn hatte. Aber das lässt sich ja ändern, wenn ich das ganze benutze, um zu testen.
Ich werde es auf jeden Fall ausprobieren. War auch gerade am Suchen, wie man das mit dem BAML löst. Du warst schneller. 🙂
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Leider wird bei uns kein App Object benutzt, wie ich gerade gesehn hatte.
Ohne Application-Objekt kein WPF.
Oder ich würde gerne den Code dazu sehen, da hat wohl jemand grob fahrlässig alles umgebaut, was geht.
Vermutlich ist es einfach nicht der normale Start, das geht ja recht einfach zu ändern, aber irgendwann gibt's immer ein Application-Objekt.
Und die Klasse hat auch eine statische Current-Property, auch kein ThreadStatic, damit könntest Du aus jedem Thread die Instanz abrufen.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
Ich kann Dir zumindest einen Teil davon zeigen.
Es wurde durch
using <OWN NAMESPACE>;
namespace <OWN NAMESPACE>
{
public static class Program
{
public static void Main(string[] args)
{
var applicationManager = new ApplicationManager();
applicationManager.Start("OFApplication","OFSplashScreen");
}
}
}
Ausgetauscht und die ApplicationManager-Klasse leitet weder von Application ab, noch macht sie sonst was WPF-trächtiges. Nada. Ist auch nicht von etwas anderem abgeleitet und hat auch kein Interface.
Sieht teilweise so aus:
public class ApplicationManager
{
private string _splashScreenType = "Default";
private Window _splashScreen;
private string _applicationName;
private ManualResetEvent _delay = new ManualResetEvent(false);
private ManualResetEvent _splashSync = new ManualResetEvent(false);
public ApplicationManager()
{
}
Und startet Threads über etwas das ein wenig so aussieht wie ein Factorypattern...
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Irgendwann muss es aber ein Application-Objekt geben.
Du musst einen Weg finden, darauf zu warten 😉
Danach müsstest Du es über die Application.Current-Property finden können.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
Das auf jeden Fall. Nur... wass willst Du erwarten, bei einer Software die erstmal 100e von Exceptions erzeugt, bis sie wirklich hochfährt.
Ich hab mal versucht folgendes zu machen:
Es gibt eine Art try/catch-Block für die ganze Applikation. Mit diesem eigens vergewalteten Application-Object, dass die Entwickler selbst "verbrochen" haben. 😉
Ich hab mal den try/catch-Block entfernt, weil zumindest in VS2019 manche Sachen nicht einfach so einsehbar waren, durch Warning-Suppressions and what not.
Selbst wenn Du den weg machst, das Ding ist so instabil, dass die Software dann nicht läuft.
Wie kann man so eine Qualität machen. Aber egal... (egal im Sinne von dann distanzier ich mich halt emotional komplett davon, was kann ich dafür. 😉)
try
{
Application.StartMainProgram();
}
catch(Exception ex)
{
Log(ex).Here();
}
und Du hast Recht. Ich hab das Application-Object auch noch nicht gefunden.
Das einzige wo ich bisher Application.Current gesehen hatte, war in einer Drag/Drop-Routine. Morgen hab ich endlich mal etwas mehr Luft, dann werde ich mir es genauer anschauen.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.
Es hat nichts damit zu tun, dass Du ein Application.Current-Objekt siehst oder nicht, sondern dass WPF das von sich aus macht.
Sie müssten schon die WPF eigene Start-Route nach entwickeln, um ohne Application-Objekt auszugehen und davon gehe ich mal nicht aus.
Und wenn Du nicht weist, wann es das Objekt gibt: Bau einen Button, der das Application-Objekt sucht und untersucht, den drückst Du dann, wenn alles fertig geladen ist.
NuGet Packages im Code auslesen
lock Alternative für async/await
Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.
Das ist tatsächlich so. Der Code ist … na ja. Bisher. Es wird gerade viel umgeändert.
Und vor allem auch das, dass es wie eine normale WPF-App funktioniert.
Auf jeden Fall in der neuesten Version gibt es das jetzt mehr oder weniger zumindest bis jetzt.
Deshalb habe ich mal angefangen, den Vorschlag von Dir auszuprobieren.
Danke nochmals dafür!
Ich werde auf jeden Fall versuchen, das Ganze zum Laufen zu bringen.
Und dann den Code hier ergänzen. Zuerst mal für den allgemeinen Fall.
Denn das Thema muss ich sowieso lösen. Bei uns brennt's halt gerade an vielen Ecken und Enden.
Hier gilt wirklich wieder mal, aufgeschoben ist nicht aufgehoben.
P.S: Ich hab also den Code übernommen und mal versucht erste Schritte zu machen.
Ich werde ein Update hier posten, sobald ich ein paar Schritte weitergekommen bin.
Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.