Laden...
H
hypersurf myCSharp.de - Member
Softwareentwickler Münster Dabei seit 16.10.2008 523 Beiträge
Benutzerbeschreibung

Forenbeiträge von hypersurf Ingesamt 523 Beiträge

21.07.2013 - 22:58 Uhr

Danke für Deine Antwort, herbivore. Ich denke, es wird wohl darauf hinauslaufen, dass ich die anderen Steuerelemente manuell deaktivieren muss. Ist auch nicht so tragisch, ich fand' das mit dem Panel nur sehr praktisch. Da ich für diese Anwendung, die in meiner Freizeit entsteht, kein WPF verwenden möchte, muss ich wohl auf die Transparenz verzichten.

Ob modal oder nicht ist sicher auch ein Stück weit Geschmackssache 😃

21.07.2013 - 12:57 Uhr

Hier der zweite Screenshot

21.07.2013 - 12:56 Uhr

Hallo Leute,

ich habe eine Anwendung die ein TabControl enthält. Dem Tab werden dynamisch neue TabPages hinzugefügt, die dann auch gleichzeitig geöffnet bleiben.

Damit die GUI bei Messageboxen nicht blockiert, habe ich eine eigene Messagebox geschrieben. Die eigene Messagebox besteht aus einem Panel welches die für die Messagebox benötigten Controls (Icon, Buttons, Label, etc.) enthält.

Mit der Benutzer mit den Controls auf der TabPage nicht interagieren kann, während die Messagebox angezeigt wird, lege ich über die TabPage ein transparentes Panel. Das Transparente Panel enthält die Messagebox als Control. Die Messagebox soll innerhalb dieses Panels grundsätzlich zentriert angezeigt werden.


        private My_TransparentPanel _MessageboxPanel;
        /// Panel welches vollständig über die TabPage gelegt wird, wenn die Messagebox angezeigt wird.
        /// Dadurch kann der User nichts mehr anklicken.
        public My_TransparentPanel MessageboxPanel
        {
            get { return _MessageboxPanel; }
            set { _MessageboxPanel = value; }
        }

        private My_MessageboxPanel _Messagebox;
        /// <summary>
        /// Messagebox welche angezeigt werden kann
        /// </summary>
        public My_MessageboxPanel Messagebox
        {
            get { return _Messagebox; }
            set { _Messagebox = value; }
        }

Erstellung der Panels innerhalb der TabPage:


            this.MessageboxPanel = new My_TransparentPanel();
            this.MessageboxPanel.Location = new Point(0, 0);
            this.MessageboxPanel.Visible = false;
            this.MessageboxPanel.Dock = DockStyle.Fill;

            this.MessageboxPanel.SizeChanged += new EventHandler(MessageboxPanel_SizeChanged);
            this.Controls.Add(this.MessageboxPanel);

            this.Messagebox = null;
            this.Messagebox = new My_MessageboxPanel();
            this.Messagebox.SizeChanged += new EventHandler(Messagebox_SizeChanged);
            this.Messagebox.MessageBoxButtonPressed += new EventHandler<My_EventArgsMessageBoxButtonPressed>(Event_MessageBoxButtonPressed);
            this.MessageboxPanel.Controls.Add(this.Messagebox);

Events für die Behandlung der Größenänderung:


 private void MessageboxPanel_SizeChanged(object sender, EventArgs e)
        {
            this.Messagebox.Left = (this.MessageboxPanel.Width - this.Messagebox.Width) / 2;
            this.Messagebox.Top = (this.MessageboxPanel.Height - this.Messagebox.Height) / 2;
        }

        private void Messagebox_SizeChanged(object sender, EventArgs e)
        {
            this.Messagebox.Left = (this.MessageboxPanel.Width - this.Messagebox.Width) / 2;
            this.Messagebox.Top = (this.MessageboxPanel.Height - this.Messagebox.Height) / 2;
        }

Code des transparenten Panels:


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
     
namespace My.Controls
{
    /// <summary>
    /// Transparentes Panel
    /// </summary>
    public class My_TransparentPanel : Panel
    {
        public My_TransparentPanel()
        {
        }
     
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams createParams = base.CreateParams;
                createParams.ExStyle |= 0x02000000;
                //0x00000020; // WS_EX_TRANSPARENT
                return createParams;
            }
        }
     
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Do not paint background.
        }
    }
}

Zum eigentlichen Problem

Das Anzeigen des transparenten Panels mit der Messagebox funktioniert problemlos (Screenshot 1). Leider wird die Messagebox nicht richtig neu gezeichnet wenn sich die Größe des Panels verändert. Das sieht man am zweiten Screenshot ganz gut. Ich habe die Form für dieses Beispiel etwas kleiner gezogen. Dieses Verhalten liegt definitiv an dem transparenten Panel, mit einem normalen Panel funktioniert es ohne Probleme..

Habt Ihr eine Idee wie ich das Problem in den Griff bekommen kann?

20.03.2013 - 23:15 Uhr

Verwende anstatt der normalen List<T> eine BindingList<T>. Dann klappt es 😃

Interessant dazu ist auch dieser Artikel: Change Notification in Windows Forms Data Binding

10.01.2013 - 15:42 Uhr

Das Löschen der Ordner hat leider nichts gebracht.

Ich habe heute nochmal diverses ausprobiert:

  • Löschen der Ordner obj und bin und erneutes kompilieren
  • Die app.config von Visual Studio einfügen lassen
  • Die DLLs aus dem GAC entfernt, in der richtigen Reihenfolge kompiliert und die Verweise manuell (per Durchsuchen) auf die Dateien gesetzt. Dann das Projekt neu kompiliert

Ich kann überhaupt nicht nachvollziehen, warum .NET noch nach einer Version 1.0.0.2 sucht. Wenn ich genau dieselbe Version (inkl. aller DLLs) bei uns auf einem blanken Rechner startet funktioniert es. Beim Kunden aber auf keinem einzigen System.

Als Lösung konnte ich bisher leider nur eine Krücke finden: Ich habe die Versionen der DLLs wieder auf 1.0.0.2 zurückgesetzt und alles neu kompiliert. Damit läuft dann auch das Programm beim Kunden 🤔

09.01.2013 - 12:06 Uhr

Hallo Leute,

ich habe ein WPF-Projekt in der auf vier selbst geschriebene DLLs verwiesen wird, die sich alle mit der korrekten Version im GAC befinden. Die Versionsnummern vergeben wir immer manuell.

Für einige Änderungen wurden zwei der DLLs kürzlich geändert und neu kompiliert. Anschließend wurden Änderungen in dem Programm durchgeführt und dieses ebenfalls kompiliert.

Bei uns auf den Entwicklungsrechnern läuft es ohne Probleme. Beim Kunden installieren wir die DLLs nicht im GAC sondern kopieren alle benötigten DLLs in das Verzeichnis in dem auch die EXE-Datei liegt. Das funktionierte bisher eigentlich auch immer ohne Probleme.

Beim Starten auf dem Zielrechner erscheint folgende Exception:

Fehlermeldung:
Eine unbehandelte Ausnahme ist aufgetreten:

System.Windows.Markup.XamlParseException: Die in der Assembly "XXX PApp, Version=1.0.4.9, Culture=neutral, PublicKeyToken=null" definierte Instanz von "MainWindow" kann nicht erstellt werden. Ein Aufrufziel hat einen Ausnahmefehler verursacht. Fehler in Markupdatei "XXX PApp;component/Views/MainWindow.xaml". ---> System.Reflection.TargetInvocationException: Ein Aufrufziel hat einen Ausnahmefehler verursacht. ---> System.IO.FileLoadException: Die Datei oder Assembly "XXX.System.WPF, Version=1.0.0.2, Culture=neutral, PublicKeyToken=67ca0fb70dafd111" oder eine Abhängigkeit davon wurde nicht gefunden. Die gefundene Manifestdefinition der Assembly stimmt nicht mit dem Assemblyverweis überein. (Ausnahme von HRESULT: 0x80131040)

Dateiname: "XXX.System.WPF, Version=1.0.0.2, Culture=neutral, PublicKeyToken=67ca0fb70dafd111"

bei XXX_PApp.ViewModels.MainViewModel..ctor()

bei XXX_PApp.MainWindow..ctor()

Das komische ist, das die Datei XXX.System.WPF.dll mittlerweile in Version 19.12.0.1 (wir hatten einen großen Versionssprung) vorliegt, das Projekt aber irgendwie noch auf eine viel ältere Version verweist.

Wenn ich im Projekt-Explorer den Verweis anklicke wird die "Version" mit dem Wert 19.12.0.1 angezeigt.

Beim Kompilieren ist mir folgende Meldung aufgefallen:

Sie können mit "app.config" die XXX.System.WPF, Culture=neutral, PublicKeyToken=67ca0fb70dafd111-Assembly-Version von 1.0.0.2 [] auf 19.12.0.1 [....\Libraries\XXX.System.WPF\XXX.System.WPF\bin\Release\XXX.System.WPF.dll] neu zuordnen, um Konflikte zu lösen und Warnmeldungen zu vermeiden.

Könnt Ihr mir einen Tipp geben, wie ich die Zuordnung neu erstellen kann? Ich konnte bei google irgendwie nichts passendes finden.

Danke 😃

11.12.2012 - 08:37 Uhr

Ich würde den Timer einfach nur stoppen, also Timer.Stop.

Ich auch, leider gibt es diese Methode im Compact Framework bei dieser Timer-Variante nicht 😃

Den Callback beendet du schlicht mit return (oder dadurch, dass er ans Ende der Methode läuft). Wenn der er das nicht (in angemessener Zeit) tut, muss du die Programmierung des Callbacks ändern. Den Timer zu zerstören hilft da gar nichts.

Das werde ich mal probieren. Wenn das nichts hilft, dann werde ich mal schauen ob ich einen anderen Timer verwenden kann, der mir vielleicht mehr Flexibilität bietet.

10.12.2012 - 09:29 Uhr

Wie beende ich denn den Timer-Thread? Ich hatte es mit _WatchTimer.Dispose() versucht. Das hat aber gar keine Auswirkung, weil das Callback des Timers immer noch weiter ausgeführt wird.


TimerCallback cb = new TimerCallback(Watch);
_WatchTimer = new System.Threading.Timer(cb, _Config, _WatchTimerWaitTimeSeconds * 1000, Timeout.Infinite);


        private void Watch(object objectState)
        {
            // Timer pausieren damit bei längerer Ausführung nichts doppelt aufgerufen wird
            _WatchTimer.Change(Timeout.Infinite, Timeout.Infinite);

            .........
            .........
            .........

            // Prüfen ob der Shortcut zum Beenden des Programms
            // eingegeben wurde.
            // Diese Methode ruft ggf. die Methode CloseApplication auf (siehe erster Beitrag)
            this.CheckBeendenShortcut();

            // Timer wieder starten
            _WatchTimer.Change(_WatchTimerWaitTimeSeconds * 1000, Timeout.Infinite);
        }


        /// <summary>
        /// Anwendung schließen
        /// </summary>
        private void CloseApplication()
        {
            _WatchTimer.Dispose();

            // Für den Fall, dass die Methode nicht aus dem GUI-Thread aufgerufen wird:
            if (this.InvokeRequired)
            {
                DelegateExitForm callback = new DelegateExitForm(DisposeForm);
                this.Invoke(callback);
            }
            else
            {
                this.Dispose();
            }

            Application.Exit();
        }


        /// <summary>
        /// Form terminieren
        /// </summary>
        private void DisposeForm()
        {
            this.Dispose();
        }

Der Aufruf des zweiten _WatchTimer.Change am Methodenende führt in diesem Konstrukt logischerweise zu einer ObjectDisposedExpection.

Ich kann auch leider nicht wie in Timer.Dispose beschrieben mit Dispose(WaitHandle) arbeiten, weil das Compact Framework diese Überladung gar nicht anbietet.

07.12.2012 - 10:58 Uhr

Hallo Leute,

wir haben eine Anwendung die mit dem Compact Framework 3.5 in c# entwickelt wurde. Diese läuft auf Windows CE (6)-Geräten.

Die Anwendung besteht aus einer einzigen Form. Innerhalb der Form läuft ein Timer (System.Threading.Timer) welcher periodisch bestimmte Aktionen ausführt.

Mittels einer Tastenkombination kann der User das Programm beenden. Da das Programm nicht mitten in einer automatischen Aktion beendet werden soll, merkt sich das Programm ob die Tastenkombination gedrückt wurde und verarbeitet diese Information am Ende der Timer-Methode.

Mein Problem ist, dass sich das Programm nicht richtig beendet, sondern als Prozess hängen bleibt.

So versuche ich das Programm zu beenden:


        /// <summary>
        /// Anwendung schließen
        /// </summary>
        private void CloseApplication()
        {
            // Für den Fall, dass die Methode nicht aus dem GUI-Thread aufgerufen wird:
            if (this.InvokeRequired)
            {
                DelegateExitForm callback = new DelegateExitForm(DisposeForm);
                this.Invoke(callback);
            }
            else
            {
                this.Dispose();
            }

            Application.Exit();
        }

        /// <summary>
        /// Form terminieren
        /// </summary>
        private void DisposeForm()
        {
            this.Dispose();
        }

Nachdem diese Methode durchlaufen wurde, läuft der Timer-Thread immer noch. Könnt Ihr mir einen Tip geben, wie ich den ich den Timer-Thread richtig beende?

15.08.2012 - 10:57 Uhr

Danke für die Erklärung 🙂

Ich habe es jetzt so gelöst, dass ich vorab einmalig ein Bitmap-Objekt erzeuge.

15.08.2012 - 10:04 Uhr

Hallo Leute,

unsere Software wurde mit dem Compact Framework 3.5 geschrieben und läuft auf Windows CE 6. Innerhalb der Software werden Bilder angezeigt, die zuvor als Bitmap geladen werden:


Bitmap bm = new Bitmap(pfadzurdatei);

Komischerweise dauert das allererste Instanzieren eines Bitmap-Objekts 2 - 3 Sekunden. Das Erzeugen aller folgenden Instanzen dauert lediglich 500 Millisekunden.

Gibt es einen Grund dafür, warum das allererste Erzeugen eines Bitmap-Objekts so viel länger dauert als die folgenden?

Danke 😃

18.06.2012 - 14:52 Uhr

Hallo Leute,

ich habe ein Problem mit einem Backgroundworker welches ich nicht verstehe. Ich habe schon diverse Blogs und auch [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) gelesen, komme aber partou nicht zu einer Lösung.

Mittels mehrerer Backgroundworker möchte ich vermeiden, dass meine UI beim Laden von Daten einfriert. Die Backgroundworker können aufgrund der UI-Logik nur nacheinander ausgelöst werden.

In den Methoden der Backgroundworker werden ObservableCollections gefüllt, die an die GUI via DataBinding gebunden sind.


public myVieModel()
{
	this.DispatcherThread = Dispatcher.CurrentDispatcher;
	this.Workflowliste = new ObservableCollection<ViewModeWFItem>();
}


        private ObservableCollection<ViewModeWFItem> _Workflowliste;
        /// <summary>
        /// Liste für das TreeListView
        /// </summary>
        public ObservableCollection<ViewModeWFItem> Workflowliste
        {
            get { return _Workflowliste; }
            set { 
                    _Workflowliste = value;
                    this.ValueChanged("Workflowliste");
                }
        }

Das Binding der Liste:

 <telerik:RadTreeListView x:Name="RadTreeListView1"
								         AutoGenerateColumns="False" 
                                         IsReadOnly="True" 
                                         ItemsSource="{Binding Workflowliste}" 
                                         CanUserFreezeColumns="False"
                                         AutoLoadHierarchy="True"
                                         Grid.Column="0" Grid.Row="0"
                                         telerikQuickStart:ThemeAwareBackgroundBehavior.IsEnabled="True"
                                         telerikDragDrop:RadDragAndDropManager.AllowDrag="True">
                    
                    <telerik:RadTreeListView.ChildTableDefinitions>
                        <telerik:TreeListViewTableDefinition ItemsSource="{Binding ItemsView}"/>
                    </telerik:RadTreeListView.ChildTableDefinitions>
                    
                    <telerik:RadTreeListView.Columns>
                        <telerik:GridViewDataColumn DataMemberBinding="{Binding Bezeichnung}" 
                                                    CellTemplateSelector="{StaticResource FirstCellTemplateSelector}" 
                                                    Header="Bezeichnung"/>
                        <telerik:GridViewDataColumn DataMemberBinding="{Binding Status}" Header="Status"/>
                    </telerik:RadTreeListView.Columns>
                </telerik:RadTreeListView>


        private void LoadData()
        {
            this.IsAtWork = true;
            this._WorkerLoad = new BackgroundWorker();
            this._WorkerLoad.DoWork += this.WorkerLoadDoWork;
            this._WorkerLoad.RunWorkerCompleted += WorkerLoadRunWorkerCompleted;
            this._WorkerLoad.RunWorkerAsync();

        }
        #endregion

        #region WorkerLoad
        private void WorkerLoadDoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                e.Result = this.Load();
            }
            catch (Exception ex)
            {
                e.Result = false;
                MessageBox.Show(ex.Message, "", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void WorkerLoadRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.IsAtWork = false;
        }
        #endregion

Load-Methode mit dem Füllen der Collection:


public void Load()
{
            this.DispatcherThread.
            Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
            {
                this.Workflowliste.Clear();
            }));

            foreach (ModelWorkflow workflow in this.ModelController.AktuellerProzess.Workflows)
            {
                ViewModelWFItem wfitem = new ViewModelWFItem();
                wfitem.Bezeichnung = workflow.Bezeichnung;
                wfitem.InternalWorkflowKey = workflow.InternalKey;

                this.DispatcherThread.
                 Invoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
                 {
                     this.Workflowliste.Add(wfitem); 
                 }));
                
            }
}

Bei den Invoke-Aufrufen bekomme ich eine System.InvalidOperationException: Es kann kein "DependencyObject" verwendet werden, das zu einem anderen Thread als das übergeordnete Freezable-Objekt gehört.

Entferne ich das Invoke bekomme ich logischerweise eine System.InvalidOperationException: Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet.

Könnt Ihr mir einen Tipp geben, was ich übersehen habe bzw. falsch mache?

15.06.2012 - 08:53 Uhr

Hallo Leute,

in einem Windows CE-Programm haben wir das Problem, dass das Füllen von Objekten relativ lange dauert.

Es werden Daten aus einer SQLCompact-Datenbank ausgelesen und anschließend ca. 15 Objekte mit jeweils 15 Attributen gefüllt. Das Auslesen aus der Datenbank geht dank optimierter Abfragen sehr schnell.
Nur das Füllen der Objekte dauert 1 - 2 Sekunden, was für die Benutzer leider zu langsam ist.

Momentan werden die Werte aus dem Datareader einfach in den entsprechenden Datentypen konvertiert und dann in das entsprechende Attribut der Instanz gefüllt.

Der Abschnitt der lange dauert sieht so aus:


                    while (reader.Read())
                    {
                        myclass instanz = myclass();

                        instanz.nr = (string)(reader["nr"]);
                        instanz.bez1 = (string)(reader["bezeichnung_1"]);
                        instanz.bez2 = (string)(reader["bezeichnung_2"]); 

                        instanz.sollBestand = (int)(reader["sollbestand"]);
                        instanz.istBestand = (int)(reader["istbestand"]);

                        liste.Add(instanz);
                    }
                    reader.Close();

Gibt es eine Möglichkeit, die Objekte mit den Werten aus dem Reader schneller zu füllen?

31.05.2012 - 16:30 Uhr

Hallo Leute,

ich habe eine Maske erstellt die mittels eines TabControls in mehrere Registerkarten unterteilt ist. Auf der letzten Registerkarte liegt ein großes Grid-Layout mit mehreren hundert Zellen.

Wenn man das erste Mal auf diese Registerkarte klickt, dauert es drei Sekunden bis das Programm die Registerkarte schließlich wechselt.

Gibt es eine Möglichkeit, das Laden des Layouts (oder was auch immer WPF bei der ersten Anzeige der Registerkarte macht) vorab laufen zu lassen? Z. B. mittels eines Backgroundsworks direkt nach dem Programmstart?

31.05.2012 - 12:25 Uhr

Hallo Leute,

ich muss innerhalb eines Fensters (Haupfenster) ein Unterfenster anzeigen. Dies soll mittels eines Frames realisiert werden, welches einfach eine weitere Page anzeigt.

Auf dem Hauptfenster liegen diverse Textboxen, deren Inhalt validiert wird. Die Validierung wird mittels eines ErrorTemplates visualisiert:


        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel LastChildFill="True" Margin="3">
                        <!-- Rotes Ausrufezeichen erzeugen (rechts angedockt) -->
                        <TextBlock DockPanel.Dock="Right" Foreground="White" FontSize="14" FontWeight="Bold" Background="Red" Padding="3" Text="!"
                                ToolTip="{Binding ElementName=Control_AdornedElementPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                        </TextBlock>
                        <!-- Roten Rahmen um das Steuerelement erzeugen (mit dem Element den Rest ausfüllen) -->
                        <Border BorderBrush="Red" BorderThickness="2">
                            <AdornedElementPlaceholder Name="Control_AdornedElementPlaceholder"/>
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

Das Frame welches das Unterfenster anzeigt ist so definiert:


        <Frame VerticalAlignment="Center" HorizontalAlignment="Center" 
                       Visibility="{Binding IsSubFrameContentVisible, Converter={StaticResource VisibilityOfBoolConverter}}"
                       Content="{Binding Path=SubFrameContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                       NavigationUIVisibility="Hidden">
        </Frame>

Die Anzeige des Unterfensters funktioniert problemlos. Leider überdeckt das ErrorTemplate das Frame anscheinend. Ich komme nur nicht dahinter warum.

Im Anhang sieht man das Unterfenster, welches im Frame angezeigt wird und die ErrorTemplates der Textboxen aus dem Hauptfenster.
Ich habe auch schon versucht die Fehlerbehandlung zu deaktivieren, während das Unterfenster angezeigt wird, leider ohne Erfolg.

Habt Ihr eine Idee wie ich das Problem lösen kann?

17.04.2012 - 16:21 Uhr

Danke, ich hab' den Wald vor lauter Bäumen schon nicht mehr gesehen.

Hab's jetzt wie folgt gelöst:


                protected void Clear<TClass>(object pInstance)
                {
                    foreach (PropertyInfo propertyInfo in
                                typeof(TClass).GetProperties(BindingFlags.Public | BindingFlags.Instance))
                    {
                        #region ModelProperty suchen und Eigenschaft 'Value' auf den Defaultwert setzen
                        if (propertyInfo.PropertyType.IsGenericType &&
                            propertyInfo.PropertyType.GetGenericTypeDefinition() == 
                                                            typeof(ModelProperty<>))
                        {
                            // ModelProperty<T> gefunden, generischen Typen merken
                            Type GenericType = propertyInfo.PropertyType.GetGenericArguments()[0];

                            // Eigentliche ModelProperty holen
                            object child = propertyInfo.GetValue((TClass)pInstance, null);
                            if (child != null)
                            {
                                PropertyInfo childinfo = propertyInfo.PropertyType.GetProperty("Value");

                                // String bekommt eine Sonderbehandlung...
                                if (GenericType.Name.Equals("String"))
                                {
                                    childinfo.SetValue(child, string.Empty, null);
                                }
                                else
                                {
                                    childinfo.SetValue(child, Activator.CreateInstance(childinfo.PropertyType), null);
                                }
                            }                                
                        }
                        #endregion
                    }//foreach (PropertyInfo
                }

17.04.2012 - 14:22 Uhr

Hallo Leute,

ich habe eine generische Klasse die jeweils eine Property meiner Models darstellt (MVVM; im folgenden ModelProperty genannt). Ein Model enthält jeweils eine ganze Scharr dieser ModelProperties.


            public class ModelProperty<T> : INotifyPropertyChanged
            {
                #region Deklarationen
                private T _value;
                /// <summary>
                /// Gibt den Wert wieder oder legt ihn fest
                /// </summary>
                public T Value
                {
                    get
                    {
                        return _value;
                    }
                    set
                    {
                        _value = value;
                        this.ValueChanged("Value");
                    }
                }       
                #endregion

                /// <summary>
                /// Erstellt eine Instanz der Klasse ModelProperty
                /// </summary>
                public ModelProperty()
                {
                }

                /// <summary>
                /// Impliziter Operator der es ermöglicht auf diese Instanz beim Schreiben
                /// wie auf eine normale Property zuzugreifen.
                /// </summary>
                /// <param name="value"></param>
                /// <returns></returns>
                public static implicit operator ModelProperty<T>(T value)
                {
                    return new ModelProperty<T> { Value = value };
                }

                #region INotifyPropertyChanged Member
                public event PropertyChangedEventHandler PropertyChanged;
                protected void ValueChanged(string propertyName)
                {
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
                #endregion
            }//class

In meinem Model möchte ich jetzt via Reflection die .Value-Eigenschaft aller ModelProperties neu initialisieren.


                protected void Clear<TClass>(object pInstance)
                {
                    foreach (PropertyInfo propertyInfo in
                                typeof(TClass).GetProperties(BindingFlags.Public | BindingFlags.Instance))
                    {
                        #region ModelProperty suchen und Eigenschaft 'Value' auf den Defaultwert setzen
                        if (propertyInfo.PropertyType.IsGenericType &&
                            propertyInfo.PropertyType.GetGenericTypeDefinition() == 
                                                        typeof(ModelProperty<>))
                        {
                            // ModelProperty<T> gefunden, generischen Typen merken
                            Type GenericType = propertyInfo.PropertyType.GetGenericArguments()[0];

                            // Eigentliche ModelProperty holen
                            object child = propertyInfo.GetValue((TClass)pInstance, null);

                            if (child != null)
                            {
                                PropertyInfo childinfo = propertyInfo.PropertyType.GetProperty("Value");
                                childinfo.SetValue(child, Activator.CreateInstance(childinfo.PropertyType), null);
                            }                                
                        }
                        #endregion
                    }//foreach (PropertyInfo
                }

Wenn ich ein ModelProperty<string> habe und die Methode Clear() aufrufe bekomme ich bei childinfo.SetValue(child, Activator.CreateInstance(childinfo.PropertyType), null); eine System.MissingMethodException, was auch logisch ist weil die Klasse String ja keinen parameterlosen Konstruktor besitzt.

Nur wie instanziere ich Typen die keinen parameterlosen Konstruktor haben via Reflection?

10.04.2012 - 14:16 Uhr

Danke, dann halt doch mit .Value 🙂

10.04.2012 - 13:45 Uhr

Hallo Leute,

ich möchte eine generische Klasse schreiben, die ich für die Eigenschaften meiner ViewModels verwenden will.

Halbwegs funktioniert das auch schon:


    public class ViewModelProperty<T> : INotifyPropertyChanged
    {
        private T _value;
        /// <summary>
        /// Gibt den Wert wieder oder legt ihn fest
        /// </summary>
        public T Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
                this.ValueChanged("Value");
            }
        }

        public static implicit operator T(ViewModelProperty<T> value)
        {
            return value.Value;
        }

        public static implicit operator ViewModelProperty<T>(T value)
        {
            return new ViewModelProperty<T> { Value = value };
        }


        #region INotifyPropertyChanged Member
        public event PropertyChangedEventHandler PropertyChanged;
        protected void ValueChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

Dank des impliziten Operators funktioniert folgendes:


ViewModelProperty<string> myproperty;

myproperty = "test";

Wenn ich den Wert allerdings mit myproperty (ohne .Value) abfragen möchte, bekomme ich immer das ganze Konstrukt wieder:


{Splash.ViewModelProperty<string>}
    _value: "test"
    PropertyChanged: null
    Value: "test"

Hintergrund: Ich möchte beim Databinding nicht immer

{Binding myproperty.Value}

schreiben müssen, sondern nur

{Binding myproperty}

Der folgende Block wird gar nicht durchlaufen und scheint falsch zu sein:


        public static implicit operator T(ViewModelProperty<T> value)
        {
            return value.Value;
        }

Habt Ihr einen Tipp für mich, warum das so nicht funktioniert?

26.03.2012 - 12:17 Uhr

~~a ansonsten rein theoretisch Jobs mit einer niedrigen Priorität nie ausgeführt würden - das wäre ja schade. 😃
@hypersurf: Ist das nicht auch das Ziel?

Ja, das ist das Ziel. Jeder Job mit höherer Priorität sticht einen Job mit niedrigerer Priorität aus.[/color]

Ich denke ich werde den Vorschlag mit der eigenen Queue aufgreifen die immer das Element mit der höchsten Prioritäten zuerst zurück gibt.

Danke für Eure Antworten, die mir sehr geholfen haben 😃

23.03.2012 - 13:02 Uhr

Hallo Leute,

ich habe einen Windowsdienst in dem 100 Threads laufen und gleichzeitig Datenpakete per FTP an mehrere hundert Computer versenden.

Dazu habe ich die geniale SyncQueue von herbivore verwendet: SyncQueue <T> - Eine praktische Job-Queue
Diese Klasse hat mir einiges an Arbeit abgenommen 👍

Jetzt wurde eine neue Anforderung ein den Dienst gestellt:

Alle Jobs in der SyncQueue haben die Attribute Computer-ID und Priorität. Die Priorität ist ein Integer. 0 stellt eine niedrige Priorität dar, 9 eine hohe.
Beim Abarbeiten der Jobs soll geprüft werden, ob zur sich gleichen Computer-ID noch Jobs mit einer höheren Priorität in der SyncQueue befinden oder ein anderer Thread evtl. gerade einen Job mit höherer Priorität verarbeitet.
Ist dies der Fall, darf der Job mit der niedrigeren Priorität erst verarbeitet werden, wenn der Job mit der höheren Priorität vollständig verarbeitet wurde.

In einem separaten Thread werden ständig weitere Jobs in die Queue gestellt die dann abgearbeitet werden. Diese neuen Jobs können natürlich wieder eine höhere oder niedrigere Priorität haben, als die die schon in der Queue vorhanden sind und auf ihre Abarbeitung warten.

So ist es bisher / So soll es sein:
Siehe Anhang

Lässt sich das mit der SyncQueue bewerkstelligen?
Habt Ihr vielleicht einen Denkanstoss für mich wie sich diese Anforderung vernünftig realisieren lässt? Bin noch relativ unerfahren was Multi-Threading betrifft und wäre für einen Tipp dankbar =)

21.03.2012 - 13:55 Uhr

Hallo Leute,

ich möchte in meinem Programm ein Raster generieren in dessen Zellen dann per Drag&Drop Elemente eingefügt werden sollen.

Da das Gridlayout ja genau so ein Raster liefert, möchte ich dieses dafür verwenden:


    <Page.Resources>
        <DataTemplate DataType="{x:Type local:ViewModelRasterElement}">
            <TextBlock Text="{Binding Bezeichnung}"/>
        </DataTemplate>
    </Page.Resources>


<ItemsControl ItemsSource="{Binding RasterElemente}">
                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Grid.Row" Value="{Binding GridRow}" />
                        <Setter Property="Grid.Column" Value="{Binding GridColumn}" />
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid ShowGridLines="True">
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>

Das ViewModel für die Elemente des Rasters:


    public class ViewModelRasterElement : BaseViewModel
    {
        private int _GridColumn;
        /// <summary>
        /// Spalte im Raster
        /// </summary>
        public int GridColumn
        {
            get { return _GridColumn; }
            set {
                _GridColumn = value;
                this.ValueChanged("GridColumn");
                }
        }

        private int _GridRow;
        /// <summary>
        /// Zeile im Raster
        /// </summary>
        public int GridRow
        {
            get { return _GridRow; }
            set {
                _GridRow = value;
                this.ValueChanged("GridRow");
                }
        }

        private string _Bezeichnung;
        public string Bezeichnung
        {
            get { return _Bezeichnung; }
            set { 
                    _Bezeichnung = value;
                    this.ValueChanged("Bezeichnung");
                }
        }

Auszug aus dem ViewModel welches die RasterElemente enthält:


        private ObservableCollection<ViewModelRasterElement> _RasterElemente;
        public ObservableCollection<ViewModelRasterElement> RasterElemente
        {
            get { return _RasterElemente; }
            set { 
                    _RasterElemente = value;
                    this.ValueChanged("RasterElemente");
                }
        }

        private void GenriereRasterElemente()
        {
            for (int zeile = 1; zeile <= this.RasterZeilen; zeile++)
            {
                for (int spalte = 1; spalte <= this.RasterSpalten; spalte++)
                {
                    ViewModelRasterElement element = new ViewModelRasterElement();
                    element.GridColumn = spalte - 1;
                    element.GridRow = zeile - 1;
                    element.Bezeichnung = "asdlkjaljksdf";
                    this.RasterElemente.Add(element);
                }
            }
        }

Das Generieren der Rasterelemente funktioniert soweit problemlos und alle Elemente werden in den richtigen Zellen angezeigt.

Jetzt zu meinen diversen Problemen/Fragen:

  • Ist es möglich, die Grid.RowDefinitions und Grid.ColumnDefinitions dynamisch und MVVM-konform zu erzeugen? Ich möchte die Größe des Rasters anhand bestimmter Kriterien vorgeben können.

  • Drag&Drop einzubinden ist generell kein Problem, ich habe nur gesehen, dass ich nicht direkt in die Gridzellen draggen kann, sondern dazu erst ein Element (z. B. irgendein Panel) in der Zelle sein muss auf das ich dann droppen kann. Ist das korrekt oder habe ich etwas übersehen?

19.03.2012 - 14:57 Uhr

Das war des Rätsels Lösung:


GC.Collect();
GC.WaitForPendingFinalizers();
07.03.2012 - 08:47 Uhr

Hallo Leute,

wir haben eine COM-Komponente in C# (Framework 4.0) geschrieben die in einem Visual Basic 5.0-Programm aufgerufen wird. Das funktioniert auch prima.

Auf einem Rechner (WinXP Pro SP3) bekommen wir, nach Beenden des Visual Baisc-Programms und vorherigem Aufruf der COM-Komponente, folgende Fehlermeldung:

Fehlermeldung:
.NET-BroadcastEventWindow.2.0.0.0.bce5ad.0

Die Anweisung in "0x7c91ff56" verweist auf den Speicher in "0x00000586". Der Vorgang "read" konnte nicht auf den Speicher durchgeführt werden.

Diese Meldung kommt aber nur auf einem einzigen PC. Auf allen anderen Rechnern hatten wir diesen Fehler bisher nicht.

Google habe ich natürlich schon bemüht, aber diese Problematik kommt so oft vor, dass es ziemlich schwierig ist eine Lösung zu finden. Lösungen die ich gefunden habe waren z. B. Neuinstallationen des Betriebssystems oder Austausch von Hardware.

Habt Ihr Erfahrungen mit dieser Fehlermeldung? Vielleicht speziell in Zusammenhang mit ".NET-BroadcastEventWindow.2.0.0.0.bce5ad.0"??

02.12.2011 - 10:30 Uhr

Das direkte Binden einer ReadOnly-Property geht trotz des OneWayToSource nicht:


ActualWidth="{Binding Path=TitleWidth, Mode=OneWayToSource}"
ActualHeight="{Binding Path=TitleHeight, Mode=OneWayToSource}"

Die Eigenschaft "ActualWidth" ist schreibgeschützt und kann nicht von einem Markup aus festgelegt werden.

Dieses Verhalten macht doch keinen Sinn, wenn der Binding-Mode schon auf OneWayToSource steht. Das scheint ein Bug vom Framework zu sein: Siehe hier

Edit sagt: Hier ist die Lösung die ich nun verwende

01.12.2011 - 17:05 Uhr

Hallo Leute,

ich arbeite mit dem 3.5er Framework zur Zeit an einem Editor, in dem der Benutzer verschiedene grafische Elemente verschieben, verändern und miteinander verbinden kann.

Leider funktionieren einige Databindings nicht und ich kann nicht nachvollziehen warum nicht. Vielleicht habt Ihr eine Idee 😃

Die Oberfläche enthält verschiedene Objekte, die alle in einer Collection meines ViewModels enthalten sind. Über DataTemplates werden die verschiedenen Objekte in dem ItemsControl dargestellt:


<ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Hidden">
                    <ItemsControl ItemsSource="{Binding Path=VisualItems}"
                                  x:Name="ItemsContainer">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Canvas AllowDrop="true"
                                        x:Name="MyCanvas"
                                        Loaded="MyCanvas_Loaded"
                                        Background="White"
                                        Height="10000"
                                        PreviewMouseLeftButtonDown="MyCanvas_PreviewMouseLeftButtonDown"
                                        PreviewMouseLeftButtonUp="MyCanvas_PreviewMouseLeftButtonUp"
                                        PreviewMouseMove="MyCanvas_PreviewMouseMove">
                                    
                                        <Canvas.LayoutTransform>
                                            <ScaleTransform ScaleX="{Binding Path=CurrentZoomfaktor}"
                                                            ScaleY="{Binding Path=CurrentZoomfaktor}"/>
                                        </Canvas.LayoutTransform>
                                    </Canvas>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>

                        <ItemsControl.ItemTemplateSelector>
                            <local:ControlTemplateSelector/>
                        </ItemsControl.ItemTemplateSelector>

                        <ItemsControl.ItemContainerStyle>
                            <Style TargetType="{x:Type ContentPresenter}">
                                <Setter Property="Canvas.Left" Value="{Binding Path=X, Mode=TwoWay}"/>
                                <Setter Property="Canvas.Top" Value="{Binding Path=Y, Mode=TwoWay}"/>
                            </Style>
                        </ItemsControl.ItemContainerStyle>
                    </ItemsControl>
                </ScrollViewer>


Das betroffene Datatemplate (es geht speziell um die Breite und Höhe des Borders):

Width="{Binding Path=TitleWidth, Mode=OneWayToSource}"
Height="{Binding Path=TitleHeight, Mode=OneWayToSource}"


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:local="clr-namespace:Workflowdesigner"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <local:PointsCollectionConverter x:Key="converter"/>
    <local:TextToVisibilityConverter x:Key="TextVisibleConverter"/>
    
    <DataTemplate x:Key="Connections.TwoPoints">
        <Canvas Height="0" Width="0">
            <Polygon Stroke="Black" 
                     StrokeLineJoin="Round"
                     Points="{Binding Path=PointsLinie, Converter={StaticResource converter}}"/>
            <Polygon Stroke="Red" Fill="Red" 
                     Points="{Binding Path=PointsDreieck, Converter={StaticResource converter}}"/>
            
            <!-- OneWayToSource sendet den Wert immer nur zur gebundenen Instanz zurück -->
            <Border Canvas.Left="{Binding Path=TitleX}"
                    Canvas.Top="{Binding Path=TitleY}"
                    Width="{Binding Path=TitleWidth, Mode=OneWayToSource}"
                    Height="{Binding Path=TitleHeight, Mode=OneWayToSource}"
                    BorderBrush="#5F5F5F"
                    Background="#E9E9E9"
                    BorderThickness="1"
                    Visibility="{Binding Path=Title, Converter={StaticResource TextVisibleConverter}}">

                <TextBlock Text="{Binding Path=Title}"
                           Margin="5"
                           FontWeight="Bold"
                           Background="#E9E9E9">
                </TextBlock>
            </Border>
        </Canvas>
    </DataTemplate>
</ResourceDictionary>

Wenn ich jetzt die Attribute TitleWidth und TitleHeight in meinem ViewModel abfrage, sind diese beim ersten Mal 0,00 beim zweiten Mal NaN.


        private double _TitleWidth;
        public double TitleWidth
        {
            get { return _TitleWidth; }
            set { 
                    _TitleWidth = value;
                    this.ValueChanged("TitleWidth");
                }
        }

        private double _TitleHeight;
        public double TitleHeight
        {
            get { return _TitleHeight; }
            set { 
                    _TitleHeight = value;
                    this.ValueChanged("TitleHeight");
                }
        }

Wenn ich den Inhalt (also auch die Breite des Textblocks ändere) müsste die das View die beiden Werte ja an das ViewModel zurückmelden. Tut es aber nicht.


            this.TitleX = this.StartpunktX + (this.EndpunktX - this.StartpunktX) - this.TitleWidth / 2;
            this.TitleY = this.StartpunktY + (this.EndpunktY - this.StartpunktY) - this.TitleHeight / 2;

Implementierung der INotifyPropertyChanged-Schnittstelle ist selbstverständlich vorhanden und funktioniert auch bei den anderen Attributen.


INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void ValueChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

Habt Ihr eine Idee woran das liegen könnte?

25.11.2011 - 19:13 Uhr

Danke für die perfekte Erklärung 😃

25.11.2011 - 14:01 Uhr

Hallo Leute,

ich möchte gerne einen Pfeil (bestehend aus einer Linie und einem Dreieck) mit Polygonen zeichnen. Das ist soweit kein Problem:


    <Canvas Height="600" Width="400">
        <Polygon Stroke="Black">
            <Polygon.Points>
                <Point X="201" Y="135.0"/>
                <Point X="395.0" Y="135.0"/>
            </Polygon.Points>
        </Polygon>

        <Polygon Stroke="Red">
            <Polygon.Points>
                <Point X="395.0" Y="135.0"/>
                <Point X="377.0" Y="145.0"/>
                <Point X="377.0" Y="126.0"/>
            </Polygon.Points>
        </Polygon>
    </Canvas>

Komischerweise steht die Linie über, obwohl der rechte äußere Punkt des Dreiecks exakt dieselben Koordinaten hat wie der rechte Verbindungspunkt der Linie.

Habt Ihr eine Idee woran das liegen könnte? 🙂

02.09.2011 - 12:57 Uhr

Lösung des Problems: Das Binding des Frames anpassen (TwoWay)


            <Frame VerticalAlignment="Center" HorizontalAlignment="Center" 
                   Visibility="{Binding SubWindowAktiv, Converter={StaticResource VisibilityOfBoolConverter}}"
                   Content="{Binding Path=SubWindowContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                   NavigationUIVisibility="Hidden"/>

01.09.2011 - 11:10 Uhr

Ich habe noch ein ganz seltsames Phänomen. Ich benutze das IDataErrorInfo-Interface zur Anzeige von Eingabefehlern. Dazu habe ich ein entsprechendes Template für die Textboxen geschrieben:


        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel LastChildFill="True" Margin="3">
                        <!-- Rotes Ausrufezeichen erzeugen (rechts angedockt) -->
                        <TextBlock DockPanel.Dock="Right" Foreground="White" FontSize="14" FontWeight="Bold" Background="Red" Padding="3" Text="!"
                                ToolTip="{Binding ElementName=Control_AdornedElementPlaceholder, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                        </TextBlock>
                        <!-- Roten Rahmen um das Steuerelement erzeugen (mit dem Element den Rest ausfüllen) -->
                        <Border BorderBrush="Red" BorderThickness="2">
                            <AdornedElementPlaceholder Name="Control_AdornedElementPlaceholder"/>
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

Dieses Template zeichnet einen roten Rahmen um die Textboxen mit den fehlerhaften Eingaben. Wenn ich per Abbrechen das Frame wieder ausblende, bleiben die roten Rahmen stehen (siehe Anhang).

Das MainViewModel enthält eine Methode die beim Klick auf Abbrechen der Eingabepage aufgerufen wird, und die Page ausblendet:


        public void HideSubWindow()
        {
            this.SubWindowAktiv = false;
            this.SubWindowContent = null;
        }

Das Frame welches die Page enthält:


            <Frame VerticalAlignment="Center" HorizontalAlignment="Center" 
                   Visibility="{Binding SubWindowAktiv, Converter={StaticResource VisibilityOfBoolConverter}}"
                   Content="{Binding Path=SubWindowContent}"/>

Habt Ihr eine Idee woran das liegen könnte?

01.09.2011 - 10:41 Uhr

Danke, genau das meinte ich 🙂

Da hätte ich auch selber drauf kommen können 😭

01.09.2011 - 09:47 Uhr

Hi Leute,

ich habe eine Anwendung die aus einem einzigen Fenster besteht. Da ich keine Messageboxen oder sonstige modale Fenster anzeigen möchte, möchte ich gerne über einem GridView ein Frame einblenden, in welchem ich dann wieder eine Page anzeige. Das funktioniert auch soweit tadellos.

Jetzt zu meinem Problem:

Nachdem die Eingabe auf der Page in dem Frame abgeschlossen wurde, bestätigt der User seine Eingaben mit "OK". Wie erhalte ich in meinem ViewModel des Hauptfensters jetzt die Nachricht, dass die Eingaben abgeschlossen wurden? Ich muss die eingegeben Daten ja auch an das ViewModel des Hauptfensters zurückgeben.

Das MainViewModel besitzt eine Eigenschaft die den Inhalt des Frames wieder spiegelt:


        private Page _SubWindowContent;
        public Page SubWindowContent
        {
            get { return _SubWindowContent; }
            set
            {
                _SubWindowContent = value;
                this.ValueChanged("SubWindowContent");
            }
        }

In einem Command wird dann der Content des Frames geladen:


            NetzwerkDatenEingabeViewModel model = new NetzwerkDatenEingabeViewModel();
            NetzwerkdatenEingabe maske = new NetzwerkdatenEingabe();
            maske.DataContext = model;
            this.SubWindowContent = maske;

Ein kurzer Tip in die richtige Richtung würde mich schon reichen, stehe irgendwie mal wieder auf dem Schlauch gerade 🤔

25.08.2011 - 10:09 Uhr

Ich muss zugeben, dass ich die Funktion nicht geschrieben habe, sondern ein Kollege. Allerdings hab' ich gestern Abend auch den Wald vor lauter Bäumen nicht mehr gesehen, sonst wären mir diese Fehler sicher aufgefallen.

Danke Euch für die Antworten 😃

24.08.2011 - 17:18 Uhr

Hi Leute,

ich erstelle mittels folgender Funktion eine ZIP-Datei. Die Datei wird auch korrekt erstellt und lässt sich ohne Probleme extrahieren. Leider kann ich, solange mein Programm noch läuft, nicht mehr auf die ZIP-Datei zugreifen (auch nicht lesen) weil sie "von einem anderen Prozess verwendet wird".


public static bool WriteZipFile(List<string> pFilesToZip, string pPath, int pCompression)
                    {
                        if (pCompression < 0 || pCompression > 9) pCompression = 5;
                        if (!Directory.Exists(new FileInfo(pPath).Directory.ToString())) return false;

                        foreach (string c in pFilesToZip)
                            if (!File.Exists(c)) return false;

                        bool retVal = true;

                        try
                        {
                            using (ZipOutputStream stream = new ZipOutputStream(File.Create(pPath)))
                            {
                                stream.SetLevel(pCompression);

                                for (int i = 0; i < pFilesToZip.Count; i++)
                                {
                                    ZipEntry entry = new ZipEntry(Path.GetFileName(pFilesToZip[i]));
                                    entry.DateTime = DateTime.Now;

                                    entry.Size = File.Open(pFilesToZip[i], FileMode.OpenOrCreate,
                                                 FileAccess.Read, FileShare.Read).Length;
                                    stream.PutNextEntry(entry);

                                    byte[] buffer = new byte[4096];
                                    using (FileStream fs = File.OpenRead(pFilesToZip[i]))
                                    {
                                       StreamUtils.Copy(fs, stream, buffer);
                                    }
                                }
                                stream.Finish();
                                stream.Close();
                            }
                        }
                        catch (Exception)
                        {
                            retVal = false;
                        }
                        return retVal;
                    }

Ich benutze diese Funktion mehrfach und wenn innerhalb desselben Threads versucht wird die erstellte ZIP-Datei zu lesen funktioniert das auch. Sobald ich es aus einem anderen Thread versuche kommt die System.IO.IOException.

Habt' Ihr eine Idee woran das liegen könnte?

08.08.2011 - 16:20 Uhr

Hallo Leute,

ich muss auf einem Windows CE 6-Gerät die IP-Adresse aus meiner Anwendung heraus lesen und ändern. Das Auslesen der Netzwerkdaten aus der Registrierung funktioniert soweit tadellos.

Leider lassen sich aufgrund des Sicherheitskonzepts von Windows CE bestimmte Einträge/Schlüssel in der Registrierung nicht ändern. Siehe dazu auch Protected Registry Keys and Values

Das Framework wirft beim Versuch den entsprechenden Schlüssel in der Registry zu ändern eine UnauthorizedAccessException.

Nach einigem googlen habe ich herausgefunden, dass die Anwendung von Windows CE wohl als vertrauenswürdig anerkannt werden muss. Habt Ihr eine Idee wie ich meine Anwendung als vertrauenswürdig kennzeichnen kann? Muss die Anwendung mit einem Zertifikat signiert werden?

Google und diverse Newsgroups haben mir nicht so richtig weitergeholfen. Der Support vom Lieferanten des Gerätes hatte leider auch Erfahrung damit...

07.08.2011 - 16:15 Uhr

Das hört sich doch genau passend an. Danke 😃

07.08.2011 - 15:50 Uhr

Hi Leute,

ich bräuchte ein paar Tips von den Experten hier wie ich folgendes umsetzen kann:

Es soll ein Dienst entwickelt werden der Daten per FTP an hunderte anderer Rechner überträgt. Dabei müssen folgende Voraussetzungen erfüllt werden:

  • Die Übertragung soll automatisch nach einem Zeitplan erfolgen aber auch manuell über eine GUI anstoßbar sein
  • Welche Übertragungen gerade wohin laufen soll in der GUI sichtbar sein
  • Man muss über die GUI dem Dienst befehle geben können (Übertragungen stoppen, neustarten, etc.)

Wie realisiere ich eine GUI die mit einem Windowsdienst kommuniziert und umgekehrt? Ich möchte keine Codeauszüge oder ähnliches, ein paar Stichworte die mir bei meiner Recherche helfen wären klasse 8)

18.07.2011 - 17:05 Uhr

Hi Leute,

ich entwickle zur Zeit einen Windows-Dienst in dem mehrere System.Threading.Timer laufen. Die Verarbeitung innerhalb der einzelnen Timer kann zum Teil mehrere Minuten dauern (im Livebetrieb später auch auch mal 20 - 30 Minuten).

Wie sollte sich der Dienst normalerweise verhalten wenn er beendet werden soll (OnStop), aber die Verarbeitung noch läuft? Wird die Anwendung irgendwann von der Windows-Dienststeuerung terminiert, so dass ich schnellstmöglich alle noch laufenden Aktivitäten beenden muss? Oder kann ich die Timer deaktivieren, so dass die aktuellen Arbeiten noch abgeschlossen werden können?
Was passiert wenn Windows heruntergefahren wird?

Vielen Dank & Gruß aus Münster

06.06.2011 - 10:26 Uhr

Der Pfad beinhaltet nur

G:\\NET\\XXX.NET\\Serveranwendung\\Beispieldaten\\beispielredu XXX\\Bilder\\084045.jpg  

Ich vermute, dass es was damit zu tun hat, dass die Anwendung als Dienst läuft. Zwar habe ich den Dienst gerade auch mal als Domänenadmin laufen lassen, funktioniert aber trotzdem nicht.

Liegen die Dateien lokal, klappts :>

06.06.2011 - 10:15 Uhr

Die Eigenschaft liefert False zurück.

Wenn ich versuche die Datei per File.Copy zu kopieren gibt es eine DirectoryNotFoundException:

Ein Teil des Pfades &quot;G:\NET\XXX.NET\Serveranwendung\Beispieldaten\beispielredu XXX\Bilder\084045.jpg&quot; konnte nicht gefunden werden.

Der Pfad beinhaltet nur

G:\\NET\\XXX.NET\\Serveranwendung\\Beispieldaten\\beispielredu XXX\\Bilder\\084045.jpg

Die Backslashes vorne und hinten fügt die Meldung der Exception anscheinend ein.

06.06.2011 - 09:57 Uhr

Hi Leute,

ich habe ein seltsames Phänomen was ich mir nicht erklären kann.
Ich frage einer Methode ab ob die Datei die gelesen werden soll überhaupt existiert.


if (File.Exists(pFilename))

Komischerweise gibt mir die Funktion immer False wieder. Die Dateien die abgefragt werden existieren aber. Kopiere ich den Inhalt von pFilename per Direktfenster 1:1 in die Zwischenablage und rufe diesen dann per Start -> Ausführen auf, wird die Datei direkt geöffnet.

Die Datei liegt auf einem Netzlaufwerk, welches über den Laufwerksbuchstaben angesprochen wird. Im Arbeitsplatz wird das Laufwerk auch korrekt und als verbunden angezeigt. Zugriff ist ohne Einschränkungen möglich.

Habt Ihr eine Idee, woran das liegen könnte?

Folgendes funktioniert übrigens tadellos:


FileInfo fi = new FileInfo(pFilename);
27.05.2011 - 11:55 Uhr

Da hab' ich wohl den Wald vor lauter Bäumen nicht gesehen. Danke 😉

PS: Mein Dienst schreibt ne entsprechende Meldung für den Admin ins Ereignisprotokoll 8)

27.05.2011 - 11:35 Uhr

Hi Leute,

ich habe einen selbstgeschriebenen Dienst den ich beenden möchte.
D. h. beim Starten des Dienstes werden bestimmte Prüfungen vorgenommen. Schlägt eine dieser Prüfungen fehl, soll sich der Dienst in der OnStart-Methode direkt wieder beenden bzw. den Startvorgang abbrechen.

Wenn ich das wie folgt versuche

Environment.Exit(0);

bekomme ich im Ereignisprotokoll immer noch den Eintrag:

Fehlermeldung:
Der Dienst kann nicht gestartet werden. Der Dienstprozess konnte keine Verbindung zum Dienstcontroller herstellen

Wie lässt sich der Dienst beim Starten sauber beenden bzw. der Start abbrechen? Eine Abort-Methode oder ähnliches konnte ich nicht entdecken.

20.04.2011 - 16:44 Uhr

Hallo Leute,

ich möchte einen Dialog realisieren in dem der User sich jederzeit entscheiden kann, die ganze Bearbeitung abzubrechen. D. h. beim Abbrechen sollen alle gemachten Änderungen nicht übernommen werden.

Der Dialog ist ein modales Fenster (und natürlich selber auch ein ViewModel) welches aus einem ViewModel aufgerufen wird. Ich könnte den UpdateSourceTrigger auf Explicit setzen (geht das bei allen Standardcontrols?), aber wie behandel ich das im ViewModel?

Auf der Microsoft-Seite habe ich folgendes Beispiel gefunden (Binding.UpdateSourceTrigger-Eigenschaft (System.Windows.Data))


<TextBox Name="itemNameTextBox"
         Text="{Binding Path=ItemName, UpdateSourceTrigger=Explicit}" />


// itemNameTextBox is an instance of a TextBox
BindingExpression be = itemNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();

Habt Ihr einen Tip wie ich die BindingExpression im ViewModel erzeugen kann? Wie löst man diesen Anwendungsfall im MVVM-Pattern?

20.04.2011 - 15:59 Uhr

Hallo Leute,
Das im Endeffekt aus einem Button und einer ListBox in einem Window (XAML) eingebettet ist. Nun habe ich versucht, die Logik aus der View zu nehmen und im ViewModel zu pflanzen. Allerdings habe ich leider keine Idee, wie ich mittels Command (vom Interface ICommand) ein Control im XAML anspreche. Habe ich die Möglichkeit ein Property via Command mit Abzufragen oder gar auf Properties von einem Control anzusprechen. Lange rede kurzer Sinn, hier der Code.

Commands dienen letztendlich dazu Aktionen auszuführen. Im MVVM-Pattern verwendet man (eigentlich) keine Events mehr.

Beispiel:

<Button Click="Button_Click"/>

Anstatt des Events verwendet man ein Command welches anstatt des Events ausgelöst wird.

<Button Command="{Binding Name_des_Commands_im_ViewModel}"/>

In der CanExecute-Methode gibst Du an, ob das Command ausgeführt werden kann. Gibt die Methode False wieder, wird in diesem Fall auch der Button automatisch auf IsEnabled=False gesetzt.

Properties der Controls kannst Du an Dein ViewModel binden, wie Du es in Deinem PersonView mit den Textblöcken schon gemacht hast.

Beachte, dass View und ViewModel nichts voneinander wissen und nur durch das DataBinding miteinander kommunizieren.

20.04.2011 - 12:48 Uhr

Geil das klappt. Vielen Dank Euch beiden 8)

20.04.2011 - 11:26 Uhr

Hallo Leute,

ich habe schon viel über RoutedCommands gelesen, finde aber irgendwie keinen passenden Lösungsansatz für mein Problem.

Meine View beinhaltet ein ItemsControl. In dem ItemsControl werden mittels Templates & TemplateSelector unterschiedlichste Elemente aus einer Collection des ViewModels dargestellt.

Jetzt muss ich auf einen Buttonclick eines Elements aus dieser Collection reagieren. Wie schleife ich den ButtonClick bis zu meinem ViewModel durch? Ich möchte keinen fertigen Code, sondern nur ein paar Ansätze/Anregungen wie man dieses Problem lösen kann 🙂

Danke 🙂

04.04.2011 - 10:24 Uhr

Falls jemand mal das gleiche Problem hat.

Folgende Codezeile löst die CanExecuted-Prüfung manuell aus:


CommandManager.InvalidateRequerySuggested();

Siehe auch http://msdn.microsoft.com/en-us/library/system.windows.input.commandmanager.invalidaterequerysuggested.aspx