Ach mache das sicherlch, habs schonmal vor 4 Jahren gemacht denke das sollte noch im Gedächniss sein. Den Rum brauche ich nur zum ähhh darum 😉
Sorry hier das richtige:
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
{
switch (keyData)
{
case Keys.Escape:
return true;
case Keys.Alt | Keys.F4:
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
Also in diesen Beispiel wird "ALT" + "F4" dieser Klammeraffengrif abgefangen und verhindert und das Eingeben der ESC Taste, einfach um das von dir gewünschte erweitern oder umschreiben.
Ich hatte vorrhin True und False verkehrt und selbst falsch rausgeodert.. na ja 🙂
Hrm, okay sollte sowas nicht zu schnell hinkritzeln, sekunde habs gleich wieder im Gedächniss 🙂
Dann machen wir es uns einfach:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData != Keys.Alt || Keys.Down)
return false;
else if (keyData != Keys.Alt || Keys.Up)
return false;
return base.ProcessCmdKey(ref msg, keyData);
}
Einfach mit || weiter BitWeise verodern wenn noch andere Nachrichten draussen haben möchtest, kannst auch das ganze schöner schreiben wenn magst 🙂
Wenn das nicht geht gibts noch ne 'härtere' Methode. Aber versuchs erstmal.
Jup da kann dir wer weiterhelfen🙂
Vorweg solltest du dazu einiges beachten:
1.) Bestimmte Tasten , z. B. die EINGABETASTE, TAB und ESC sowie die Pfeiltasten, werden von Steuerelementen automatisch behandelt. Damit diese Tasten das KeyDown-Ereignis auslösen, muss man in jeden Steuerlement die KeyDown-Ereignise überschreiben.
2.) Bei der WndProc werden zwar ebenso die WindowsNachrichten für die Tasten bearbeitet nur eben sind diese wie Punkt 1.) zu behandeln.
Frage: Was willst du erreichen, möchtest du die Tasten global Abfragen oder eben nur an einer entsprechenden Stelle wie z.B dem Formular unter berücksichtigung von Punk 1.) oder Systemweit?
Dazu hat egrath einen guten Artikel verfasst (sind PDFs). Aber kann dir den Link nur empfehlen.
Das Wochenende habe ich damit verbracht nen alten Rechner zum laufen zu kriegen, was nicht klappte... Nach 4 Stunden meinte nen Freund zu mir, für was ist das Kabel da gut....
Glaube das geht jeden mal so 🙂
*schmunzelt* Ich hab nur von Dragon abgekupfert, mehr auch nicht 🙂
Wünsche dir und allen anderen auch eine schöne Weihnachtszeit!
Hrm, wenn es nicht um den geposteten Code geht von dir sondern um die Frage als solches dann glaube ich suchst du nach Multithreading Funktionen.
Dafür solltest mal unter WaitHandels nachsehen. Du kannst dann Simultan Threads laufen lassen und diese beobachten, beschleunigen kann man einen Thread selbst nicht. Aber man könnte aber in einer WaitHandels Liste eben bestimmte Threads anhalten wenn ein Thread eben mehr performance benötigen würde und diese nach dessen abarbeitung oder bei nen bestimmten Flag dann wieder joinen.
Nur phuu ob das wirklich so funktioniert, ich denke mal Windows ist da ziemlich dynamisch was das angeht, bei ner Linux Anwendung geht sowas bestimmt.
Aber ich fürchte wenn deine Frage wirklich dahin abziehlt muss ich passen. Aber werde den Thread weiter verfolgen.
Also du kannst nun etweder so machen.
// deine main form methode
private void btnGenerateNewRow_Click(object sender, EventArgs e)
{
NewRowBox nrb = new NewRowBox();
nrb.m_hWndMain = this.Handle;
nrb.ShowDialog();
}
// in der Klasse NewRowBox
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate,
IntPtr hrgnUpdate, uint flags);
public IntPtr m_hWndMain = IntPtr.Zero;
private void InvalidateWindow()
{
WinAPI.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero,
0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/
| 0x0001/*RDW_INVALIDATE*/);
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
InvalidateWindow(this.m_hWndMain);
}
Oder halt die MainForm übergeben und das Invalidate auslösen.
Oder du kannst auch die MainForm als Singleton erstellen und wiederum das Invalide auslösen. Was dir halt mehr zusagt 🙂
Hrm, ich wusste nicht das es ein Bug ist?
Normal Compalierst dein Projekt einmal durch, gehst in den Designer klickst auf die Ribbonbar Toolbox und machst rechte Maustaste ChooseItems oder ItemsAuswählen. Gehst dann nen Kaffe holen, schaust nochmal rein, gehst eine Rauchen und wenn wieder am Arbeitsplatz bist und Glück hast - Kommt ne Auswahlliste wo dann dein Projekt Auswählen kannst und er zeigt dir dann deine Controls an.
Hrm, okay grübelt
Wo rufst den das PopUp genau auf wenn nicht von der Form selbst die du refreshen möchtest?
Muh, das richt nach Ribbon Controls.
Ich weis nicht ob .NET Standardcontrols das wirklich können.
Aber um ehrlich zu sein, sei FAUL!
Gönn dir und deinen Team einfach die DevExpress Controls, da kann man sich das Zeug sogar prima zusammen klicken 🙂
*hust* das sollte keine Schleichwerbung sein, hab nur die Erfahrung gemacht das es damit sehr elegant geht so etwas zu realisieren. Mal davon abgesehen das es gut aussieht, die jungs haben sich da wirklich Mühe gegeben.
Oki, mach ne neue Klasse, leite diese von ListView ab, dann im Konstruktor gibst du folgendes an.
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.OptimizedDoubleBufferm true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
Dann verwendest deine neue Klasse statt den ListView, brauchst es ja in der Klasse eben schnell zu ersetzen 🙂
Ha! Endlich mal wieder was GUI technisches 🙂
Also du hast zwei Möglichkeiten das schönste ist das Invalidate aufzurufen vom Control oder Form des Lagerbildes.
Das zweite wäre via WinAPI, was nicht mehr als Handle greifbar hättest:
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, [In] ref RECT lprcUpdate,
IntPtr hrgnUpdate, uint flags);
[DllImport("user32.dll")]
static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate,
IntPtr hrgnUpdate, uint flags);
#define RDW_INVALIDATE 0x0001
#define RDW_INTERNALPAINT 0x0002
#define RDW_ERASE 0x0004
#define RDW_VALIDATE 0x0008
#define RDW_NOINTERNALPAINT 0x0010
#define RDW_NOERASE 0x0020
#define RDW_NOCHILDREN 0x0040
#define RDW_ALLCHILDREN 0x0080
#define RDW_UPDATENOW 0x0100
#define RDW_ERASENOW 0x0200
#define RDW_FRAME 0x0400
#define RDW_NOFRAME 0x0800
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
// sample
private void InvalidateWindow()
{
WinAPI.RedrawWindow(this.Handle, IntPtr.Zero, IntPtr.Zero,
0x0400/*RDW_FRAME*/ | 0x0100/*RDW_UPDATENOW*/
| 0x0001/*RDW_INVALIDATE*/);
}
Zwei Dinge
1.) combobox.selectedItem.ToString() gibt dir den Typen als String geschrieben von selectedItem zurück aber nicht den Text!
(Okay wenn das Item was hinzugefügt wurde das protected override string ToString() überschrieben hast evtl. schon, aber vergiss das mal)
2.) combobox.SelectedIndex = 0 gibt dir den Index zurück aber nicht den Wert von Item 🙂
Mach einfach combobox.selectedItem != null 🙂
Mit 2.0 meint er das .NET Framework 2.0 das dir Fehlermeldungen ausgibt (Exceptions) das tut das alte .NET Framework 1.X zwar auch genauso das neueste. Aber das alte .NET Framework gab eben keine so detalierten Fehlermeldungen aus was Threads angeht.
Was er mit UI meint ist das UserInterface, damit meint man nichts anderes als die Formulare die der Benutzer zur Ansicht bekommt. Wie eine ComboBox oder ein Label usw.
Was svenson dir richtiger weise sagen wollte ist das es zu Fehlern kommt, mal unabhängig von deiner Frage weil er gesehen hat das du in deiner ThreadMethode z.B:
private void ThreadProcedere1()
{
for(int i = v1; i < b1; i++)
{
RTBOUT.AppendText("\nThread1: "+i);
Thread.Sleep(s1);
}
RTBOUT.AppendText("\n\tThread1 fertig");
BT11.Enabled = true;
BT11.BackColor = Color.Green;
BT12.Enabled = false;
BT12.BackColor = Color.Transparent;
}
Gesehen hat, das du zugriffe auf eben diese UI-Steuerlemente wie hier einer RichtTextBox machst und füllst. Du darfst zwar zugriffe machen von einem Thread aus auf ein Steuerlement wie eben die RichtTExtBox aber nicht so. Weil dein GDI das Grafische Daten Interface mit dem jedes Formular innerhalb von Application.Run arbeitet ebenso einen Thread besitzt (man sagt manchmal auch GUI Thread oder GDI Thread dazu). Wenn nun zwei Threads auf ein und das selbe Steuerelement (Objekte aller Art auch strings int usw.) zugreifen. Kommt es zu fehlern, die häufigste Frage dazu im Forum ist: " Warum blokiert meine GUI".
Was er ebenso richtig gesehen hatist das es nichts taugt, aus einen einfachen Grund:
Du nutzt die Threads um Standardwerte zu setzen, damit erhöhst du keinesfalls die Performance im Gegenteil du verschlechterst sie. Dafür ist SusbendLayout und ResumeLayout vom ganz normalen Visual Studio Designer beim reinziehen von Steuerelementen auf den Designer sowieso schon zuständig.
Nutze Threads um etwas Sinnvolles abzuarbeiten wie z.B eine große Datenverarbeitung.
Das setzen der ThreadPrioität bedeutet nicht das die Threads schneller laufen weil sie plötzlich mehr Speicher bekommen. Sondern nur die Reihenfolge in der diese die CPU belasten bis zur Abarbeitung. Bei Prioität High kann es daher passieren das die CPU alle anderen Prozesse nicht mehr genug versorgt (naja dummer vergleich) und somit dein PC einfriert (bei Spielen spricht man von Frezzes).
Ich habs versucht in einfache Worte zu packen was dahinter steckt ist in wirklichkeit viel viel mehr.
Na ja, dann reden wir nicht ganz aneinander Vorbei:
Jetzt weis ich zumindest das einen Hook in ein Fremdprogramm machen möchtest.
In C# darf und kann man keine Prozessübergreifenden Hooks machen, man darf aber Hooks in seinen eigenen Prozess machen.
Und deshalb hatte ich dir drei Lösungsansätze gepostet einen für Fremdprogramme die in deinen programm laufen und einen für Fremdprogramme die nicht in deinen Programm laufen.
Aber ich schreibe dir einfach zwei Lösungen:
1.) In C# mit C++ für einen Prozessübergreifenden hook.
2.) Nur C# abfangen eines Dialogs und ihn zu zerstören.
Allerdings gehe ich das erst am Wochenende an, da ich hierfür ne Flasche Rum brauche 🙂
[Lies das nur wenn es selbst machen möchtest]
Wenn es aber schon selber angehen möchtest dann schau dir nochmal den ersten Post von mir hier in dieser Rubrik an. Ich hatte dir zwar C# Code gepostet aber auch geschrieben was unter C++ in verbindung mit C# Code für einen Hook zu beachten ist.
Grob, mach ein C++ MFC Projekt erstell eine DLL mach eine Headerdatei mit _GC vor der Klasseninitalisierung. Mach dann eine Cpp Datei die nur unmanged Code versteht udn binde die Headerdatei ein, bilde dann den C# Code nur eben auf C++ basis ein und den rest wie gehabt. Dll in C# Projekt einbinden alle 5-20 Sekunden die DLL neu initalisieren und erneut dne Hook aufrufen.
Okay, normal sollte man dir auf die Finger klopfen 🙂
Aber hier mal der Ansatz einer Singleton Klasse.
public class Class1
{
// wir machen die klasse static
private static Class1 myClass;
// hier die instanz der Klasse
public static Class1 Instance
{
get
{
if (myClass == null)
myClass = new Class1();
return myClass;
}
}
/// <summary>
/// Wir killen den public Konstruktor
/// </summary>
private Class1()
{
}
public string DoMyWäsche
{
get { return "Klar mache ich"; }
}
public void Blöcken()
{
System.Windows.Forms.MessageBox.Show("Blööök");
}
}
public class Class2
{
public Class2()
{
string a = Class1.Instance.DoMyWäsche;
Class1.Instance.Blöcken();
}
}
Hrm, okay irgendwie muss ich zu verwirrend geschrieben haben - passiert mir ständig 🙂
Das was ich zuletzt gepostet habe ist der Ansatz für 'nur' C# Code wenn der Fehlermeldungs-Dialog auch innerhalb deiner Anwendung erscheint. Also wenn diese Fremdanwendung in deinem C# Quellcode aufrufst und dieser innerhalb deiner .Exe Datei läuft.
Wenn dieser Fehlermeldungs-Dialog nicht innerhalb deiner eigenen .Exe (Prozess) angezeigt wird. Du also im Windowstaskmanager feststellst das dieser in einer anderen ProzessID (PID) erscheint so musst das mit C++ angehen.
Da ich aber stark vermute das du einfach ne Referenz in dein C# Projekt einbindest und dort dann dieser Fehlerdialog hochkommt, so brauchst du nur C# Code. Du erstellst nun ein neues Projekt als Klassenbibiothek, das Projekt nennst du Hook.dll. Dann erstellst du in diesen neuen Projekt eine Klasse, dort kopierst du den von mir oben geposteten Code rein. Dann machst du in deinen Hauptptojekt einfach eine Referenz auf das Projekt Hook.dll. Dannach instanzierst du an einer entsprechenden Stelle die Hook.dll bzw die darin befindliche Klasse und instanzierst zuerst den Eventhandler und rufst dannach die Methode Hook auf.
Wenn nen Event rüber gereicht bekommst ist alles in Ordnung, wenn nicht musst das was ich oben geschrieben habe noch befolgen.
Und wie gesagt, vergiss nicht den Eventhandler noch in der Funktion die ich oben gepostet habe einzubauen.
Ich hoffe habs nun besser hinbekommen 😮)
Hrm, okay da war ich wohl nicht mehr ganz nüchtern, man sollte ja niemals mehr globale variablen benutzen als nötig. Du brauchst kein Dictionary, mach das Ding weg 🙂
Hier bezogen auf dein Testprojekt:
int i = 0;
private void button1_Click(object sender, EventArgs e)
{
A a = new A();
a.X= i;
a.Y = "Linsengericht";
this.aBindingSource.Add(a);
A test = this.Find(i);
i++;
}
private A Find(int _nKey)
{
A[] array = new A[this.aBindingSource.List.Count];
this.aBindingSource.List.CopyTo(array, 0);
return Array.Find<A>(array, delegate(A _a) { return _a.X == _nKey; });
}
Ich glaube würde sogar noch eine Klasse machen von der ich dann BindingSource ableite und würde dort dann die Methode Find das gepostete Find erweitern 🙂
Schöner wäre das unter .Net 3.0 da man hier ähm erweitern kann mir fällt grade das Stichwort dazu nicht ein... Aber egal .-)
Wenn das alles nicht klappt, ich habe zu Hause noch ne Dialog Native geschrieben da müsstest dich nur etwas gedulden, vorallem die ist verdammt schwer zu verstehen. Ich würde dir dieses dann geben, evtl. als Dateianhang dann reinposten (wenn es nicht zu groß ist und wenn dann nur gekürzt).
Ja nein, die DLL beinhaltet auch ne Klasse diese Klasse beinhaltet den von mir geposteten Code. Der globale Adressenbereich dient nicht nur zum rumfummeln sondern auch um die darin 'enthaltenen' Funktionen zur verfügung zu stellen.
Und diese sollten dann folgende sein:
public bool Hook()
public bool Unhook()
public delegate void GlobalWndProcHandler(ref System.Windows.Forms.Message);
public event GlobalWndProcHandler GlobalWndProcEvent;
Die Klasse dazu sieht ca. so aus, ich bin nur zu faul alles reinzumachen, das mit dem Event bekommst denke ich locker hin hab dir die stelle makiert:
public class Hook
{
[DllImport("user32.dll")]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadID);
[DllImport("user32.dll")]
private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
private HookProc myCallbackDelegate = null;
[DllImport("kernel32")]
public extern static int LoadLibrary(string librayName);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool FreeLibrary(IntPtr hModule);
public delegate void GlobalWndProcHandler(ref System.Windows.Forms.Message);
public event GlobalWndProcHandler GlobalWndProcEvent;
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
private IntPtr hHook = IntPtr.Zero;
private int hInst = 0;
public bool UnHook()
{
UnhookWindowsHookEx(hHook);
FreeLibrary(hInst);
}
public bool Hook()
{
// initialize our delegate
this.myCallbackDelegate = new HookProc(this.MyCallbackFunction);
hInst = LoadLibrary(Application.StartupPath + @"Hook.dll");
// setup a global wndproc hook
hHook = SetWindowsHookEx(HookType.WH_CALLWNDPROC, this.myCallbackDelegate, hInst, AppDomain.GetCurrentThreadId());
return (hHook == IntPtr.Zero) ? false : true;
}
private int MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
{
// hier dann das event abfeuern
//return the value returned by CallNextHookEx
hHook = CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
}
Ich hab das Zeug aus dem Kopf geschrieben bzw aus PInvoke eben zusammenkopiert Tippfehler können drinnen vorkommen. Was schauen musst ob AppDomain.GetCurrentThreadId() wirklich drinnen sein muss, ich denke eher kannst das durch '0' ersetzen. Bin mir grad selber nicht mehr so sicher.
Ich glaube da hast du Recht norman_timo, vielleicht liegt es ja nur an der extrem hohen Nachfrage 🙂
Okay, mach das DataBinding mal weg und nutze nur DataGridView.DataSource = dictionary;
Dann wirst die Einträge bekommen.
Das expleziete Binden von Daten bedeutet nur das den Current Value (Daher auch der CurrencyManger) angezeigt bekommst. Durch eine Source die DataSource umschalten kann wie ne Combobox könntest weiter Browsen oder eben mit dem CurrencyManger.
Aber eine DataGridView ist ja dafür da alle Sourcen anzuzeigen.
Wenn nun nachdem DataGridView.DataSource = dictionary gemacht hast mal ne Label auf dein GDI draufhaust und das mit DataBinding an die Text Eigenschaft und eben das Value von dictionarybindest dann wirst feststellen - wenn auf die DataGridView rumklickst das sich der Text des Labels ändert.
Ich denke wenn das mal ausprobierst wird es dir klarer werdne wie das ganze funktioniert 🙂
Ähm ich versuche es mal zu erklären:
Für einen 'globalen' hook den du hier benörigst brauchen wir unbedingt die eine explizite Verknüpfung mit einer DLL um den Adressbereich des aufrufenden Prozesses zuordnen zu können. Das Handle das wir bekommen, kann dann eine explezite Verknüpfung mit anderen Funktionen zur verfügung stellen.
Also alles worum es uns geht ist eben dieser globale Adressenbereich in den wir rumfummeln wollen, der zugleich für alle Threads im Prozess zur verfügung steht.
Du kannst die umkehrprobe machen, aber vergewissere dich das alles Speicherst und auch deine Programme die nebenher laufen gespeichert sind. Wir kritzeln nun mal in nen Speicherbereich der uns zwar gehört den wir aber durch unsere Anwendung belegen. Mach einfach aus dem LoadLibrary ein GetWindowLong(this.Handle, -6) und du wirst erstmal was äusserst nettes erleben 😉
Die Anwendung sollte dauerfehler hervorrufen vorallem da sie ja dauernd die Paint WindowsNachricht erhällt dadurch sollte der WindowsExplorer abschmieren wie alle anderen geöffneten Programme 🙂
Wenn LoadLibrary dir nichts zurückliefert kannst das Projekt auch COM Regestrieren und eben über LoadLibraryEx reinladen.
Was ich nur zu faul war reinzuposten, sind die die Win API Funktionen: FreeLibrary(); und UnHook();. Und beim beenden deines Programms beherzigen solltest. Da der Adressenbereich sonst immer noch benutzt wird. Ich wies leider nicht ob dann das Programm überhaupt schließt, wenn es draussen lässt. Oder beim nächsten mal kompilieren dann meint, dass die DLL nicht Laden kannst weil sie schon benutzt wird.
Wie gesagt unter C++ zu C# ist das nen hauch komplizierter. Da kann ich es dir kaum erklären, warum und weshalb.
Ich fürchte da gibt es leider keine Lösung generische Objekte können nicht deseralisiert werden. Ich hatte mir da mal einen abgebrochen. Was aber geht sind object Arrays.
Das heisst übertrage einfach das generische List<object> mit List<object>.ToArray(). Wichtig ist nur das beim deseralisieren wiederum eine Identische Klasse verwendest ohne dessen Property zwar gleich wie die zu Serialisierende Klasse benennst aber eben statt das diese einen generisches object enthällt eben ein Array beinhaltet. Also Bsp
Zu serialisieren
//bla bla bla...
public List<string> myList
{
get{return this.m_myList; }
}
wird deserialisiert zu
//bla bla bla...
public string[] myList
{
get{return this.m_myList; }
set{this.m_myList = value; }
}
Beim deseralisieren geht es dir ja soweiso nur um die inhallte, die kannst dann durch Methoden oder neue Properties sowieso wieder zu generischen typen umwandeln.
Was du sonst noch machen kannst ist dein eigenes Protokoll zu schreiben statt soap eben eigene Formatvorlagen verwenden die du dann verwendest. okay, das geht nur mit naja mittelmäßigen Aufwand über reflections aber ob es sich lohnt die Zeit zu investieren musst du wissen 🙂
Hrm, da habt ihr beide natürlich recht.
Das Abschlusszeugniss der jeweiligen Ausbildung war schon wichtig bzw. dann das Arbeitszeugniss.
Hrm, du hast eine neue Klasse erstellt und vom DataGridView abgeleitet, wenn nein versuche das mal und nutze das protected override void OnMouseUp(MouseEventArgs e) Ereigniss.
Wenn es imme rnoch zu Problemen kommt mach ne Abfrage ob die Location des MouseUps dem der Zelle entspricht, new Region(Cell.Rectangle).IsVisible(e.Location) == false dann base.OnMouseUp(e); erlauben ansonsten nicht.
Wenn das nicht klappt gibt es noch mehr möglichkeiten 🙂
Ich schreibe es nur mal so grob hin, denke das meiste wird dir dann schon klar werden:
Du hast ja deine Threadmethode: ThreadListGroups in dieser erweiterst du jeweils das ListView.
private delegate void FillHandler(Group gp);
// das hier immer noch als thread starten
public void ThreadListGroups(object Connection)
{
.....
Groups = nConnection.GetGroupList();
foreach(Group _gp in Groups)
{
this.Fill(_gp);
}
}
private void Fill(Group gp)
{
if (this.InvokeRequired)
{
this.Invoke(new FillHandler(Fill), gp);
return;
}
else
{
this.lvGroups.Items.Add() // usw..
}
}
Das trägt die Sache flott ein und brauchst dir keine Gedanken zu machen das irgendetwas blockiert.
Hrm, grübel
Den Abstand zwischen DeviceContext und Control.Size zu ermitteln bringt dir ja leider nichts, da ihn nicht raus bekommst. Der ist einfach fest vorhanden.
Jetzt kannst höchstens von Label ableiten und die OnPaint Methode überschreiben und schauen ob evtl ne protected Methode mit DrawString oder ähnliches findest die dann um das base.Draw ausklammerst. Oder du machst ein UserControl das dann komplett selbst neu aufbaust umd das Label zu ersetzen. Auch mit der Win32 API kannst in den DeviceContext höchstens reinmalen. Aber positionierungen ersetzen phuu, nie probiert.
Evtl gibts da ja etwas um den DeviceContext umzupositionieren, müsstest aber suchen (GetHDC usw.).
Hrm, ich schaue mir noch mal deinen Code an was genau du eigentlich adden wolltest vielleicht findet sich ja eine andere Lösung dazu 🙂
HRn, okay mit fällt auch nichts anderes zu ein als über Threads zu Adden. Schreibe dir gleich dne Code dazu rein.
Hrm, ich sitze leider nun zu Hause.
Also schau mal ob die ListView ein DataSource object besitzt. Über DataBinding geht es dann recht flott mal eben mehrere Tausend Objekte anzuzeigen. Sogar schneller als mit der anderen Methode. Aber ich denke bevor das angehst, machs erstmal mit Add ganz normal auch wenn es lange geht um zu sehen ob das funktioniert.
Ich glaube ich habs gleich bzw. vermute was 😉
Also den genauen Fehlergrund muss dir jemand anderes erklären evtl. schaut herbivore nochmal rein:
Du füllst die Items im virtuellen Modus und bearbeitest sie. Soweit ist das auch normal und okay.
Jetzt kommt der Effekt. Wenn ein Testprojekt erstellst und ebenso die Items erweiterst mit +1, nur eben über einen Timer hinweg der einen Thread startet der wiederum eine Methode aufruft die das übernimmt aber nur wenn InvokeRequired = false. So wird zwar das Event angesteuert, aber es werden alle sich im Thread befindlichen "globalen" variabeln = null. Somit kommt statt der erwarteten NullException .Net Fehlermeldung eine COM Fehlermeldung, da dein COM object = Null wird. Ich konnte den Effekt provozieren indem ich an der Bildlaufleiste etwas hin und her gezupft habe 🙂
Okay soweit so blöd..
Wie Lösen: Mach es dir und mir einfach, füll das Control nicht über den virtuellen Modus sondern über ListView.Items.Add(bla). Dann passiert der Effekt hoffentlich nicht mehr.
//this.listView1.VirtualMode = true; // weg damit
// stattdessen dann
this.lvGroups.Items.Add(<dein item>);
Ach glaube ist wieder so ne Control spezifische scheiße die man erstmal wissen muss.
Dadurch das ich nur Basisprogrammierung gemacht habe oder so knuffelige Controls von Fremdfirmen benutze kenne ich auch nicht alle Controls hin und auswenig. Was mich nur verunsicht ist die Fehlermeldung die du bekommst.
Hrm, ich rauche flux eine und schaue mir mal das ListView genau an, muss zugeben arbeite recht wenig mit den .Net Standard Controls. Nach nen kleinen TestProgramm weis ich evtl mehr 🙂
Aber zur Beruhigung, ich glaube nicht das es wegen des Threads passiert oder das es an dem COM Object liegt.
Hrm mach mal bei lvGroups.VirtualListSize = Group.Count zu lvGroups.VirtualListSize = 1; Und lass den rest Ausgeklammert in lvGroups_RetrieveVirtualItem.
Hoffen wir mal das dann kein Fehler kommt ansonsten schaue ich mir nochmal das ganze Post von dir durch, irgendwas ist es was ich nicht gleich sehe.
Na ja, zumindest hast da mal ein Problem was sich nicht so leicht finden lässt 🙂
Okay, nun poste mal den abschnitt wo du lvGroups Initalisierst.
Wir wissen nun ja das es damit zusammenhängt.
Machs einfachs einfach mal und wenn es dann dennoch abstürzt schauen wir weiter 🙂
Hier kann man nur Schritt für Schritt den Fehler suchen.
Ich will nur auschließen das wir uns nicht um dne Code der sich in der Methode befindet kümmern müssen. Sondern uns nur das object lvGroups nachher ansehen🙂
Das Problem ist, das es sich nicht um ein Threading Problem handelt. Das ist richtig gelöst worden in der Methode ShowGroupList. Mir gehts nur darum das in der Methode lvGroups_RetrieveVirtualItem einfach mal alles was sich darin befindet ausklammerst 🙂
Und wenn kein Fehler ausgelöst wird nach für nach wieder rein machst bis der Fehler eben erscheint.
Weil ich glaube kaum das das hier passt:
nGroupname = ((Newsgroup)Groups[index]);
Aber das muss man halt erst testen, kann mich ja auch irren.
Hrm, hast den einfach mal lvGroups.VirtualListSize = Groups.Count; ausgeklammert um zu sehen ob der Fehler noch auftritt. Wenn ja klammer einfach mal alle kritischen stellen in private void lvGroups_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) aus und wenn dann nen Fehler auftritt schaun wir mal weiter wenn nicht nach für nach wieder reinmachen bis die Stelle genau identifiziert hast.
Ich glaube hier bleibt dir nur Try&Error übrig bis es halt genau gefunden hast.
Hrm, wann wird den lvGroups_RetrieveVirtualItem aufgerufen und wo bindest das Event an die Methode?
Hallo Andreas.May,
ich habe es nicht ausprobiert. Die Doku sagt:
Der Standardwert ist ApartmentState.Unknown.
herbivore
Huch, okay wieder was gelernt 🙂
Also ich glaube der Fehler befindet sich an diesen Punkt:
TGetGroupList.Start(nConnection);
Du übergibst das COM object "nConnection" an den Thread und Rufst dort das COM object ab. Normal kein Thema aber dabei etwas mit dem nConnection und zwar innerhalb davon werden Methoden ausgeführt die auf objecte zugreifen die nicht mehr Synchon verarbeitet werden. Soo, was tun?
Ich denke das einfachste wäre um zumindest den Fehelr zu finden:
Gibt die wichtigen daten von nConnection in der FunktionTGetGroupList.Start(object) mit und instanzier in der Methode.
public void ThreadListGroups(object Connection)
{
NntpConnection newConnection = new NntpConnection(Connection);
Groups = newConnection .GetGroupList();
ShowGroupList(<die Dinge die zum anzeigen brauchst übergeben>);
}
Das NntpConnection neu. Damit ist diese Instanz nur innerhalb des Threads bzw. der Methode am leben. Wenn dann fehler auftreten kann man schon etwas weiter suchen, ansonsten wenn keine mehr auftauchen denke ich weist was zu tun ist 🙂
Also das Arbeitszeugnis ist natürlich wichtig.
Also meinen ersten Rechner bekam ich von meinem Onkel mit 8 Jahren.
Das war ein alter Commodore 64 (C64) 8-Bit Grafik und mit 64 KB Arbeitsspeicher. Meistens spielte ich diese Wintergames wobei ich das erste mal mit harter Körperlicher Arbeit in kontakt kam, die schwilen an den Fingern von dem Knüppeljoystik hatte ich noch Wochen danach 😁
Und als ich nach ner Buchvorlage irgendwan mal mehrere Tausend komisch aussehender Zeichenketten eingegeben hatte habe ich das erste mal nen Screensaver programmiert und war total stolz drauf das es lief (so nen schwebender Balong). Und ich dachte, jawohl jetzt bist nen "echter" Programmierer und hab natürlich damit vor meiner großen Schwester angegeben. Bis sie rausfand das ich es nur nach nen Buch abgetippt hatte.
Natürlich hat sie mich danach erstmal mit ihren Freundinnen verprügelt 😜
Dumme Frage dazu ist der nicht immer STA Standardmäßig und bei COM löst meistens nur Multithread also MTA nen Fehler aus 🤔
Hrm, du machst auch das using drum herum bei der Connection so das sie auch disposed wird? Ich hab ausversehen OleDbConnection geschrieben statt SQLConnection.
Ansonsten hrm, vielleicht postest doch etwas mehr rein, normal reicht SqlConnection.Close echt aus.
Ist euch schon einmal aufgefallen das bei Bewerbungen oder Einstellungskriterium niemals Zeugnisse zur Hand genommen werden?
Huch da hatten wir beide falsch reinkopiert. Das args am Ende muss einfach weg + das Komma
private void btn_Start_Click(object sender, System.EventArgs e)
{
System.Windows.Forms.MessageBox.Show(Test);
}
Aber denk dran was ich davor schon schrieb mit derm member string Test. Entweder muss diese static sein und dein Main muss sich in der Klasse Form1 befinden. Oder du übergibst es an den Konstruktor der Form1 oder halt nen Property oder nen public Member 🙂
Hrm,
ich bin mir nicht sicher ob es klug wäre dir jetzt dazu die Lösung zu geben da der Ansatz nicht so ganz richtig ist.
Du willst ja nur aneinander gebebte Formulare haben wie bei Winamp.
Im Prinzip solltest daher ersteinmal eine gemeinsamme Singleton Klasse haben über die du alle Aktionen schickst die sich idealerweise selbstausführen (CommandPattern Command oder Action als Suchstichwörter im Forum). Zum anderen ist die überlegung ob nicht lieber sowas wie ein Splitten von eines Formulars anstreben solltest. Da es sehr schwer wird ohne Windows API Kentnisse den Dockbefehl GUI Threadübergreifend hin zu bekommen.
Wenn du es wirklich dennoch möchtest, dann
if (FormA.WindowState != FormWindowState.Normal)
{
FormB.Show();
FormC.Show();
}
Das müsstest dann bei jeden anderen machen.
Für das ALT TAP Problem musst du die WindowsAPI Funktion Bring To Front benutzen also das hier
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
protected override void OnActivated(EventArgs e)
{
SetForegroundWindow(this.Handle);
base.OnActivated(e);
}
Aber überdenke nochmals dein Design. Du kannst auch gerne Tricksen indem du in ein Formular mehrere Formulare reinbebst und das dann quasi unsichtbar machst. Oder nimmst nen MDI Control das dann unsichtbar machst.
/ps
Ich bin selbst ein Fachinformatiker 🙂