Laden...

Forenbeiträge von Gwar Ingesamt 46 Beiträge

15.05.2013 - 10:40 Uhr

Besten Dank für den Tipp. Tatsächlich war dies bereits so implementiert. Anbei der Quellcode des Lesethreads:


public void ReadTcpIpMessages(TcpClient tcpClient)
{
    NetworkStream clientStream = tcpClient.GetStream();
    
    // this.thread is an instance of a class which holds the following properties
    // thread.
    //   Workerthread -> A thread
    //   Signal       -> An autoreset event
    //   Done         -> A threadsafe flag indicating whether the thread
    //                   done or not
    this.thread.WorkerThread.Start(clientStream);
    Encoding encoder = Encoding.Unicode;

    this.thread.Done = false;
    while (!this.thread.Done)
    {
        var bytes = new byte[64]; // We also tried other buffer sizes - made no difference
        try
        {
            // this.clientStreamLock is of type object.
            // Initially we were assuming, this is unneccessary since
            // TcpClient should be thread safe.
            // In order to rule this out as a source of error, we
            // introduced this lock
            lock (this.clientStreamLock)
            {
                bool dataAvailable = clientStream.DataAvailable;
                // Prevent blocking by checking for availability of data first
                if (dataAvailable)
                {
                    int bytesRec = clientStream.Read(bytes, 0, bytes.Length); // Would block if no data is available
                    
                    if (bytesRec == 0)
                    {
                        // Received nothing, connection may be closed.
                        this.thread.Done = true;
                    }
                    else
                    {
                        // Write the data to the data buffer and signal the message handling thread to have a look at it
                        this.dataManager.WriteToBuffer(bytes, bytesRec);
                        this.thread.Signal.Set();
                    }
                }
                else
                {
                    Thread.Sleep(10);
                }
            }

            // Look for messages to SEND to this connected client
            this.SendOutMessages(clientStream, encoder);
        }
        catch (System.IO.IOException e)
        {
            Log.Debug(e.Message);
            Log.Debug("Closing connection.");
            this.thread.Done = true;
        }
        catch (ArgumentNullException e)
        {
            Log.Error(e);
            this.thread.Done = true;
            throw;
        }
        catch (ArgumentOutOfRangeException e)
        {
            Log.Error(e);
            this.thread.Done = true;
            throw;
        }
        catch (ObjectDisposedException e)
        {
            Log.Error(e);
            this.thread.Done = true;
            throw;
        }
    }

    // Waiting for child thread to finish
    this.thread.WorkerThread.Join();
    // Child thread finished. Sending remaining out messages
    this.SendOutMessages(clientStream, encoder);
    
    tcpClient.Client.Shutdown(SocketShutdown.Both);
    tcpClient.Close();
}

und der Quellcode des Behandlerthreads:

/// <summary>
///   Handles the received bytes in a separate thread.
/// </summary>
/// <param name = "stream">The stream.</param>
private void HandleReceivedBytes(object stream)
{
    NetworkStream clientStream = (NetworkStream)stream;
    const int WAIT_TIME = 2000;

    // this.ReceiveBytesDone is a threadsafe flag which allows
    // to shut down threads in a controlled way
    while (!this.ReceiveBytesDone)
    {
        // Wait until there is data in the Buffer.
        // the AutoReset Event will only be signaled
        // if bytes have been received. Otherwise a timeout occured.
        bool bytesReceived = this.thread.Signal.WaitOne(WAIT_TIME);

        if (bytesReceived)
        {
            // Data available in Buffer.
            // Now read all available data (synchronously)
            this.ReceiveBytesDone = this.HandleMessages(clientStream); 
        }
        else
        {
            // this.thread.Done indicates that the connection
            // has been closed
            // If that is the case, we can leave this thread.
            // But ONLY in that case because otherwise
            // open and valid connections will also be broken.
            if (this.thread.Done)
            {
                this.ReceiveBytesDone = true;
                break;
            }
        }
    }
}
14.05.2013 - 15:39 Uhr

Im Testszenario wäre es übrigens kein Problem, mit dem Schließen des Clients zu warten, bis der Server alle Nachrichten erhalten hat, da wir die Logik des Servers mittels Dependency Injection beeinflussen und uns somit über den Erhalt der Nachrichten informieren lassen können.

Tatsächlich führt dies dazu, dass alle Nachrichten ankommen. Dies ist allerdings logisch, da wir mit dem schließen des TcpClients länger warten. Wenn wir uns sicher wären, dass bspw. Ressourcen-Bedingungen des Betriebssystems verantwortlich für das beobachtete Verhalten sind, wäre dies auch eine akzeptable Lösung. Noch sind wir uns jedoch nicht sicher, das Problem verstanden zu haben.

14.05.2013 - 15:00 Uhr

Hallo Weismat,

vielen Dank für Deine Antwort.
Eine Log-Out Message ist mit Sicherheit sinnvoll und auch geplant (soweit sind wir mit der Implementierung aber noch nicht).

Unser Problem ist jedoch, dass der Client "denkt", er hätte NUM_MESSAGES (== 1000) verschickt, tatsächlich werden aber weniger als NUM_MESSAGES (nicht deterministisch. Zwischen 27 und 950) verschickt. Eine Log-Out Message käme ohne Weiteres also ebenfalls nicht an. Ausnahme: Wir warten mit dem Schließen des Clients, bis er ein Acknowledge vom Server erhalten hat. Meintest Du dies vielleicht?

Habt ihr auf dem Server das Lesen in einem eigenen Thread, so dass es dort keine Verzoegerungen geben kann, weil die TCP Queue voll ist?

Ja. Direkt nach TcpListener.AcceptTcpClient() wird ein Thread geöffnet, der die weitere Behandlung übernimmt.

Außerhalb der Testumgebung werden die Nachrichten übrigens bislang zuverlässig übertragen (100% Empfang bei 1000 Nachrichten welche in einer Schleife verschickt werden). Wir sind uns aber nicht sicher, ob dies ggf. durch zeitliche Abhängigkeiten verursacht wird. Im Unterschied zur Testumgebung "lebt" der TcpClient nämlich länger - im Test wird er nach dem Durchlaufen von SendMessages() zerstört und mit großer Wahrscheinlichkeit im Anschluss vom GarbageCollector entfernt. Wenn dies jedoch ursächlich für das Verhalten wäre, müsste ein Thread.Sleep() nach dem Client.Close() aber das Problem "beheben". Dies ist jedoch leider nicht der Fall.

Viele Grüße

13.05.2013 - 17:00 Uhr

Liebes Forum,

trotz intensiver Recherche komme ich einem TCP/IP Problem nicht auf die Schliche. Das Problem scheint verwandt mit
[1] NetworkStream doesn't always send data unless I Thread.Sleep() before closing the NetworkStream. What am I doing wrong? und
[2] TcpClient.Close() works only with Thread.Sleep()
zu sein. Die dort vorgeschlagenen Lösungen helfen jedoch in meinem Fall nicht.

Wir schreiben eine Client-Server Anwendung. Um deren Robustheit zu beweisen kommt eine mehrstufige Teststrategie zum Einsatz. Auf unterster Ebene stehen die Unit-Tests. Eine Ebene darüber stehen Integrationstests, welche für die Komposition der individuellen Klassen verwendet werden.
In einem der Integrationstests wird die Kommunikation zwischen Client und Server getestet. Als Client kommt eine TcpClient-Instanz zum Einsatz. Interessanterweise scheint diese nicht alle Daten zu versenden (Wir konnten diese Vermutung nicht mit Wireshark und/oder RawCap.exe verifizieren, da dort merkwürdigerweise keine für uns relevanten Daten mitgeloggt werden – obwohl der Server Daten empfängt. Wir haben den Test sowohl mit dem Loopback Interface als auch mit der extern Sichtbare IP Adresse laufen lassen).

Das folgende Code-Beispiel zeigt die Sende-Methode des Integrationstests:


private void SendMessages()
{
        using (TcpClient client = new TcpClient())
       {
            LingerOption lo = new LingerOption(true, 10000);
            client.LingerState = lo;
            client.NoDelay = true;
            IPEndPoint serverEndPoint = new IPEndPoint(
               IPAddress.Parse("127.0.0.1"),
               50981);

            client.Connect(serverEndPoint);
            NetworkStream clientStream = client.GetStream();
            Encoding encoder = Encoding.Unicode;
            List<byte> buffer = new List<byte>();

            // add some bytes

            clientStream.Write(buffer.ToArray(), 0, buffer.Count);            
            clientStream.Flush();

            for (int i = 0; i < NUM_MESSAGES; ++i)
            {
                buffer.Clear();

		   // add some bytes

                clientStream.Write(buffer.ToArray(), 0, buffer.Count);
                clientStream.Flush();
             }
             
             // Thread.Sleep(1);
             client.Client.Shutdown(SocketShutdown.Send);
             clientStream.Close();
             client.Close();
             Log.Debug("Closing test thread.");
        }
}

Der Server scheint aufzuhören, Daten vom Netzwerkstream zu lesen, sobald der Testclient bei „client.Close()“ ankommt. Das Beispiel enthält schon einige der im Internet aufgetauchten „Problemlösungen“ (Verwendung von LingerState und NoDelay bzw. manuelles schließen des Sockets). Diese bewirken allesamt keine Verbesserung.
Interessanterweise versendet der Test alle Daten, wenn die Codezeile „Thread.Sleep(1)“ einkommentiert wird. Wenn statt „clientStream.Write(…)“ jeweils der folgende Code eingebaut wird, funktioniert der Test ebenfalls:


IAsyncResult asyncResult = clientStream.BeginWrite(buffer.ToArray(), 0, buffer.Count, null, null);
asyncResult.AsyncWaitHandle.WaitOne();

Beide Workarounds erscheinen uns jedoch fragile und unsaubere „Lösungen“ zu sein.

Daher unsere Frage: Kann uns jemand einen Tipp geben, warum der Client trotz „NoDelay“ und LingerOption == true die Daten nicht vollständig auf den Stream schreibt?

Beste Grüße

Ergänzung:
Uns ist bewusst, dass der Server die Daten vom Stream in einer Schleife lesen muss, da die Daten fragmentiert ankommen. Das versendete Protokoll verwendet auch Start/Endmarkierungen, um Beginn und Ende einer Nachricht identifizieren zu können.

18.01.2011 - 18:44 Uhr

Hallo zusammen,

meine Firma möchte sich zum signieren von C# Desktop Assemblies ein Authenticode Zertifikat kaufen. Ich scheitere jedoch beim Bestellprozess - und meine Supportanfragen bei den entsprechenden Firmen blieben bislang unbeantwortet. Ich finde auch keine Antworten auf meine Fragen im Netz 😦

Daher meine Fragen an die Community:
1.) Welchen Cryptographic Service Provider muss ich wählen?
Nach meinem Verständnis reicht die Default Einstellung, die durch den Zertifikatprovider vorausgewählt wird. Doch wann benötigt man einen anderen?
2.) Warum muss ich einen Pfad zu dem Zertifikat angeben?
Bedeutet dies, dass das Zertifikat nur von einem bestimmten Pfad aus nutzbar ist? Bislang bin ich davon ausgegangen, dass das Zertifikat auf einem NAS gespeichert wird, worauf nur der Buildserver und 1 bis 2 Entwickler Zugriff haben...

Vielen Dank und beste Grüße
Gwar

12.07.2010 - 14:52 Uhr

Hallo,

nun kenne ich natürlich die Schnittstelle nicht. Davon ausgehend, dass Du eine objektorientierte, xml-unabhängige Schnittstelle hast, solltest Du mMn das XML aufschlüsseln. Der andere Ansatz verletzt Kapselungsprinzipien (Du möchtest XML direkt bearbeiten, weil Du "weißt", dass WCF so arbeitet - aber was, wenn sich in Zukunft etwas ändert? Und sei es nur die Dokumentenstruktur...). Außerdem erzwingt der andere Ansatz eine unnötige Schnittstellenänderung, die dann auch vom FileWriter und dem HTTP-Handler unterstützt werden müsste. Abgesehen davon würde die Schnittstelle dann ja auch wissen, wie eine ihrer Implementierungen arbeitet und nur deswegen eine Methode anbieten. Auch das ist unsauber.

Daher würde ich - unter genannten Voraussetzungen - das Parsen des XML Dokuments bevorzugen - auch, wenn es mehr Arbeit ist.

Grüße

09.07.2010 - 09:38 Uhr

Hallo herbivore,

deine Synchronisierung kann prinzipiell nicht funktionieren

Woran liegt das? An den Fallstricken, die Du auch unter
Programmier-Spiel erwähnst?

Grüße

08.07.2010 - 09:21 Uhr

Wenn ich das richtig in Erinnerung habe ziehen diese Klassen die Versionen der Assemblies mit ein. Falls Du also noch an dem Code schraubst kann es Versions-Probleme geben. Es gibt eine Upgrade() Methode, die genau dies verhindern soll (indem sie sich die Einstellungen der letzten Version holt).

07.07.2010 - 13:12 Uhr

Hallo xingelxangel,

Die Markierung nur mit [Serializable] funktioniert nicht.

Was ist denn der Grund dafür?
Grüße

07.07.2010 - 12:59 Uhr

Wie kommt es überhaupt, dass Du auf ein unbekanntes Setting zugreifen willst?
Das klingt für mich, als würdest Du das Symptom eines gänzlich anderen Fehlers bekämpfen wollen...

Grüße

07.07.2010 - 12:57 Uhr

Hallo herbivore,

wie mir jetzt klar geworden ist, nicht genau erkennen, ob es darum geht, vorhandenes Dispose aufzurufen oder darum geht, in (allen) Klassen IDisposable zu implementieren. Ich habe letzteres verstanden.

... womit das "Missverständnis" geklärt ist, denn ich habe ersteres verstanden 😃

Grüße
Gwar

07.07.2010 - 12:46 Uhr

Das müsste doch theoretisch auch gehen, indem die (selbst entwickelten?) Unterklassen als [Serializable] markiert werden, denke ich.

Andernfalls könnte man die Implementierung von IXmlSerializable fordern.

07.07.2010 - 11:47 Uhr

Hallo herbivore,

damit hat er vollkommen Recht! Dispose braucht man im Prinzip nur für nicht-verwaltete Ressourcen;

... weshalb er wiederum Unrecht hat. Wenn ich eine Komponente nicht selbst entwickelt habe und diese Komponente IDisposable implementiert sollte ich - der Sicherheit halber - auch davon ausgehen, dass nichtverwaltete Ressourcen verwendet werden. Ergo sollte Dispose() aufgerufen werden, wenn es vorhanden ist.

Grüße
Gwar

05.07.2010 - 11:15 Uhr

Zusätzlich hilfreich, um Fehler "im Feld" zu rekonstruieren ist effizientes Logging.
In der .net-Pro gibt es ein paar gute Artikel zu best practices (z.B. jeden Methodenaufruf mit Parametern loggen) und Umsetzung (z.B. Aspektorientierte Programmierung).

Edit: Das hilft natürlich nur, wenn die Antwortung prinzipiell zwar startet, aber beim Zugriff auf Registry o.ä. scheitert. Wenn die Anwendung gar nicht erst startet (Rechteproblem beim Start von Netzlaufwerk o.ä.) greifen die anderen Mechanismen.

08.06.2010 - 13:08 Uhr

Hallo solick,

Möglich ist das sicherlich. Ob es rentable ist, ist jedoch von sehr vielen Faktoren abhänig...

Ein paar Beispiele:

  • Umfang der Anwendung
  • Software Design ("Spaghetti Code" erschwert eine Änderung der GUI)
  • Projektfortschritt
  • Abgabetermin
  • Kundenwunsch
  • Erfahrung mit WPF/Forms im Entwicklungsteam
  • ...

Die Antwort auf diese Frage ist somit so stark vom konkreten Projekt abhängig, dass Du hier keine generische Antwort erhalten wirst. Du müsstest uns schon ein paar mehr Infos geben.

Konverter sind IMHO weder sinnvoll noch verfügbar.

Grüße
Gwar

08.06.2010 - 13:03 Uhr

Hallo Herbivore,

danke für Deine Einschätzung und Hilfe!

Grüße
Gwar

08.06.2010 - 09:08 Uhr

Hallo Herbivore,

vielen Dank für die Antwort.

Nun wird meine Frage technisch: wenn ich eine C#-Dll in VS2008 über "Verweis hinzufügen" einbinde und keine bestimmte Version verlange, dann müsste diese doch im Explorer über Copy&Paste austauschbar sein. Ergo: sie ist dynamisch gelinkt. Richtig?

Grüße
Gwar

PS: sollte ich mich tatsächlich für die Integration einer LGPL-Bibliothek entscheiden, werde ich mir natürlich vorher rechtlichen Beistand holen. Dennoch Danke für die Interpretation.

08.06.2010 - 09:02 Uhr

Ich habe mit NHibernate nur ein wenig herumgespielt, aber IMHO müsste es gehen, wenn Du Dir in der Session nur die ID Felder merkst. Diese hast Du in Deinem XML-Mapping angegeben.

Aus den HTTP POST Daten kannst Du Dir die restlichen Felder ja wieder holen.

07.06.2010 - 13:02 Uhr

Hallo zusammen,

dank dieses Artikels: http://www.gnu.org/licenses/lgpl-java.html habe ich nun die Restriktionen der LGPL soweit verstanden.

Nun muss man ja in seinem "Nicht-open-Source"-Quellcode, das Neu-Linken der DLL plus ReverseEngineering zulassen. Am Einfachsten ist ersteres über DLLs, die dynamisch geladen werden.

Wie funktioniert dies in .net, wenn ich eine LGPL DLL einbinde? Ich muss ja immer einen Verweis zu den entsprechenden DLLs haben, um die Schnittstellen zu kennen. Ein "PlugIn" Ansatz erscheint mir daher nicht sinnvoll, da ich in jedem Fall mindestens einen abgeleiteten Quellcode (nämlich das Interface) erstellen muss.

Grüße
Gwar

21.05.2010 - 08:46 Uhr

Wenn ein Gerät Dir unterschiedliche Typen schickt, sind diese wohl auch unterschiedlich zu interpretieren.

Beispiel: Messwerte als float, Nachrichten als strings und GeräteID als int.

Daher ist es am saubersten, erst den Nachrichtentyp zu identifizieren (protokollabhängig), die Nachricht entsprechend zu parsen und dann darauf zu reagieren - ohne dafür ein und dieselbe Variable zu benutzen.
Dies kannst Du z.B. realisieren, indem Du verschiedene Handler je Protokollnachricht aufrufst.

21.05.2010 - 08:37 Uhr

Hallo kleines_eichhörnchen,

der as-Operator arbeitet korrekt. Allerdings erwartet der As-Operator auf der linken Seite eine Instanz, mit Typen funktioniert das nicht.

ich hatte Deine Aussage von oben wohl falsch verstanden, da ich das ganze nicht auf Typen bezogen habe.

Der weg von dir oben, alle exportierten Typen zu instanzieren und anschließend mittels as-Operator zu prüfen, ob diese IPlugin implementieren, ist nicht der grad der Hit.

Das habe ich ja auch eingesehen und geändert (s.o.)

20.05.2010 - 11:53 Uhr

Ich würde mal behaupten, dass ist abhängig vom Drucker/Druckertreiber. Schon mal bei HP nachgefragt?

20.05.2010 - 09:01 Uhr
  
if (typeof(IPlugin).IsAssignableFrom(t)) {  
  ...  
}  
  

Ich habe das mal entsprechend geändert:


private static IPlugin LoadPlugin(string dll)
        {
            Assembly assembly = Assembly.LoadFrom(dll);
            Type[] types = assembly.GetExportedTypes();
            foreach (Type t in types)
            {
                if (typeof(IPlugin).IsAssignableFrom(t))
                {
                    try
                    {
                        object plugin = assembly.CreateInstance(t.FullName);
                        return (IPlugin)p;
                    }
                    catch (MissingMethodException)
                    { 
                    }
                }
            }
            return null;
        }

Ansonsten müsste man alle Basisklassen des Typs abgrasen und zusätzlich noch zu jeder Basisklasse die Interfaces.

Entweder ich habs falsch verstanden oder es stimmt nicht. Der folgende Code gibt zwei mal "Hi!" aus:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AsOperatorTest
{
    interface I
    {
        string TellMeSomething();
    }
    class A : I
    {
        public A()
        { }

        #region I Member

        public string TellMeSomething()
        {
            return "Hi!";
        }

        #endregion
    }

    class B : A
    {
        public B() : base()
        { }

        public string TellMeMore()
        {
            return "Go!";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            object a = new A();
            object b = new B();

            I ia = a as I;
            if (ia != null)
            {
                Console.WriteLine(ia.TellMeSomething());
            }

            I ib = b as I;
            if (ib != null)
            {
                Console.WriteLine(ib.TellMeSomething());
            }
        }
    }
}

19.05.2010 - 17:31 Uhr

klingt nach Threading Problemen...!?

19.05.2010 - 17:14 Uhr

private static IPlugin LoadPlugin(string dll)
        {
            Assembly assembly = Assembly.LoadFrom(dll);
            Type[] types = assembly.GetExportedTypes();
            foreach (Type t in types)
            {
                object plugin = assembly.CreateInstance(t.FullName);
                IPlugin p = plugin as IPlugin;
                if (p != null)
                {
                    return p;
                }
            }
            
            return null;
        }

... falls Du nur einen Typ laden willst, ansonsten kannst Du man das recht einfach anpassen.

19.05.2010 - 16:53 Uhr

Danke für die Antwort!

Dann ist das leider nichts für mich 😉

18.05.2010 - 09:25 Uhr

Hallo,

leider ist auf der Seite nicht ersichtlich, ob das Angebot auch für die Mac Version gilt. Ich konnte es zumindest nicht herausfinden...

Hat schon jemand bestellt und kann darüber Auskunft geben, ob das Angebot auch für Mac gilt?

Grüße

26.04.2010 - 16:04 Uhr

Mittlerweile konnte ich das gewünschte Verhalten wie folgt erreichen:


<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <Style x:Key="{x:Type TextBox}" TargetType="{x:Type TextBox}">
            <Setter Property="FontFamily" Value="Arial"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Padding" Value="4"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Grid>
                            <Border Margin="0" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Padding="0" BorderBrush="#000000" BorderThickness="1" >
                                <Border Margin="0" Padding="0" BorderBrush="#FF0000" BorderThickness="1">
                                    <Border Margin="0" Padding="0" BorderBrush="#00FF00" BorderThickness="1">
                                        <Border Margin="0" Padding="0" BorderBrush="#0000FF" BorderThickness="1">
                                        </Border>
                                    </Border>
                                </Border>
                            </Border>
                            <ScrollViewer  Margin="0" x:Name="PART_ContentHost"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <TextBox Height="23" Margin="78,9,80,0" Name="textBox1" VerticalAlignment="Top" TextAlignment="Left">Hallo</TextBox>
    </Grid>
</Window>


21.04.2010 - 09:53 Uhr

Recht umständlich, aber als Denkansatz:

Mit FileSystemWatcher temporäres Verzeichnis überwachen und bei Änderung Datie ggf. zurückschreiben. Wenn mehrere Dateien gleichzeitig offen sind, musst Du da natürlich noch eine Zuordnung zwischen temporärer Datei und Datenbank eintrag treffen.

Grüße

19.04.2010 - 18:48 Uhr

Hallo zusammen,

wer meine Posts bei Windows Forms verfolgt hat, weiß, dass ich versuche, mir eine eigene Textbox zu basteln. Ich bin auch schon recht weit vorangeschritten, aber es ist halt doch viel Arbeit, da ich alles händisch machen muss. Daher dachte ich mir, ich versuche es mal mit WPF.

Durch Forumssuche bin ich immerhin so weit gekommen (Minimalbeispiel):


<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <ControlTemplate x:Key="myTextBox" TargetType="{x:Type TextBox}">
            <Grid>
                <Border Margin="0" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Padding="0" BorderBrush="#000000" BorderThickness="1" >
                    <Border Margin="0" Padding="0" BorderBrush="#FF0000" BorderThickness="1">
                        <Border Margin="0" Padding="0" BorderBrush="#00FF00" BorderThickness="1">
                            <Border Margin="0" Padding="0" BorderBrush="#0000FF" BorderThickness="1">
                            </Border>
                        </Border>
                    </Border>
                </Border>
                <ScrollViewer  Margin="0" x:Name="PART_ContentHost"/>
            </Grid>
        </ControlTemplate>
    </Window.Resources>
    <Grid>
        <TextBox Height="23" Margin="78,9,80,0" Name="textBox1" VerticalAlignment="Top" Template="{DynamicResource myTextBox}">Hallo</TextBox>
    </Grid>
</Window>


Mein Problem ist, dass folgende Zeile jeden Versuch, den Font oder die Schriftfarbe zu ändern, ignoriert:


<ScrollViewer  Margin="0" x:Name="PART_ContentHost"/>

Ein ContentPresenter dagegen zeigt mir den Caret nicht an.

Da sowohl Caret als auch das ändern des Fonts unabdingbar sind, wäre ich für jede Hilfe, die mich in die richtige Richtung drückt, dankbar.

15.04.2010 - 14:16 Uhr

Hallo herbivore

Allerdings ist das Problem schon gelöst worden.

das stimmt - die entsprechende Markierung des Threads ist mir leider erst nach dem Post aufgefallen 🙁 Daher wollte ich noch einen Denkansatz beisteuern...

Bei deinem Vorschlag fehlt mindestens noch ein Monitor.Exit, das auch sicher ausgeführt wird, wenn das Enter geklappt hat.

Auch das ist richtig. Dies müsste (der Vollständigkeit halber) dann entsprechend geändert werden:


void ConditionLoop(object sender, EventArgs e)
{
  if (monitor.TryEnter(lockObject))
  {
    try
    {
      if (condition_1 == true)
      {
        //mach was
      }
      else
      {
        //mach was anderes
      }
    }
    finally
    {
      monitor.Exit();
    }
  }
} 

Gruß

15.04.2010 - 12:02 Uhr

Hallo,

Danke, diese Lösung funktioniert, bis auf eine Kleinigkeit: In der Taskbar wird nur ein Form angezeigt, bei ALT+TAB aber beide. Dies lässt sich mit folgendem Code aber lösen:



///<summary>
/// Prevent form being showed when ALT+TAB is pressed
///</summary>
protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                // turn on WS_EX_TOOLWINDOW style bit
                cp.ExStyle |= 0x80;
                return cp;
            }
        }

15.04.2010 - 11:48 Uhr

Hallo,
geht es nicht darum, die zweimalige Ausführung zu verhindern?
Also sowas hier (ungetestet!)


private Object lockObject;
private Monitor monitor;
public form1()
{
// Initialisierung des monitors und lockObjects:
monitor = new Monitor();
lockObject = new Object();
//
//allgemeine Definitionen des Formulars...
//
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new System.Timers.ElapsedEventHandler(ConditionLoop);
myTimer.Interval = 1000;
myTimer.Start();
}

void ConditionLoop(object sender, EventArgs e)
{
if (monitor.TryEnter(lockObject))
{
if (condition_1 == true)
{
//mach was
}
else
{
//mach was anderes
}
}
} 

Damit würde man halt schlimmstenfalls einen Timerzyklus bis zur nächsten Ausführung warten. Aber so wie ich das verstehe ist das kein Problem?

14.04.2010 - 13:48 Uhr

Hallo zusammen,

zunächst einmal vielen Dank für die Antworten. Ich habe noch nicht alle Optionen geprüft, aber es ist schon viel wertvoller Input dabei.

@inflames2k: Ich habe Invalidate() auf das Form aufgerufen.

@ErfinderDesRades: Der Ansatz mit den beiden Forms klappt super. Das Problem dann ist, dass ich über ALT+TAB die beiden Forms auch unabhängig voneinander in den Vorder/Hintergrund kriege. Klappt es, diese quasi als "ein Form" zu behandeln?

Dank und Gruß

12.04.2010 - 15:51 Uhr

Hallo zusammen,

Ich möchte ein Form erstellen, das keinen Rahmen und keine ControlBox hat. Über diesem Form soll - statt der eigentlichen ControlBox - ein halbtransparentes Control sein, mit welchem sich die Form verschieben lässt.
Durch Forumssuche bin ich schon ein ganzes Stück weiter gekommen. Ich habe ein transparentes Form und ein transparentes Control. Beim Verschieben der Form jedoch wird der (transparente) Hintergrund nicht neu gezeichnet, es sieht also aus, als würde man einen Screenshot verschieben. Code sagt mehr als 1000 Worte:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace FormTranKey
{

    public class DragControl : Control
    {
        public DragControl()
        {
            MinimumSize = new Size(100, 30);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20;
                return cp;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        { }

        protected override void OnPaint(PaintEventArgs e)
        {
            Color c = Color.FromArgb(125, Color.Red);
            using (Brush b = new SolidBrush(c))
            {
                e.Graphics.FillRectangle(b, 0, 0, this.Width, this.Height);
            }
        }
    }

    public class MyTableLayoutPanel : TableLayoutPanel
    {
        public MyTableLayoutPanel()
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            BackColor = Color.Transparent;
        }
    }

    public class Class1 : Form
    {
        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;
        
        public Class1()
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            ShowInTaskbar = false;
            ShowIcon = false;
            ControlBox = false;
            this.FormBorderStyle = FormBorderStyle.None;

            TableLayoutPanel tbp = new MyTableLayoutPanel();
            tbp.RowCount = 3;
            tbp.ColumnCount = 1;
            DragControl dc = new DragControl();
            dc.MouseDown += new MouseEventHandler(dc_MouseDown);
            tbp.Controls.Add(dc, 0, 0);
            tbp.Controls.Add(new TextBox(), 0, 2);
            
            Controls.Add(tbp);
        }

        void dc_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
        }
        
        protected override void OnPaintBackground(PaintEventArgs e)
        {
        
        }

        [DllImportAttribute("user32.dll")]
        private static extern int SendMessage(IntPtr hWnd,
                         int Msg, int wParam, int lParam);
        [DllImportAttribute("user32.dll")]
        private static extern bool ReleaseCapture();
    }
}


Wie kriege ich das Form dazu, sich beim Bewegen neu zu zeichnen?
Sämtliche Versuche mit Update(), Invalidate() oder Refresh(), z.B. bei LocationChanged, sind gescheitert.

Viele Grüße
Gwar

09.04.2010 - 09:57 Uhr

Ansonten könntest Du, AFAIK, auch mit listener.Pending() abfragen, ob ein Client überhaupt eine Anfrage stellt.

07.04.2010 - 09:21 Uhr

Du könntest natürlich auch probieren, das ARToolkit einzubinden. Damit kann man Marker (statt Farben) erkennen. Es ist natürlich in C++ geschrieben. Ich weiß nicht, ob es C# Bindings gibt, aber falls nein kann man ja recht simpel selbst die DLLs einbinden.

http://www.hitl.washington.edu/artoolkit/

06.04.2010 - 17:58 Uhr

Das ist zwar schöner, allerdings hast du damit leider keine Designerunterstützung mehr.

Ertappt. Hier muss man natürlich abwägen, was einem wichtiger ist.

06.04.2010 - 17:29 Uhr

Hallo,

müsste der Code für GetBitmap nicht so lauten?


public static Bitmap GetBitmap(this Control _control)
        {
            System.IntPtr srcDC = DllImport.GetDC(_control.Handle);
            Bitmap bm = new Bitmap(_control.Width, _control.Height);
            Graphics g = Graphics.FromImage(bm);
            System.IntPtr bmDC = g.GetHdc();
            // Difference begins here: Get client coordinates
            Point loc = _control.PointToClient(_control.Location);
            // Use client coordinates (instead of 0,0 coordinates)
            DllImport.BitBlt(bmDC, 0, 0, bm.Width, bm.Height, srcDC, loc.X, loc.Y, 0x00CC0020 /*SRCCOPY*/);
            // Difference ends here
            DllImport.ReleaseDC(_control.Handle, srcDC);
            g.ReleaseHdc(bmDC);
            g.Dispose();
            return bm;
        } 

Edit: Sorry, ich war etwas voreilig. Mein Code funktionierte, als ich einen Dialog von außen "fotografierte". Der von dr4g0n76 gepostete Code funktioniert, wenn man man ein Control innerhalb eines Dialoges "fotografiert".

06.04.2010 - 16:56 Uhr

Hallo zusammen,

die letztgenannte Lösung von herbivore ist das, wonach ich ursprünglich gesucht habe. Durch die Posts von dr4g0n76 bin ich aber auch auf andere Ideen gekommen (insbesondere vielen Dank für die Komponente). Insofern habe ich nun alle Informationen, um meine TextBox selber umzusetzen. Wie ich das mache ist aber noch nicht entschieden.

Herzlichen Dank

06.04.2010 - 16:49 Uhr

Wenn ich das richtig verstanden habe, möchtest Du den Zugriff auf Form.Controls verbieten und nur auf Panel.Controls zulassen?

Mein Ansatz wäre dann, die nicht zugreifbaren Parameter auf "private" zu setzen. Damit sind sie geschützt. Alles was zugreifbar sein muss, würde ich - wie dN!3L schon schrieb, "protected" machen. Im Unterschied zu dN!3L würde ich jedoch nicht das Panel selbst sichtbar machen, sondern entsprechende Methoden oder Properties definieren.

Aus der Hüfte geschossen wäre das:


private Panel panel;
// ...
protected void AddControlToForm(Control control)
{
   panel.Controls.Add(control);
}

Das hat den Vorteil, dass sich der Code Deiner Kindklassen nicht ändert, wenn Du das Panel umbenennst oder ersetzt.

05.04.2010 - 11:21 Uhr

Hallo Herbivore,

danke für diesen Tipp. Diese Property habe ich bislang noch gar nicht in Betracht gezogen. Ich werde das Ganze mal ausprobieren, sobald ich wieder einen .net Rechner habe.

Frohe Ostern

02.04.2010 - 14:55 Uhr

Hallo Herbivore,

ich habe leider erst am Dienstag wieder einen Windowsrechner zur Verfügung und auf dem System hier ist kein Mono drauf. Daher ist der folgende Code ungeprüft:


private void RenderText(PaintEventArgs e, bool clipRight)
{
    string longText = "This is some long text that will be clipped at the end or in the beginning, depending on the TextFormatFlags."
    TextFormatFlags flags;
    if (clipRight)
    {
       // Starts painting at "This is..." from left to right. Right part is clipped 
       flags = TextFormatFlags.Left; 
    }
    else
    {
       // Starts painting at "TextFormatFlags" from right to left. left part is clipped
       flags = TextFormatFlags.Right;
     } 
    TextRenderer.DrawText(e.Graphics, longText, this.Font,
        new Rectangle(0, 0, 10, 10), SystemColors.ControlText, flags);
}

Ich wüsste nun nicht, wie ich den TextRenderer dazu bekomme, den Text z.B. von "long text..." an zu zeichenen (es sei denn manuell programmierte Puffer kommen ins Spiel).

Grüße

02.04.2010 - 13:48 Uhr

Hallo,

vielen Dank für Eure Antworten!

@herbivore:
Da (bislang) mein Ansatz war, alles selbst zu implementieren, weiß ich, wie ich den TextRenderer dazu bringe, Text zu zeichnen. Leider klappt dies nur, wenn der komplette String entweder komplett von Links oder von Rechts gezeichnet wird. Es ist nicht möglich, einen String so innerhalb des Clippingbereiches zu zeichnen, dass von diesem links und rechts etwas abgeschnitten wird. Natürlich ist es denkbar, einen internen Puffer zu implementieren. Damit wäre aber nur "buchstabenweises" Scrollen möglich. Die nativen Controls unterstützen aber pixelweises Scrollen, somit denke ich, dass das ja auch irgendwie von Hand gehen müsste.

@Jürgen
Ja, ich habe mir einige Lösungen bei CodeProject angeschaut, leider haben meine Suchen zu keinem Ergebnis geführt.

@dr4gon76

[...]Es gäbe aber auch noch eine ganz andere Alternative mit einer unsichtbaren Richtextbox.[...]

Das klingt auch sehr vielversprechend. Wenn der Text so auch "Anti-Aliased" gezeichnet werden kann, ist dies durchaus ein gangbarer Weg.

[...]bei Deinem Ansatz würde ich mir die TextEditor Komponente von SharpDevelop ansehen. Und den Ansatz herausziehen oder vielleicht nimmst Du die Komponente komplett um das Aussehen zu verändern.[...]

Auch das klingt gut. Vielen Dank für die Anregungen! Ich werde zunächst "zweigleisig fahren" und mir die genannten Alternativen anschauen. Sobald ich zu einer Lösung gekommen bin, werde ich den Weg selbstverständlich hier posten.

Viele Grüße und besten Dank an alle
Gwar

Edit:
Oh, das habe ich hier noch überlesen:

Ok, ich werd sie hier posten.

Auch das wäre natürlich super. Ich habe mir Deine anderen Lösungen (leider noch nicht im Detail) angeschaut und bin beeindruckt - insofern zweifele ich nicht, dass ich (und andere User) aus dieser Komponente wertvolle Ansetze ziehen kann.

01.04.2010 - 22:41 Uhr

Hallo zusammen,

ich versuche eine TextBox zu entwickeln, welche zwar die Funktionalität einer herkömmlichen Textbox implementiert, aber ein komplett anders aussieht (bspw. sollen die Ecken abgerundet dargestellt werden, ähnlich wie manche Eingabefelder bei Apple).
Da die Standardtextbox ja nur ein Wrapper des Standard-Windows-Controls ist, klappt das Überladen der OnPaint() Methode ja nur bedingt. Ich vermute , dass es in meinem Fall auch nicht sinnvoll ist, da der Text (per Anforderungsdefinition) "Anti-Aliased" dargestellt werden soll.

Ich bin daher dazu übergegangen direkt von Control zu erben und die Funktionalität von Hand nachzubilden - auch, wenn das viel Arbeit bedeutet.

Nun bin ich aber an einem Punkt angelangt, wo ich nicht ganz weiter komme: beim Vertikalen Scrollen von Text. Jeder kennt das Verhalten: bei der Texteingabe scrollt der Text an der Position des Cursors mit, sobald die Breite des Textes die Breite der TextBox überschreitet. Auch mit den Pfeiltasten kann man den dargestellten Text so scrollen. Analog gilt dies auch für das Horizontale Scrollen bei mehrzeiligen Eingabefeldern. Denkbar ist ähnliches Verhalten auch mit Scrollbalken.

Ich habe im .Net Fragewort leider keine Möglichkeit gefunden, dies zu realisieren, zumal Buchstaben ja auch "teilweise" gezeichnet werden müssten, sobald der Text (begrenzt durch die Größe der TextBox) nur teilweise dargestellt wird.

Ich wäre äußerst dankbar, falls mir jemand einen Tipp geben könnte, wie ich dies realisieren kann. Alternativ bin ich dankbar für Tipps, die mir helfen, den hohen Aufwand des kompletten Tastaturhandlings zu umgehen.

Vielen Dank im Voraus
Gwar