• Noser.com
facebook
linkedin
twitter
youtube
  • NOSERmobile
    • Android
    • HTML 5
    • Hybrid Apps
    • iOS
    • Windows Phone
  • NOSERembedded
    • Medizintechnik
  • NOSERprojektmanagement
  • NOSERtesting
  • NOSERlifecycle
    • .NET Allgemein
    • Application Lifecylce Management
    • Apps
    • Architektur
    • ASP.NET
    • Azure
    • Cleancode
    • Cloud
    • Silverlight
    • Visual Studio / Team Foundation Server
    • Windows 8
    • Windows Presentation Foundation
  • NOSERinnovation
    • Big Data
    • Cloud
    • IoT
    • Operations Research
    • Augmented Reality
    • RFID, NFC, Bluetooth LE

Debugging von WPF Data- Bindings

20. Januar 2013
Andreas Schicker
Off
Data Binding, Debugging, WPF

Schon öfters wurde ich bezüglich eines nicht richtig funktionierenden WPF Data-Binding zur Hilfe gebeten. Ebenfalls tauchen auch immer wieder auf verschiedenen Foren wie Stackoverflow.com Fragen dazu auf.

Hier ein paar Tipps zum Debuggen von Data-Binding Fehlern:

TraceLevel erhöhen

Normalerweise erscheinen im Debug Output Window nur sehr wenige Infos bezüglich Data-Bindings. Um detaillierte Informationen (z.B. auch Value-Änderungeng bei einem funktionierenden Binding) zu erhalten kann man ganz einfach den TraceLevel auf ‘High’setzen.

Beispiel:

<TextBox Text=“{Binding Path=TextA, PresentationTraceSources.TraceLevel=High}”/>

Die meisten Binding-Fehler werden damit sehr schnell ersichtlich.

Ps.: Nicht vergessen das TraceLevel- Property wieder zu löschen. Erstens werden sonst später Unmengen unnötiger Outputs generiert und zweitens wird der Debugger merklich langsamer.

Falsch initialisierte Dependency Properties

Dieser Error bezieht sich zwar nicht direkt auf Data-Bindings, führt aber sehr oft zu nicht funktionierenden Bindings. Ein Auge sollte man auf die Registrierung des DependencyProperties werfen.

Beispiel:

internal class TestClass
{
public static readonly DependencyProperty SomeStringProperty = DependencyProperty.Register(“SomeString”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));
}

Als dritten Parameter der oben beschriebenen Register Methode muss der OwnerType übergeben werden. Mit wenigen Ausnahmen ist dies wie der Name schon sagt der Typ in welchem das Property definiert wurde (wie hier TestClass).

In Folge von Flüchtigkeitsfehlern (Copy-Paste, Verschieben des Dependency Properties von einer Klasse in eine andere) passiert es nun leider öfters, dass der OwnerType falsch gesetzt ist. Oftmals tritt erst zur Laufzeit ein Problem auf und die geworfenen Exceptions deuten nicht immer auf das wirkliche Problem hin.

Ps.: Visual Studio oder Programme wie ReSharper geben keine Warning aus.<!> Ich hab mir ein kleines Programm geschrieben, welches alle Klassen nach Dependency Properties durchforstet die den falschen “OwnerType” haben.

Binding Chains

Als letztes noch einen Tipp zu einem Problem welches zwar eher selten auftritt, jedoch auch ganz hilfreich für das Verständnis bezüglich Data-Bindings ist.

Nehmen wir an, wir haben ein TextBox-Control als Eingabefeld (tb1).

XAML:

<TextBox x:Name=“tb1″/>

Nun möchten wir den Value des TextBox.Text- Properties mit zwei weiteren Properties über Data-Binding verbinden. Die Bindings sollen aber nicht im XAML gemacht werden sondern mittels Code:

internal class TestClass
{
public static readonly DependencyProperty StringAProperty = DependencyProperty.Register(“StringA”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));
public static readonly DependencyProperty StringBProperty = DependencyProperty.Register(“StringB”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));

public TestClass()
{
// Binding für das erste Property:
var binding1 = new Binding(“StringA”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding1);

// Binding für das zweite Property:
var binding2 = new Binding(“StringB”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding2);
}
}

Wenn wir das Beispiel nun laufen lassen, stellen wir fest, dass die Bindings nicht funktionieren. Aber warum?

Grundsätzlich kann einem Dependency Property nur ein Data-Binding attached werden. Oben im Code wird aber dem TextProperty der TextBox tb1 zweimal ein Binding gesetzt. Ein SetBinding() löscht aber als erstes immer das schon bestehende Binding.

Deklariert man die Bindings stattdessen im XAML ist es offensichtlich, dass nicht zwei Bindings gesetzt werden können:

<!–Es kann nur ein Property für das Binding verwendet werden–>
<TextBox x:Name=”tb1″ Text=“{Binding StringA}“/>

Lösung:

In diesem speziellen Beispiel könnte man natürlich ein MultiBinding verwenden. Als alternativen Weg könnte man nun aber das eine Binding ‘verdrehen’ (was zu einer sogenannten ‘Binding Chain’ führt):

// Binding für das erste Property:
var binding1 = new Binding(“StringA”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding1);

// Binding für das zweite Property:
var binding2 = new Binding(“Text”) { Source = tb1, Mode = BindingMode.OneWay };
SetBinding(TestClass.StringBProperty, binding2);

Somit haben wir jetzt nur noch je ein Binding ‘attached’.

Ps.: Zu beachten gilt es hier, dass dieser Weg nur funktioniert, wenn die Properties (hier StringPropertyA und StringPropertyB) Dependency Properties sind, da dies für Bindings zwingend erforderlich ist.

WinForms meets WPF meets WinForms meets WPF

16. Januar 2013
Andreas Schicker
0
.NET, WinForms, WPF

Modaler Printdialog- Aufruf in einer gemischten WPF/WinForms Anwendung

Ausgangslage:

Eine bestehende WinForms Applikation soll um eine Teilkomponente erweitert werden. Da die neue Komponente grafisch anspruchsvolle Inhalte darstellen soll (dynamisch skalierbares Diagramm), wird dessen Benutzeroberfläche mit WPF realisiert.

Die Einbettung von WPF UserControls in einer WinForms Applikation an sich stellt keine Probleme dar, da diese Problematik durch Framework Komponenten wie folgt gelöst werden kann.

Host Applikation (WinForms):

namespace WinFormsHost
{
   using System.Windows.Forms;
   using WpfControlLibrary;

   public partial class MainWinForm : Form
   {
      public MainWinForm()
      {
         InitializeComponent();

         // elementHost is defined & initialized in MainWinForm.Designer.cs file:
         // private System.Windows.Forms.Integration.ElementHost elementHost;
         // this.elementHost = new System.Windows.Forms.Integration.ElementHost();
         elementHost.Child = new ModalPrintDialogControl();
      }
   }
}

Eingebettetes WPF UserControl (ModalPrintDialogControl):

XAML:

"WpfControlLibrary.ModalPrintDialogControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    

und dazugehörendes code behind cs.-file:

namespace WpfControlLibrary
{
   using System.Windows;
   using System.Windows.Controls; 

   public partial class ModalPrintDialogControl : UserControl
   {
      public ModalPrintDialogControl()
      {
         InitializeComponent();
      }
   }
}

Natürlich liegt es nahe, dass die neue Komponente auch eine Printfunktion enthalten muss. Auch da bietet das Framework einfache Mittel, um grafische WPF Elemente auszudrucken:

Anpassung im XAML:


       

Anpassung im code behind file:

private void ButtonPrintClicked(object sender, RoutedEventArgs e)
{
   var printDialog = new PrintDialog();
   printDialog.ShowDialog();

   // prints the whole representation of the WPF Control
   printDialog.PrintVisual(this, "My Diagram");
}

ShowDialog() ruft den Printdialog modal auf. Das heisst, währendem der Printdialog geöffnet ist, soll die restliche Applikation (sowohl die neue WPF Komponente wie auch die schon bestehende WinForms Hostapplikation) gesperrt sein.

Und hier kommen wir zum eigentlichen Problem:

Während dieser modale Printdialog Aufruf in reinen WPF Applikationen problemlos funktioniert, erscheint der Printdialog im beschriebenen Scenario als nicht modaler Dialog. Dies hat zur Folge, dass trotz des geöffneten Printdialoges die Applikation selber weiter bedienbar ist und somit theoretisch ein mehrfaches gleichzeitiges Aufrufen des Printdialoges ermöglicht wird:

Bild eines mehrfachen Prind dialog aufrufes

 

 

 

 

 

 

 

 

Lösungsansätze:

Natürlich ist dieses Verhalten nicht zu gebrauchen und somit ist ein neuer Lösungsansatz nötig um den Printdialog modal aufrufen zu können:

Lösungsansatz 1:

Da das eigentliche Problem ist, dass die Hostapplikation den Printdialog nicht als modalen Dialog erkennt, könnte statt des oben verwendeten Printdialog dessen Pendant aus dem WinForms Namespace (System.Windows.Forms) verwendet werden:

PrintDialog winFormsPrintDialog = new PrintDialog();
winFormsPrintDialog.ShowDialog();

Dieser WinForms Dialog wird von der Hostapplikation erkannt und modal aufgerufen. Doch die Nachteile dieses Print-Dialoges sind beträchtlich, da nicht wie beim oben beschriebenen Printdialog aus dem System.Windows.Controls Namespace direkt mittels PrintVisual(..) ausgedruckt werden kann.

 

Lösungsansatz 2:

Verwendet soll also der ursprüngliche Printdialog aus dem System.Windows.Controls Namespace. Da aber dieser aber nicht direkt als modaler Dialog aufgerufen werden kann, wird nun ein weiteres WinForm zur Hilfe genommen.

Statt aus der WPF Komponente direkt den Printdialog aufzurufen, wird dieser in ein WinForms Control “verpackt”. Die WPF Komponente ruft nun dieses WinForms Control modal auf. Da diese auch von der Hostapplikation erkannt wird, ist die ganze Applikation gesperrt solange der Printdialog offen ist.

WinForms Control welches den WPF Printdialog aufruft:

namespace WpfControlLibrary
{
   using System;
   using System.Windows.Forms;

   public class PrintDialogHost : Form
   {
      // wpf print dialog instance.
      private System.Windows.Controls.PrintDialog selectedPrintDialog;

      private PrintDialogHost()
      {
         Load += PrintDialogHostLoad;
      }

      // Returns instance of WPF print dialog or null if 
      // the user cancel the print dialog window. 
      public static System.Windows.Controls.PrintDialog GetPrintDialogInstance()
      {
         System.Windows.Controls.PrintDialog printDialog;
         using (PrintDialogHost instance = new PrintDialogHost())
         {
            instance.ShowDialog();
            printDialog = instance.selectedPrintDialog;
         }

         return printDialog;
      }

      private void PrintDialogHostLoad(object sender, EventArgs e)
      {
         var pd = new System.Windows.Controls.PrintDialog();
         selectedPrintDialog = pd.ShowDialog() == true ? pd : null;
         Close();
      }
   }
}

Natürlich haben wir nun das störende WinForm welches jedes Mal zusätzlich zum Printdialog erzeugt und dargestellt wird. Dieses wird zwar zwingend benötigt, kann aber über einige Einstellungen für den Benutzer unsichtbar aufgerufen werden:

private PrintDialogHost()
{
   ShowInTaskbar = false;
   WindowState = FormWindowState.Minimized;
   Load += PrintDialogHostLoad;
}
Anregungen oder alternative Lösungen sind sehr willkommen.
123

Tag Cloud

.NET android Angular AngularJs Arduino ASP.Net automated testing Azure Big Data C# C++ Cloud continuous integration Elm Embedded Führung gRPC Internet of Things IoT Java Javascript M2M OWASP Projektmanagement protobuf Python Raspberry Pi Reactive Programming REST Scrum Security Softwarequalität SPA Testen testing Testmanagement Teststrategie UX Visual Studio WebAPI windows WPF Xamarin Xamarin.Android Xamarin.Forms

Archive

Current Posts

  • Akzente setzen mit der Android Splash Screen API unter .NET MAUI
  • Do You have Your Personal Space?
  • Automated provisioning with ARM Templates
  • Asynchrone Beobachtungen und Versprechungen in Angular
  • Simplify Your Automated Tests With Fluent Syntax

Last Comments

  • Hans Reinsch bei Der Safety-Plan: Die wichtigsten Antworten mit Checkliste
  • George H. Barbehenn bei Modeling Optocouplers with Spice
  • Noser Blog Touch-Actions in Xamarin.Forms - Noser Blog bei Mach mehr aus Animationen in Xamarin.Forms mit SkiaSharp
  • Noser Blog Focus on the Secure Storage service of Trusted Firmware (TFM) - Noser Blog bei First run of the Trusted Firmware (TFM) application
  • Noser Blog First run of the Trusted Firmware (TFM) application - Noser Blog bei Focus on the Secure Storage service of Trusted Firmware (TFM)

Popular Posts

Xamarin.Android Code Obfuscation

6 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 1 - Aufbruch

5 Comments

ManuScripts: Wenn jemand eine Reise tut... Funktionale Programmierung mit Elm - Teil 2 - Kein Picknick

4 Comments

Contact us

  1. Name *
    * Please enter your name
  2. Email *
    * Please enter a valid email address
  3. Message *
    * Please enter message
© 2013 NOSER ENGINEERING AG. All rights reserved. Datenschutz | Cookie-Richtlinie