none
WPF und Datenbindung RRS feed

  • Frage

  • Hallo alle,

    ich möchte in WPF (4.0) den Inhalt eines Labels berechnen lassen. Grundlage ist ein Fixwert, der aus einer Datenbank geladen wird, abzgl. eines in einer Textbox eingegebenen Wertes.

    Der Code dazu in der XAML ist (Ausschnitt):

    <Window x:Class="EolDL800.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:EolDL800"   
        Title="EOL DL800" Height="495" Width="683" Name="wndEOL">
        <Window.Resources>
            <local:ErrorsToMessageConverter x:Key="eToMConverter" />
    		<local:clsProperty x:Key="Props"/>
    		<local:clsBetätigung x:Key="Betätigung"/>
    		<local:lblMp2Converter x:Key="Mp2Converter"/>
        </Window.Resources>
        <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="450">
            <GroupBox Header="ZSB Betätigung" Margin="18,137,0,58" Name="grpBetätigung" HorizontalAlignment="Left" Width="300" GotFocus="grpBetätigung_GotFocus">
                <GroupBox.DataContext>
    		<Binding Source="{StaticResource Betätigung}"/>				
    	    </GroupBox.DataContext>
                <Grid Height="248">
                  <TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top">
    		<TextBox.Text>
    			<Binding Path="FormatTxbMp001"  UpdateSourceTrigger="LostFocus" NotifyOnSourceUpdated="True">
                            </Binding>
    		</TextBox.Text>
    	     </TextBox>
                 <Label Height="23" Margin="0,127,28,0" Name="lblMP2" HorizontalAlignment="Right" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" VerticalAlignment="Top"
    					Content="{Binding Source={StaticResource Mp2Converter}, Converter={StaticResource Mp2Converter}}">				
    	     </Label>
               </Grid>
            </GroupBox>

    Im Codebehind (clsBetätigung) habe ich folgendes:

    public class clsBetätigung : IDisposable,INotifyPropertyChanged
    	{
    		public event PropertyChangedEventHandler PropertyChanged;
    		public string Mp001
    		{
    			get { return _mp001; }
    			set
    			{
    				_mp001 = FormatiereTextBox(value, 1); //Eingabe formatieren
    				OnPropertyChanged(new PropertyChangedEventArgs("txbMp001"));
    			}
    		}
    		public string Mp2
    		{
    			get { return _mp2; }
    			set
    			{
    				_mp2 = Convert.ToString(ErrechneMP2(value)); // Der Wert, der errechnet, und im Label dargestellt werden soll
    				OnPropertyChanged(new PropertyChangedEventArgs("IstwertMp2"));
    			}
    		}
    		protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    		{
    			if (PropertyChanged != null)
    				PropertyChanged(this, e);
    		}
    		private double ErrechneMP2(string messwert) //Sollwerte aus der DB holen, in Abhängigkeit vom gewählten Bauteil (hier: Betätigung), und des Messpunktes
    		{
    			PropertyInfo _MP = Window1.ZSB.GetType().GetProperty("MP");
    			PropertyInfo _MpWerte = Window1.ZSB.GetType().GetProperty("MpWerte");
    			double mw=Convert.ToSingle(messwert);
    
    			//Berechne MP3I
    			_MP.SetValue(Window1.ZSB, "3I", null);
    			mpWerte = (DataRow)_MpWerte.GetValue(Window1.ZSB, null);
    			mw = Math.Round((double)mpWerte["soll"] - Convert.ToSingle(_mp001), 3); //ZSB ein allgemeines Object, das je nach Bauteil mit der zugehörigen Klasse aufgerufen wird
    			return mw;
    		}
    		
    	}
    	public class lblMp2Converter : IValueConverter
    	{
    		string wertMp001 //Der Wert, der abgezogen wird
    		{ get; set; }
    
    		public lblMp2Converter() { }
    
    		public object Convert(object value, Type targetType, object parameter,
    			System.Globalization.CultureInfo culture)
    		{
    			if (Window1.ZSB == null) return 0;
    
    			PropertyInfo _MP = Window1.ZSB.GetType().GetProperty("MP");
    			PropertyInfo _MpWerte = Window1.ZSB.GetType().GetProperty("MpWerte");
    
    			//Berechne MP3I
    			_MP.SetValue(Window1.ZSB, "3I", null);
    			DataRow mpWerte = (DataRow)_MpWerte.GetValue(Window1.ZSB, null);
    			return Math.Round((double)mpWerte["soll"] - (double)value, 3);
    		}
    		
    		public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    		{
    			throw new NotImplementedException();
    		}
    	}
    }

    Anmerkung: Mp2 und der Converter lblMp2Converter sind sicherlich dopelt gemoppelt. Das beruht noch auf verzweifeltem rumprobieren. Welches davon hinterher verwendet wird, ist mir egal.

    Meiner persönlichen Ansicht nach, liegt das Problem darin, dass das Label keine Nachricht davon erhält, dass sich in der Textbox der Wert geändert hat. "OnPropertyChanged" läuft iwie ins Leere. Wie also besser machen?

    Grüße

      Heiko

    Mittwoch, 24. September 2014 10:28

Antworten

  • Hallo Heiko,

    leider sieht man nicht alles was nötig wäre.

    Damit die Bindung des Label erkennen kann das eine Änderung erfolgt ist, müssen alle Beteiligten mitspielen. Da ein Label wiederum niemals von sich aus einen Wert zuweist, muss irgendjemand MP2 setzen oder es sich aus den Benachrichtigungen ergeben, dass ein neuer Wert dafür vorliegt.

    Du mischt hier fröhlich Eigenschaften und Werte. Der "Wert", der im Setter von MP2 gesetzt wird, hängt dann noch von einem Rattenschwanz anderer Werte ab, die nicht nachvollziehen, kann - so dem Wert aus  "Window1.ZSB.MP",  "Window1.ZSB.MpWerte" (Reflektion der Lesbarkeit halber entfernt), dem Wert aus der DataRow mpWerte["soll"] (mit Zwischenschritten) und schließlich "MP001".

    Davon auftauchen und Nachrichten verschicken tut aber nur "MP001" - was zusätzlich ein OnPropertyChanged("MP2") auslösen sollte. Alle anderen schweigen aber immer noch stille und es passiert nichts.

    Eine wirkliche Lösung kriegst Du aber erst, wenn Du etwas weniger halbherzig mit der Klasse umgehst, und alle Daten in "clsBestätigung" hinterlegst - die Reflektion gehört dabei ganz eliminiert.

    An der jetzigen Informationen, kann ich nicht darstellen, dafür ist das Dickicht für mich schon zu groß (was Dir eine Warnung für die Wartbarkeit Deine Programms sein sollte).

    Gruß Elmar

    Mittwoch, 24. September 2014 13:26
  • Hi Heiko,
    Dein Code ist etwas konfus. Bindung mit direkter Zuweisung ist gemischt. Das ist auch die Ursache Deiner Probleme. Mp2 ist an lblMP2 gebunden. Gleichzeitig ist aber im CodeBehind eine Zuweisung von string.Empty an lblMP2. Damit wird das Ergebnis der Bindung überschrieben, d.h. gelöscht.

    Außerdem ist mit aufgefallen, dass Du nicht typsicher bezüglich eingestellter Culture arbeitest. Die Eingabe eines Amerikaners, der beispielsweise "1,234.56" eingetragen hat, wandelst Du um in "1.234.56". Bei der Konvertierung wird ein Fehler geworfen, den Du nicht abfängst.

    Du solltest den Anwender in seiner eingestellten Culture Daten erfassen lassen und auch so anzeigen. Außerdem sollten Konvertierungsfehler ausgewiesen werden und nicht zum Programmabsturz führen.

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Freitag, 26. September 2014 12:50
  • Hallo Heiko,

    ich habe jetzt mal was "gebastelt". Da es zu umfangreich fürs Forum ist als Download via OneDrive:

    WpfSturm1.zip

    Da es mir wie Peter ging - es war einfach zu undurchsichtig - habe ich das Ganze in Richtung MVVM mit einem ViewModel umgestellt (naja, mit einigen Freiheitsgraden). Jegliche Reflektion und andere Unschönheiten sollten damit nicht mehr notwendig sein.

    Ich habe mich dabei auf die "Bestätigung" beschränkt. Dinge, die im Code angedeutet sind, aber fehlen, wie die zweite Variante bzw. die Validation, wären nach zu bauen. Was aber über ein weitere Modelle leichter realisierbar sein sollte.

    Da ich vieles erraten musste, was die "Messpunkte" und ihre Zusammenhänge angeht, ist vermutlich einiges nicht der Realität entsprechend.

    Die Geschichte mit der "automatischen Teilung" habe ich erst einmal komplett gestrichen, da sie IMO so nur Probleme bereitet. Falls eh nur das fehlende Komma war, das wird auf deutsche Verhältnisse eingestellt (siehe App.OnStartup)

    Schau es Dir einfach mal an... Wenn Fragen bleiben (vermutlich), so frag.

    Gruß Elmar


    Samstag, 27. September 2014 08:53
  • Hi,
    nachfolgend mein Versuch mit Deinen Codeausschnitten, mit dem die Anzeige funktioniert:

    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for Window3.xaml
      /// </summary>
      public partial class Window3 : Window
      {
        public Window3()
        {
          InitializeComponent();
        }
    
        DataRow mpWerte;
        private static object _zsb;
        clsSQL Sql = new clsSQL();
        MesswertValidationRule MwValid = new MesswertValidationRule();
        //gehört zur ValidationRule
        public static readonly DependencyProperty CheckCharge = DependencyProperty.Register("CheckCharge", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckLiefdat = DependencyProperty.Register("CheckLiefdat", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckLfdnr = DependencyProperty.Register("CheckLfdnr", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckAuftrag = DependencyProperty.Register("CheckAuftrag", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckZgfrdat = DependencyProperty.Register("CheckZgfrdat", typeof(string), typeof(Window3));
    
        public static object ZSB
        {
          get
          {
            return _zsb;
          }
    
        }
    
        private void grpRastgestänge_GotFocus(object sender, RoutedEventArgs e)
        {
          //Dynamisierung der Klassen. Je nachdem welche GroupBox den Focus hat wird dem Objekt "ZSB" eine Klasse zugewiesen
          //Das erspart so einiges an Abfragen, bei Klassenbezogenen Anweisungen
          if (_zsb == null || _zsb is clsBetätigung)
          {
            _zsb = (clsRastgestänge)Activator.CreateInstance(typeof(clsRastgestänge));
            txbCharge.Clear();
            txbLiefDat.Clear();
            txbLfdNr.Clear();
            txbMP1.Clear();
            txbMP2.Clear();
            txbMP3.Clear();
            _zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
            txbCharge.Focus();
            this.DataContext = _zsb;
          }
        }
    
        private void grpBetätigung_GotFocus(object sender, RoutedEventArgs e)
        {
          if (_zsb == null || _zsb is clsRastgestänge)
          {
            _zsb = (clsBetätigung)Activator.CreateInstance(typeof(clsBetätigung));
            txbCharge.Clear();
            txbLiefDat.Clear();
            txbLfdNr.Clear();
            txbMP001.Clear();
            txbMP002.Clear();
            txbMP3II.Clear();
            //lblMP2.Content = string.Empty;
            lblMP3I.Content = string.Empty;
            _zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
            txbCharge.Focus();
          }
        }
    
        TextBox txbMP1 = new TextBox();
        TextBox txbMP2 = new TextBox();
        TextBox txbMP3 = new TextBox();
    
        TextBox txbCharge = new TextBox();
        TextBox txbLiefDat = new TextBox();
        TextBox txbLfdNr = new TextBox();
    
        private void btnSaveBet_Click(object sender, RoutedEventArgs e)
        {
    
        }
        
      }
    
    
      public class clsBetätigung : IDisposable, INotifyPropertyChanged
      {
        DataSet _SollwerteBetätigung = new DataSet();
        DataTable _SollwerteBetätigungTabelle = new DataTable();
        DataRow mpWerte;
        string _mp = "";
        private string _mp001; //5,759
        private string _mp002; //0
        private string _mp3II; //14,1N
        private string _mp3I; //45,947
        private string _mp2; //40,171
        public event PropertyChangedEventHandler PropertyChanged;
        
        public DataTable SollwerteBetätigungTabelle
        {
          get { return _SollwerteBetätigungTabelle; }
          set { _SollwerteBetätigungTabelle = SollwerteLaden(); }
        }
        public DataRow MpWerte
        {
          get
          {
            DataRow _y = _SollwerteBetätigungTabelle.Rows.Find(_mp);
            return _y;
          }
        }
        public string MP
        {
          get { return _mp; }
          set { _mp = value; }
        }
    
        public string Mp001
        {
          get { return _mp001; }
          set
          {
            _mp001 = FormatiereTextBox(value, 1);
            Mp2 = ErrechneMP2(_mp001);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp001"));
          }
        }
        public string Mp002
        {
          get { return _mp002; }
          set
          {
            _mp002 = FormatiereTextBox(value, 1);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp002"));
          }
        }
        public string Mp3II
        {
          get { return _mp3II; }
          set
          {
            _mp3II = FormatiereTextBox(value, 2);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp3II"));
          }
        }
    
        // MP2 für Betätigung
        public string Mp2
        {
          get { return _mp2; }
          set
          {
            _mp2 = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));
          }
        }
    
        public clsBetätigung() { }
    
        public DataTable SollwerteLaden()
        {
          string sql = "";
          DataRow zeile;
    
          _SollwerteBetätigungTabelle = _SollwerteBetätigung.Tables.Add("tabelle");
          // Tabelle definieren			
          _SollwerteBetätigungTabelle.Columns.Add("mp_int", typeof(string));
          _SollwerteBetätigungTabelle.Columns.Add("soll", typeof(double));
          _SollwerteBetätigungTabelle.Columns.Add("otg", typeof(double));
          _SollwerteBetätigungTabelle.Columns.Add("utg", typeof(double));
          _SollwerteBetätigungTabelle.PrimaryKey = new DataColumn[] { _SollwerteBetätigung.Tables["tabelle"].Columns["mp_int"] };
    
          //Werte, die nicht aus der Datenbank kommen eintragen
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = "001";
          zeile["soll"] = 5.759;
          zeile["otg"] = 0.3;
          zeile["utg"] = -0.3;
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = "002";
          zeile["soll"] = 0;
          zeile["otg"] = 0.3;
          zeile["utg"] = -0.3;
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          //------
          sql = "SELECT mp_int,soll,otg,utg FROM messpunkte\r" +
            "WHERE artikel = 47 AND versionn=(SELECT MAX(versionn) FROM messpunkte WHERE artikel=47)";
          //SqlDataReader ds = null; // App.MySqlDb.DatenLesen(sql);
          //if (ds.HasRows)
          //{
          //  while (ds.Read())
          //  {
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = 0; // ds["mp_int"].ToString();
          zeile["soll"] = 0; // Math.Round(Convert.ToSingle(ds["soll"].ToString().Replace(".", ",")), 3);
          zeile["otg"] = 0; // Math.Round(Convert.ToSingle(ds["otg"].ToString().Replace(".", ",")), 3);
          zeile["utg"] = 0; // Math.Round(Convert.ToSingle(ds["utg"].ToString().Replace(".", ",")), 3);
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          //  }
          //}
          //ds.Close();
          //ds = null;
    
          return _SollwerteBetätigungTabelle;
        }
        public void Dispose()
        {
          return;
        }
    
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
          if (PropertyChanged != null)
            PropertyChanged(this, e);
        }
    
        private string FormatiereTextBox(string text, int p)
        {
          //Es soll dem User freigestellt sein, Werte mit oder ohne Komma einzugeben, daher erfolgt hier eine entsprechende Formatierung. p ist dabei die Anzahl der Vorkommastellen
          text = text.Replace(",", "");
          string _formatTxb = text.Substring(0, p) + "," + text.Substring(p);
          //Falls ein Wert mit Punkt statt Komma eingebenen wurde, den Punkt zum Komma machen
          _formatTxb = _formatTxb.Replace(".", ",");
          return _formatTxb;
        }
    
        private string ErrechneMP2(string messwert)
        {
          PropertyInfo _MP = Window3.ZSB.GetType().GetProperty("MP");
          PropertyInfo _MpWerte = Window3.ZSB.GetType().GetProperty("MpWerte");
          double mw = Convert.ToSingle(messwert);
    
          //Zum Berechnen von MP2 wird der Sollwert des MP 3I benötigt
          _MP.SetValue(Window3.ZSB, "3I", null);
          mpWerte = (DataRow)_MpWerte.GetValue(Window3.ZSB, null);
          mw = 0; // Math.Round((double)mpWerte["soll"] - Convert.ToSingle(_mp001), 3);
          return "55"; // mw.ToString();
        }
    
      }
    
      class clsSQL
      {
      }
    
      class MesswertValidationRule
      {
      }
    
      public class clsRastgestänge
      {
      }
    
      public class ErrorsToMessageConverter
      {
      }
    
      public class lblMp2Converter
      {
      }
    }

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Montag, 29. September 2014 09:42

Alle Antworten

  • Hallo Heiko,

    was auffällt: Deine Eigenschaften Mp001 und Mp2 verschicken falsche PropertyChanged Nachrichten. Es muss dabei der Name der Eigenschaft (also "Mp001" bzw. "Mp2" sein, damit die Datenbindung es dem richtigen Eigenschaft zuordnen kann.

    Dann dürfte es auch mit der Bindung klappen, vorausgesetzt die ist richtig (im XAML) eingerichtet.

    Den Converter solltest Du "beerdigen", direkt mit Fenster / Eigenschaften zu rbeiten, führt auf Sicht zur Wartungskatastrophe (zumal Deine Benennungen IMO viel zu kryptisch sind).

    Gruß Elmar

    Mittwoch, 24. September 2014 11:23
  • Hallo Elmar,

    die Bezeichnungen habe ich geändert. Das Label wird trotzdem nicht aktualisiert.Ohne COnverter würde ich die Bindung im Label so setzen:

    Content="{Binding Path=Mp2}"

    Das OnPropertyChanged- Ereignis wird zwar abgesetzt, das Label bekommt davon aber aus irgendwelchem Grund nichts mit.

    Grüße

      Heiko

    Mittwoch, 24. September 2014 12:25
  • Hallo Heiko,

    leider sieht man nicht alles was nötig wäre.

    Damit die Bindung des Label erkennen kann das eine Änderung erfolgt ist, müssen alle Beteiligten mitspielen. Da ein Label wiederum niemals von sich aus einen Wert zuweist, muss irgendjemand MP2 setzen oder es sich aus den Benachrichtigungen ergeben, dass ein neuer Wert dafür vorliegt.

    Du mischt hier fröhlich Eigenschaften und Werte. Der "Wert", der im Setter von MP2 gesetzt wird, hängt dann noch von einem Rattenschwanz anderer Werte ab, die nicht nachvollziehen, kann - so dem Wert aus  "Window1.ZSB.MP",  "Window1.ZSB.MpWerte" (Reflektion der Lesbarkeit halber entfernt), dem Wert aus der DataRow mpWerte["soll"] (mit Zwischenschritten) und schließlich "MP001".

    Davon auftauchen und Nachrichten verschicken tut aber nur "MP001" - was zusätzlich ein OnPropertyChanged("MP2") auslösen sollte. Alle anderen schweigen aber immer noch stille und es passiert nichts.

    Eine wirkliche Lösung kriegst Du aber erst, wenn Du etwas weniger halbherzig mit der Klasse umgehst, und alle Daten in "clsBestätigung" hinterlegst - die Reflektion gehört dabei ganz eliminiert.

    An der jetzigen Informationen, kann ich nicht darstellen, dafür ist das Dickicht für mich schon zu groß (was Dir eine Warnung für die Wartbarkeit Deine Programms sein sollte).

    Gruß Elmar

    Mittwoch, 24. September 2014 13:26
  • Hallo Elmar,

    das die Wart-/ Lesbarkeit des Programms stark eingeschränkt, da gebe ich dir völlig recht. Ich stehe mit WPF, Datenbindung, Validierung, etc noch am Anfang, und das was du siehst (wovon es noch mehr gibt) ist chaotisch. Es ist aus gefühlt 500 Websites zusammengebastelt.

    Reflection habe ich gemacht, da ich in beiden Baugruppen jeweils drei Messwerte in eine DB übertragen will, und ich nicht den gleichen Code in jeder Klasse haben wollte. Auch da gibt es sicher noch eine Menge Verbesserungsmöglichkeiten.

    Was das Problem mit der Datenbindung angeht, habe ich jetzt folgendes gemacht:

    In der XAML:

    <Label Height="23" Margin="0,127,28,0" Name="lblMP2" HorizontalAlignment="Right" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" VerticalAlignment="Top">
    	<Label.Content>
    		<Binding Path="Mp2" UpdateSourceTrigger="PropertyChanged" />
    	</Label.Content>
    </Label>

    Setzen der Werte:

    public string Mp001
    {
    	get { return _mp001; }
    	set
    	{
    		_mp001 = FormatiereTextBox(value, 1);
    		_mp2 = ErrechneMP2(_mp001);
    		OnPropertyChanged(new PropertyChangedEventArgs("Mp001"));
    	}
    }
    
    public string Mp2
    {
    	get { return _mp2; }
    	set
    	{
    		_mp2=value;
    		OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));
    	}
    }

    Für mich sieht das logisch aus, das Label ist leider anderer Meinung, die Änderung wird nicht angezeigt.

    Grüße

      Heiko

    Mittwoch, 24. September 2014 14:31
  • Hi Heiko,
    mit der Zuweisung "_mp2 = ErrechneMP2(_mp001);" wird das für die 'Meldung' der Änderung erforderliche "OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));" nicht durchlaufen. Setze deshalb "Mp2 = ErrechneMP2(_mp001);" oder füge das "OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));" mit in den Setter der Mp001.

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 24. September 2014 14:56
  • Hallo Peter,

    hab ich gemacht: "Mp2=ErrechneMP2(_mp001)", und in den Setter von Mp2: "OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));".

    Es läuft alles durch wie gewünscht, nur das Label bleibt leer.

    Grüße

      Heiko

    Donnerstag, 25. September 2014 07:32
  • Hallo Heiko,

    wie oben schon skizziert, ist es mit den beiden Eigenschaften alleine nicht getan. Wenn Du etwas konkreter beschreiben könntest, wäre es leichter ein passendes Beispiel zu zeigen.

    Dazu müsste man aber wissen, wie die Daten zusammenhängen, insbes. wer oder was Window1.ZSB ist und wie die Inhalte dort mit dem Rest zusammenhängen.

    Gruß Elmar

    Donnerstag, 25. September 2014 09:03
  • Hallo Elmar,

    okay, mal schauen, ob ich alles Notwendige "erwische":

    Window1.xaml.cs:

    namespace EolDL800
    {
    	/// <summary>
    	/// Interaktionslogik für Window1.xaml
    	/// </summary>
    	/// 
    	public partial class Window1 : Window
    	{
    		DataRow mpWerte;
    		private static object _zsb;
    		clsSQL Sql = new clsSQL();
    				MesswertValidationRule MwValid=new MesswertValidationRule();
    	//gehört zur ValidationRule
    		public static readonly DependencyProperty CheckCharge = DependencyProperty.Register("CheckCharge", typeof(string), typeof(Window1));
    		public static readonly DependencyProperty CheckLiefdat = DependencyProperty.Register("CheckLiefdat", typeof(string), typeof(Window1));
    		public static readonly DependencyProperty CheckLfdnr = DependencyProperty.Register("CheckLfdnr", typeof(string), typeof(Window1));
    		public static readonly DependencyProperty CheckAuftrag = DependencyProperty.Register("CheckAuftrag", typeof(string), typeof(Window1));
    		public static readonly DependencyProperty CheckZgfrdat = DependencyProperty.Register("CheckZgfrdat", typeof(string), typeof(Window1));
    				
    		public static object ZSB
    		{
    			get 
    			{ 
    				return _zsb;
    			}
    
    		}
    		public Window1()
    		{
    			InitializeComponent();
    		}
    
    		private void grpRastgestänge_GotFocus(object sender, RoutedEventArgs e)
    		{
    			//Dynamisierung der Klassen. Je nachdem welche GroupBox den Focus hat wird dem Objekt "ZSB" eine Klasse zugewiesen
    			//Das erspart so einiges an Abfragen, bei Klassenbezogenen Anweisungen
    			if (_zsb == null || _zsb is clsBetätigung)
    			{
    				_zsb = (clsRastgestänge)Activator.CreateInstance(typeof(clsRastgestänge));
    				txbCharge.Clear();
    				txbLiefDat.Clear();
    				txbLfdNr.Clear();
    				txbMP1.Clear();
    				txbMP2.Clear();
    				txbMP3.Clear();
    				_zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
    				txbCharge.Focus();
    			}
    		}
    
    		private void grpBetätigung_GotFocus(object sender, RoutedEventArgs e)
    		{
    			if (_zsb == null || _zsb is clsRastgestänge)
    			{
    				_zsb = (clsBetätigung)Activator.CreateInstance(typeof(clsBetätigung));
    				txbCharge.Clear();
    				txbLiefDat.Clear();
    				txbLfdNr.Clear();
    				txbMP001.Clear();
    				txbMP002.Clear();
    				txbMP3II.Clear();
    				lblMP2.Content = string.Empty;
    				lblMP3I.Content = string.Empty;
    				_zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
    				txbCharge.Focus();
    			}
    		}	
    // Danach folgt Code, der noch ein paar Dinge vornimmt, wie das Tagedatum ins Excelformat umzuwandeln und die Werte in einer DB speichert (über Button_Click)

    Die Groupbox- Definition in Window1.xaml:

    <Window x:Class="EolDL800.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:EolDL800"   
        Title="EOL DL800" Height="495" Width="683" Name="wndEOL">
    	<Window.Resources>
    		<local:ErrorsToMessageConverter x:Key="eToMConverter" />
    		<local:clsBetätigung x:Key="Betätigung"/>
    		<local:lblMp2Converter x:Key="Mp2Converter"/>
    	</Window.Resources>
    	<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="450">
    		<GroupBox Header="ZSB Betätigung" Margin="18,137,0,58" Name="grpBetätigung" HorizontalAlignment="Left" Width="300" GotFocus="grpBetätigung_GotFocus">
    			<GroupBox.Resources>
    			</GroupBox.Resources>
    			<GroupBox.DataContext>
    				<Binding Source="{StaticResource Betätigung}"/>
    			</GroupBox.DataContext>
    			<Grid Height="248">
    				<Label Height="28" Margin="10,23,0,0" Name="label3" VerticalAlignment="Top" HorizontalAlignment="Left" Width="90" VerticalContentAlignment="Center" FontWeight="Bold">Forderungen</Label>
    				<Label Height="28" Margin="0,23,28,0" Name="label4" VerticalAlignment="Top" HorizontalAlignment="Right" Width="80" VerticalContentAlignment="Center" FontWeight="Bold">Ist- Wert</Label>
    				<Label Margin="10,49,0,0" Name="label5" HorizontalAlignment="Left" Width="95" VerticalContentAlignment="Center" Height="28.04" VerticalAlignment="Top" IsEnabled="True" IsHitTestVisible="True">5,759 +/- 0,3</Label>
    				<TextBox Margin="180,52,0,0" Name="txbMP001" HorizontalAlignment="Left" Width="80" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"  Height="23.04" VerticalAlignment="Top">
    					<TextBox.Text>
    						<Binding Path="Mp001"  UpdateSourceTrigger="LostFocus" NotifyOnSourceUpdated="True">
    							<!--<Binding.ValidationRules>
    								<local:MesswertValidationRule MP="001"/>
    							</Binding.ValidationRules>-->
    						</Binding>
    					</TextBox.Text>
    				</TextBox>
    				<Label Margin="10,75,0,0" Name="label6" HorizontalAlignment="Left" Width="95" VerticalContentAlignment="Center" Height="28.04" VerticalAlignment="Top">0 +/- 0,3</Label>
    				<TextBox Margin="0,78,28,0" Name="txbMP002" HorizontalAlignment="Right" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Height="23.04" VerticalAlignment="Top">
    					<TextBox.Text>
    						<Binding Path="Mp002"  UpdateSourceTrigger="LostFocus">
    							<!--<Binding.ValidationRules>
    								<local:MesswertValidationRule MP="002"/>
    							</Binding.ValidationRules>-->
    						</Binding>
    					</TextBox.Text>
    				</TextBox>
    				<Label Margin="10,101,0,0" Name="label7" VerticalContentAlignment="Center" HorizontalAlignment="Left" Width="124.583" Height="28.04" VerticalAlignment="Top">Kraft 14,1N +/- 1,65N</Label>
    				<TextBox Margin="0,104,28,0" Name="txbMP3II" HorizontalAlignment="Right" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Height="23.04" VerticalAlignment="Top">
    					<TextBox.Text>
    						<Binding Path="Mp3II"  UpdateSourceTrigger="LostFocus">
    							<!--<Binding.ValidationRules>
    								<local:MesswertValidationRule MP="002"/>
    							</Binding.ValidationRules>-->
    						</Binding>
    					</TextBox.Text>
    				</TextBox>
    				<Label Margin="10,127,143,0" Name="lblAbst40Soll" VerticalContentAlignment="Center" Height="28.04" VerticalAlignment="Top">Abstand 40,171 +/- 0,3</Label>
    				<Label Height="23" Margin="0,127,28,0" Name="lblMP2" HorizontalAlignment="Right" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" VerticalAlignment="Top" Content="{Binding Mp2}"/>
    				<Label Margin="10,153,0,0" Name="lblAbst45Soll" VerticalAlignment="Top" HorizontalAlignment="Left" VerticalContentAlignment="Center" Width="131.953">Abstand 45,947 +/- 0,3</Label>
    				<Label Height="23" Margin="180,153,0,0" Name="lblMP3I" HorizontalAlignment="Left" Width="80"  HorizontalContentAlignment="Right" VerticalContentAlignment="Center" VerticalAlignment="Top"></Label>
    				<Button Height="30" Margin="0,0,0,25" Name="btnSaveBet" VerticalAlignment="Bottom" HorizontalAlignment="Center" Width="100" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="btnSaveBet_Click">Speichern</Button>
    			</Grid>
    		</GroupBox>

    Ich habe hier jetzt nur eine Groupbox eingefügt, die andere erfasst nur Werte, ohne irgendetwas noch zu berechnen. Die ValidationRule ist auskommentiert, da ich sie nicht immer im Einzelschritt durchlaufen will. Sie funktioniert aber wie gewollt.

    Die Klasse clsBetätigung:

    namespace EolDL800
    {
    	public class clsBetätigung : IDisposable,INotifyPropertyChanged
    	{
    		DataSet _SollwerteBetätigung = new DataSet();
    		DataTable _SollwerteBetätigungTabelle = new DataTable();
    		DataRow mpWerte;
    		string _mp = "";
    		private string _mp001; //5,759
    		private string _mp002; //0
    		private string _mp3II; //14,1N
    		private string _mp3I; //45,947
    		private string _mp2; //40,171
    		public event PropertyChangedEventHandler PropertyChanged;
    
    
    		public DataTable SollwerteBetätigungTabelle
    		{
    			get { return _SollwerteBetätigungTabelle; }
    			set { _SollwerteBetätigungTabelle = SollwerteLaden(); }
    		}
    		public DataRow MpWerte
    		{
    			get
    			{
    				DataRow _y = _SollwerteBetätigungTabelle.Rows.Find(_mp);
    				return _y;
    			}
    		}
    		public string MP
    		{
    			get { return _mp; }
    			set { _mp = value; }
    		}
    
    		public string Mp001
    		{
    			get { return _mp001; }
    			set
    			{
    				_mp001 = FormatiereTextBox(value, 1);
    				Mp2 = ErrechneMP2(_mp001);
    				OnPropertyChanged(new PropertyChangedEventArgs("Mp001"));
    			}
    		}
    		public string Mp002
    		{
    			get { return _mp002; }
    			set
    			{
    				_mp002 = FormatiereTextBox(value, 1);
    				OnPropertyChanged(new PropertyChangedEventArgs("Mp002"));
    			}
    		}
    		public string Mp3II
    		{
    			get { return _mp3II;}
    			set
    			{
    				_mp3II = FormatiereTextBox(value, 2);
    				OnPropertyChanged(new PropertyChangedEventArgs("Mp3II"));
    			}
    		}
    		MP2 für Betätigung
    		public string Mp2
    		{
    			get { return _mp2; }
    			set
    			{
    				_mp2 = value;
    				OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));
    			}
    		}
     
    		public clsBetätigung()	{ }
    
    		public DataTable SollwerteLaden()
    		{
    			string sql="";
    			DataRow zeile;
    			
    			_SollwerteBetätigungTabelle=_SollwerteBetätigung.Tables.Add("tabelle");
    			// Tabelle definieren			
    			_SollwerteBetätigungTabelle.Columns.Add("mp_int",typeof(string));
    			_SollwerteBetätigungTabelle.Columns.Add("soll",typeof(double));
    			_SollwerteBetätigungTabelle.Columns.Add("otg",typeof(double));
    			_SollwerteBetätigungTabelle.Columns.Add("utg",typeof(double));
    			_SollwerteBetätigungTabelle.PrimaryKey = new DataColumn[] { _SollwerteBetätigung.Tables["tabelle"].Columns["mp_int"] };
    
    			//Werte, die nicht aus der Datenbank kommen eintragen
    			zeile = _SollwerteBetätigungTabelle.NewRow();
    			zeile["mp_int"] = "001";
    			zeile["soll"] = 5.759;
    			zeile["otg"] = 0.3;
    			zeile["utg"] = -0.3;
    			_SollwerteBetätigungTabelle.Rows.Add(zeile);
    			zeile = _SollwerteBetätigungTabelle.NewRow();
    			zeile["mp_int"] = "002";
    			zeile["soll"] = 0;
    			zeile["otg"] = 0.3;
    			zeile["utg"] = -0.3;
    			_SollwerteBetätigungTabelle.Rows.Add(zeile);
    			//------
    			sql="SELECT mp_int,soll,otg,utg FROM messpunkte\r"+
    				"WHERE artikel = 47 AND versionn=(SELECT MAX(versionn) FROM messpunkte WHERE artikel=47)";
    			var ds = App.MySqlDb.DatenLesen(sql);
    			if (ds.HasRows)
    			{
    				while (ds.Read())
    				{					
    					zeile = _SollwerteBetätigungTabelle.NewRow();
    					zeile["mp_int"] = ds["mp_int"].ToString();
    					zeile["soll"] = Math.Round(Convert.ToSingle(ds["soll"].ToString().Replace(".",",")),3);
    					zeile["otg"] = Math.Round(Convert.ToSingle(ds["otg"].ToString().Replace(".",",")),3);
    					zeile["utg"] = Math.Round(Convert.ToSingle(ds["utg"].ToString().Replace(".",",")),3);
    					_SollwerteBetätigungTabelle.Rows.Add(zeile);
    				}
    			}
    			ds.Close();
    			ds = null;
    
    			return _SollwerteBetätigungTabelle;
    		}
    		public void Dispose()
    		{
    			return;
    		}
    
    		protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    		{
    			if (PropertyChanged != null)
    				PropertyChanged(this, e);
    		}
    		
    		private string FormatiereTextBox(string text, int p)
    		{
                                 //Es soll dem User freigestellt sein, Werte mit oder ohne Komma einzugeben, daher erfolgt hier eine entsprechende Formatierung. p ist dabei die Anzahl der Vorkommastellen
    			text = text.Replace(",", "");
    			string _formatTxb = text.Substring(0, p) + "," + text.Substring(p);
    			//Falls ein Wert mit Punkt statt Komma eingebenen wurde, den Punkt zum Komma machen
    			_formatTxb = _formatTxb.Replace(".", ",");
    			return _formatTxb;
    		}
    
    		private string ErrechneMP2(string messwert)
    		{
    			PropertyInfo _MP = Window1.ZSB.GetType().GetProperty("MP");
    			PropertyInfo _MpWerte = Window1.ZSB.GetType().GetProperty("MpWerte");
    			double mw=Convert.ToSingle(messwert);
    
    			//Zum Berechnen von MP2 wird der Sollwert des MP 3I benötigt
    			_MP.SetValue(Window1.ZSB, "3I", null);
    			mpWerte = (DataRow)_MpWerte.GetValue(Window1.ZSB, null);
    			mw = Math.Round((double)mpWerte["soll"] - Convert.ToSingle(_mp001), 3);
    			return mw.ToString();
    		}
    		
    	}

    (Ich hoffe, es ist aussagekräftig genug)

    Die Textboxformatierung funktioniert einwandfrei. Wenn ich einen Wert ohne Komma eingebe, wird ein Komma gesetzt und in der Textbox auch angezeigt. Nur beim Label wird das Ergebnis der Berechnung (das richtig ist) nicht angezeigt (sprich, es wird nicht aktualisiert).

    Grüße

      Heiko

    Donnerstag, 25. September 2014 09:42
  • Hallo Heiko,

    ich habe Dich nicht vergessen.

    Leider hat mich Deine optimistische Aussage: "Die Textboxformatierung funktioniert einwandfrei." länger aufgehalten als es hätte sein sollen. Da ich davon ausgehe, am Ende muss eine (richtige) Zahl herauskommen, kommt das nicht so ganz hin.

    Und Zahlen müssen es sein, will man daraus Werte ableiten, wie es vorgesehen ist...

    Deswegen dauert es ein wenig länger - denn ich muss zwischenzeitlich was anderes tun. Zumal ich mir vorgenommen habe, etwas mehr im Gesichteten aufzuräumen.

    Gruß Elmar

    • Als Antwort vorgeschlagen Peter Fleischer Freitag, 26. September 2014 13:12
    • Nicht als Antwort vorgeschlagen Peter Fleischer Freitag, 26. September 2014 13:12
    Freitag, 26. September 2014 10:13
  • Hi Heiko,
    Dein Code ist etwas konfus. Bindung mit direkter Zuweisung ist gemischt. Das ist auch die Ursache Deiner Probleme. Mp2 ist an lblMP2 gebunden. Gleichzeitig ist aber im CodeBehind eine Zuweisung von string.Empty an lblMP2. Damit wird das Ergebnis der Bindung überschrieben, d.h. gelöscht.

    Außerdem ist mit aufgefallen, dass Du nicht typsicher bezüglich eingestellter Culture arbeitest. Die Eingabe eines Amerikaners, der beispielsweise "1,234.56" eingetragen hat, wandelst Du um in "1.234.56". Bei der Konvertierung wird ein Fehler geworfen, den Du nicht abfängst.

    Du solltest den Anwender in seiner eingestellten Culture Daten erfassen lassen und auch so anzeigen. Außerdem sollten Konvertierungsfehler ausgewiesen werden und nicht zum Programmabsturz führen.

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Freitag, 26. September 2014 12:50
  • Hallo Heiko,

    ich habe jetzt mal was "gebastelt". Da es zu umfangreich fürs Forum ist als Download via OneDrive:

    WpfSturm1.zip

    Da es mir wie Peter ging - es war einfach zu undurchsichtig - habe ich das Ganze in Richtung MVVM mit einem ViewModel umgestellt (naja, mit einigen Freiheitsgraden). Jegliche Reflektion und andere Unschönheiten sollten damit nicht mehr notwendig sein.

    Ich habe mich dabei auf die "Bestätigung" beschränkt. Dinge, die im Code angedeutet sind, aber fehlen, wie die zweite Variante bzw. die Validation, wären nach zu bauen. Was aber über ein weitere Modelle leichter realisierbar sein sollte.

    Da ich vieles erraten musste, was die "Messpunkte" und ihre Zusammenhänge angeht, ist vermutlich einiges nicht der Realität entsprechend.

    Die Geschichte mit der "automatischen Teilung" habe ich erst einmal komplett gestrichen, da sie IMO so nur Probleme bereitet. Falls eh nur das fehlende Komma war, das wird auf deutsche Verhältnisse eingestellt (siehe App.OnStartup)

    Schau es Dir einfach mal an... Wenn Fragen bleiben (vermutlich), so frag.

    Gruß Elmar


    Samstag, 27. September 2014 08:53
  • Hallo Peter,

    die Zuweisung "lblMP2.Content=string.Empty" habe ich entfernt, ein Ergebnis wird trotzdem nicht angezeigt.

    Das mit der Culture ist nicht schlimm, da es sich um betriebsinterne Software handelt. Die Konvertierung mit dem Punkt ist für die dahinter stehende MySQL Datenbank.

    Grüße

      Heiko

    Montag, 29. September 2014 08:57
  • Hi,
    nachfolgend mein Versuch mit Deinen Codeausschnitten, mit dem die Anzeige funktioniert:

    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApplication1
    {
      /// <summary>
      /// Interaction logic for Window3.xaml
      /// </summary>
      public partial class Window3 : Window
      {
        public Window3()
        {
          InitializeComponent();
        }
    
        DataRow mpWerte;
        private static object _zsb;
        clsSQL Sql = new clsSQL();
        MesswertValidationRule MwValid = new MesswertValidationRule();
        //gehört zur ValidationRule
        public static readonly DependencyProperty CheckCharge = DependencyProperty.Register("CheckCharge", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckLiefdat = DependencyProperty.Register("CheckLiefdat", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckLfdnr = DependencyProperty.Register("CheckLfdnr", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckAuftrag = DependencyProperty.Register("CheckAuftrag", typeof(string), typeof(Window3));
        public static readonly DependencyProperty CheckZgfrdat = DependencyProperty.Register("CheckZgfrdat", typeof(string), typeof(Window3));
    
        public static object ZSB
        {
          get
          {
            return _zsb;
          }
    
        }
    
        private void grpRastgestänge_GotFocus(object sender, RoutedEventArgs e)
        {
          //Dynamisierung der Klassen. Je nachdem welche GroupBox den Focus hat wird dem Objekt "ZSB" eine Klasse zugewiesen
          //Das erspart so einiges an Abfragen, bei Klassenbezogenen Anweisungen
          if (_zsb == null || _zsb is clsBetätigung)
          {
            _zsb = (clsRastgestänge)Activator.CreateInstance(typeof(clsRastgestänge));
            txbCharge.Clear();
            txbLiefDat.Clear();
            txbLfdNr.Clear();
            txbMP1.Clear();
            txbMP2.Clear();
            txbMP3.Clear();
            _zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
            txbCharge.Focus();
            this.DataContext = _zsb;
          }
        }
    
        private void grpBetätigung_GotFocus(object sender, RoutedEventArgs e)
        {
          if (_zsb == null || _zsb is clsRastgestänge)
          {
            _zsb = (clsBetätigung)Activator.CreateInstance(typeof(clsBetätigung));
            txbCharge.Clear();
            txbLiefDat.Clear();
            txbLfdNr.Clear();
            txbMP001.Clear();
            txbMP002.Clear();
            txbMP3II.Clear();
            //lblMP2.Content = string.Empty;
            lblMP3I.Content = string.Empty;
            _zsb.GetType().InvokeMember("SollwerteLaden", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, ZSB, null);
            txbCharge.Focus();
          }
        }
    
        TextBox txbMP1 = new TextBox();
        TextBox txbMP2 = new TextBox();
        TextBox txbMP3 = new TextBox();
    
        TextBox txbCharge = new TextBox();
        TextBox txbLiefDat = new TextBox();
        TextBox txbLfdNr = new TextBox();
    
        private void btnSaveBet_Click(object sender, RoutedEventArgs e)
        {
    
        }
        
      }
    
    
      public class clsBetätigung : IDisposable, INotifyPropertyChanged
      {
        DataSet _SollwerteBetätigung = new DataSet();
        DataTable _SollwerteBetätigungTabelle = new DataTable();
        DataRow mpWerte;
        string _mp = "";
        private string _mp001; //5,759
        private string _mp002; //0
        private string _mp3II; //14,1N
        private string _mp3I; //45,947
        private string _mp2; //40,171
        public event PropertyChangedEventHandler PropertyChanged;
        
        public DataTable SollwerteBetätigungTabelle
        {
          get { return _SollwerteBetätigungTabelle; }
          set { _SollwerteBetätigungTabelle = SollwerteLaden(); }
        }
        public DataRow MpWerte
        {
          get
          {
            DataRow _y = _SollwerteBetätigungTabelle.Rows.Find(_mp);
            return _y;
          }
        }
        public string MP
        {
          get { return _mp; }
          set { _mp = value; }
        }
    
        public string Mp001
        {
          get { return _mp001; }
          set
          {
            _mp001 = FormatiereTextBox(value, 1);
            Mp2 = ErrechneMP2(_mp001);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp001"));
          }
        }
        public string Mp002
        {
          get { return _mp002; }
          set
          {
            _mp002 = FormatiereTextBox(value, 1);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp002"));
          }
        }
        public string Mp3II
        {
          get { return _mp3II; }
          set
          {
            _mp3II = FormatiereTextBox(value, 2);
            OnPropertyChanged(new PropertyChangedEventArgs("Mp3II"));
          }
        }
    
        // MP2 für Betätigung
        public string Mp2
        {
          get { return _mp2; }
          set
          {
            _mp2 = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Mp2"));
          }
        }
    
        public clsBetätigung() { }
    
        public DataTable SollwerteLaden()
        {
          string sql = "";
          DataRow zeile;
    
          _SollwerteBetätigungTabelle = _SollwerteBetätigung.Tables.Add("tabelle");
          // Tabelle definieren			
          _SollwerteBetätigungTabelle.Columns.Add("mp_int", typeof(string));
          _SollwerteBetätigungTabelle.Columns.Add("soll", typeof(double));
          _SollwerteBetätigungTabelle.Columns.Add("otg", typeof(double));
          _SollwerteBetätigungTabelle.Columns.Add("utg", typeof(double));
          _SollwerteBetätigungTabelle.PrimaryKey = new DataColumn[] { _SollwerteBetätigung.Tables["tabelle"].Columns["mp_int"] };
    
          //Werte, die nicht aus der Datenbank kommen eintragen
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = "001";
          zeile["soll"] = 5.759;
          zeile["otg"] = 0.3;
          zeile["utg"] = -0.3;
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = "002";
          zeile["soll"] = 0;
          zeile["otg"] = 0.3;
          zeile["utg"] = -0.3;
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          //------
          sql = "SELECT mp_int,soll,otg,utg FROM messpunkte\r" +
            "WHERE artikel = 47 AND versionn=(SELECT MAX(versionn) FROM messpunkte WHERE artikel=47)";
          //SqlDataReader ds = null; // App.MySqlDb.DatenLesen(sql);
          //if (ds.HasRows)
          //{
          //  while (ds.Read())
          //  {
          zeile = _SollwerteBetätigungTabelle.NewRow();
          zeile["mp_int"] = 0; // ds["mp_int"].ToString();
          zeile["soll"] = 0; // Math.Round(Convert.ToSingle(ds["soll"].ToString().Replace(".", ",")), 3);
          zeile["otg"] = 0; // Math.Round(Convert.ToSingle(ds["otg"].ToString().Replace(".", ",")), 3);
          zeile["utg"] = 0; // Math.Round(Convert.ToSingle(ds["utg"].ToString().Replace(".", ",")), 3);
          _SollwerteBetätigungTabelle.Rows.Add(zeile);
          //  }
          //}
          //ds.Close();
          //ds = null;
    
          return _SollwerteBetätigungTabelle;
        }
        public void Dispose()
        {
          return;
        }
    
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
          if (PropertyChanged != null)
            PropertyChanged(this, e);
        }
    
        private string FormatiereTextBox(string text, int p)
        {
          //Es soll dem User freigestellt sein, Werte mit oder ohne Komma einzugeben, daher erfolgt hier eine entsprechende Formatierung. p ist dabei die Anzahl der Vorkommastellen
          text = text.Replace(",", "");
          string _formatTxb = text.Substring(0, p) + "," + text.Substring(p);
          //Falls ein Wert mit Punkt statt Komma eingebenen wurde, den Punkt zum Komma machen
          _formatTxb = _formatTxb.Replace(".", ",");
          return _formatTxb;
        }
    
        private string ErrechneMP2(string messwert)
        {
          PropertyInfo _MP = Window3.ZSB.GetType().GetProperty("MP");
          PropertyInfo _MpWerte = Window3.ZSB.GetType().GetProperty("MpWerte");
          double mw = Convert.ToSingle(messwert);
    
          //Zum Berechnen von MP2 wird der Sollwert des MP 3I benötigt
          _MP.SetValue(Window3.ZSB, "3I", null);
          mpWerte = (DataRow)_MpWerte.GetValue(Window3.ZSB, null);
          mw = 0; // Math.Round((double)mpWerte["soll"] - Convert.ToSingle(_mp001), 3);
          return "55"; // mw.ToString();
        }
    
      }
    
      class clsSQL
      {
      }
    
      class MesswertValidationRule
      {
      }
    
      public class clsRastgestänge
      {
      }
    
      public class ErrorsToMessageConverter
      {
      }
    
      public class lblMp2Converter
      {
      }
    }

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Montag, 29. September 2014 09:42
  • Hallo Elmar,

    erstmal vielen Dank für die Arbeit, die du dir gemacht hast. Im Moment arbeite ich mich gerade durch den Code. Ich denke, dass ich dafür noch ein bis zwei Tage benötige.

    Ich melde mich dann wieder.

    Grüße

      Heiko

    Montag, 29. September 2014 11:06
  • Hi Heiko,
    aber denk daran, dass man auch mit MySql typsicher arbeiten kann und auf die Verrenkungen in Deinem gezeigten Code verzichten kann. Falls Werte aus Gründen der Kompatibilität mit anderen Anwendungen als Zeichenketten abgelegt werden müssen, dann sollte beim Auslesen und Abspeichern die passende Konvertierung ausgeführt werden. Im Programm kann dann durchgängig typsicher gearbeitet werden.

    --
    Peter
    Meine Homepage mit Tipps und Tricks

    Montag, 29. September 2014 11:51
  • Hallo Elmar,

    ich habe dich nicht vergessen, war nur eine Menge los hier in den letzten Tagen. Erstmal noch vielen Dank für deine Arbeit.

    Ich habe deinen Code weitestgehend nachvollziehen können (denke ich). Einige mir bislang unbekannte Dinge sind drin (wie zB Binding Path=MP001.AnzeigeSollText, sprich Feld.Methode oder auch Lambda- Ausdrücke), aber ich denke im Großen und Ganzen hab ich's erfasst.

    Ich werde auf Basis deiner Arbeit mal weitermachen, also Messpunkte laden über SQL- Abfrage, Speichern in die Datenbank, Validierung, und den anderen ZSB einbauen. Apropos Validierung: Ich würde die ins ViewModel packen, richtig?

    Wenn ich auf Schwierigkeiten stoße (wovon ich ausgehe) melde ich mich wieder.

    Grüße

      Heiko
    Mittwoch, 8. Oktober 2014 12:15
  • Hallo Peter,

    danke für den Hinweis. Ich werde mich stets bemühen... :-)

    Grüße

      Heiko

    Mittwoch, 8. Oktober 2014 12:16
  • Hallo Heiko,

    das Beispiel hat naturgemäß einige Löcher, da ich doch vieles erraten musste.

    Die Validation kann mit einem ViewModel über IDataErrorInfo (oder ab .NET 4.5 auch INotifyDataErrorInfo) erfolgen, womit die - vermutlich mit Reflektion gespickten - Validationsklassen nicht notwendig wären. Zwei Artikel dazu:

    Data Validation in MVVM (sehr umfassend, zeigt auch andere Dinge noch mal)
    Data validation in WPF (geht auf INotifyDataErrorInfo ein)

    (Generell vermeide aber die Behandlung von numerischen Daten als Zeichenketten, das macht wirklich nur Probleme - auch wenn das bedeutet, dass man ein Komma geben muss ;)

    Peter hat es auch angesprochen: Für die Datenbank solltest Du  Dir ebenso angewöhnen, typgerecht zu arbeiten, d. h. für die Zugriffe einen Data Layer einbauen. Wobei Du auf eine DataTable verzichten könntest und direkt mit den Klassen im Modell (bei mir angedeutet mit Messpunkt) arbeiten.

    Gruß Elmar

    Mittwoch, 8. Oktober 2014 14:06
  • Hallo Elmar,

    ich bin jetzt beim Thema Validation angekommen. Solche Dinge wie gültiges Datum, gültige Auftragsnummer etc. lassen sich ja recht einfach validieren. Beim Thema Messwerte wird's für mich allerdings schon wieder kompliziert. Die gemessenen Teile müssen i.O. sein, d.h. ich brauche Messwerte außerhalb der Toleranz gar nicht erst speichern.

    Meine XAML exemplarisch en einer TextBox:

    <TextBox Grid.Row="1" Grid.Column="1" Margin="8,4" Width="96" Name="IstMP001TextBox" HorizontalContentAlignment="Right" VerticalContentAlignment="Center">
    	<TextBox.Text>
    		<Binding Path="IstMP001" StringFormat="0.000;;#" UpdateSourceTrigger="LostFocus" >
    			<Binding.ValidationRules>
    				<vm:MesswertValidationRule MP="MP001"/>
    			</Binding.ValidationRules>
    		</Binding>
    	</TextBox.Text>
    </TextBox>

    Die Validation- Klasse dazu:

    //ValidationRule für die Messwerte
    public class MesswertValidationRule : ValidationRule
    {	
    	private Messpunkt _mp;
    	public Messpunkt MP
    	{
    		get { return _mp; }
    		set { _mp = value; }
    	}
    		public MesswertValidationRule() { }
    		public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    	{
    		////Werte prüfen
    		if ((double)value > _mp.Soll + _mp.Otg || (double)value < _mp.Soll + _mp.Utg) return new ValidationResult(false, null);
    	
    		return new ValidationResult(true, null);		
    	}
    }

    Der Hintergedanke ist, im XAML lediglich auf den Messpunkt zu verweisen, um dann die Soll- und andere Werte aus der Klasse Messpunkt zu holen. Ich bekomme allerdings im XAML den Fehler: "Fehler    1    Vom TypeConverter-Objekt für "Messpunkt" wird das Konvertieren aus einer Zeichenfolge nicht unterstützt."

    Der Link zum Thema "Data Validation in MVVM" ist zwar hochinteressant, scheint mir für mein Projekt allerdings viel zu weit gehend.  Ich hätt's gerne kleiner und einfacher, wenn möglich. Nur, wie?

    Grüße

      Heiko

    Mittwoch, 15. Oktober 2014 13:02
  • Hallo Heiko,

    mir scheint, Du klammerst Dich zu sehr an dem, was schon "funktioniert" hat.

    Zum einen sind ValidationRules nicht besonders gut geeignet, wenn sich um dynamische Prüfungen handelt - hier weil die Prüfung in Abhängigkeit vom jeweiligen Messpunkt erfolgt. Sie unterstützen von Haus aus keine Bindungen, sondern nur DependencyProperties. Man kann das zwar ändern, aber es wird dann auch "komplex", siehe z. B.: WPF: Passing values to Validation Rules from bound data

    Zum anderen ist IDataErrorInfo eine sehr einfache Schnittstelle - mit gerade mal zwei Methoden, von denen man eine braucht. Die von mir zitierten Artikel gehen weiter als man muss. Jedoch sollte man mindestens zu Verstehen suchen, was da passiert, denn es kann helfen, wenn das Projekt größer wird - u. a. kann man einiges an Wiederholungen sparen.

    Möchtest Du es am Anfang einfacher haben, um es leichter zu verstehen, gibt es Beispiele für einfache Varianten zu Hauf.

    Ich habe das Beispiel WpfSturm1 um IDataErrorInfo erweitert, was einige wenige Zeilen erfordert hat. Nebenbei reagiert jetzt die "Speichern"-Schaltfläche entsprechend und es gibt eine (einfache) Fehleranzeige durch etwas zusätzliches XAML (oben in der Sicht).

    Nur zum Vergleich habe ich eine MesspunktValidationRule geschrieben, wie sie in etwa aussehen müsste - schon das sind deutlich mehr Zeilen. Und ich habe sie nicht mehr "verdrahtet", denn das hätte entweder DependencyProperties bedeutet und das ViewModel "verunreinigt" oder eine Erweiterung, um Bindungen möglich zu machen - womit es wieder komplexer geworden wäre.

    Jetzt liegt es an Dir, Deinem Blick zu öffnen und mit etwas mehr Mut zu Neuem an die Sache heranzugehen.

    Gruß Elmar

    • Bearbeitet Elmar Boye Donnerstag, 16. Oktober 2014 16:40
    Donnerstag, 16. Oktober 2014 16:38
  • Hallo Elmar,

    erst nochmal vielen Dank für die Arbeit, die du dir machst.

    Ich habe auf Basis dessen das Projekt weiter bearbeitet, und u.a. Kopfdaten (Auftragsnummer, Bauteilnummer, etc) mit eingebaut. Eine Validierung der Kopfdaten erfolgt wie von dir gezeigt.

    Das DataGrid zur Kontrolle ist raus, stattdessen will ich da einen Textblock einbauen, der die letzten fünf gespeicherten Bauteile anzeigt. Plan dabei ist, die Funktion dafür als ObservableCollection ins Model aufzunehmen. Allerdings fehlt mir noch der Zusammenhang zwischen View, Viewmodel, und Model.

    Über die Sache mit dem Komma einfügen habe ich noch nicht endgültig entschieden.

    Falls du Lust und Langeweile hast, ich habe das hier mal abgelegt.

    Grüße

      Heiko



    • Bearbeitet Heiko Sturm Donnerstag, 23. Oktober 2014 12:14 Link korrigiert
    Donnerstag, 23. Oktober 2014 12:10