none
MVVM - Textbox BackgroundColor mit Converter ändern? RRS feed

  • Frage

  • Hallo Zusammen,

    ich wünsche Euch allen noch ein frohes und gesundes neues Jahr.

    Nun, aktuell habe ich mehrere <TextBox> in meinem View. Wenn ich nun über

            <Style TargetType="{x:Type TextBox}">
                <Setter Property="Background" Value="Aqua"/>
            </Style>       

    die Hintergrundfarbe ändere, dann tut er das natürlich für alle Boxes. Wie kann man dies auch für einzelne Textboxes realisieren?

    In meinem DataGrid verwende ich aktuell folgenden Converter, der mir die Farben in abhängigkeit der Eigenschaften X_Soll, XminTol, etc. ändert und zwar mit:

                                    <DataGridTextColumn Header="XMess" Binding="{Binding X_Mess_Kompensiert}">
                                        <DataGridTextColumn.ElementStyle>
                                            <Style TargetType="TextBlock">
                                                <Setter Property="Background">
                                                    <Setter.Value>
                                                        <MultiBinding Converter="{StaticResource conv}">
                                                            <Binding Path="X_Mess_Kompensiert"/>
                                                            <Binding Path="X_Soll"/>
                                                            <Binding Path="XMinTol"/>
                                                            <Binding Path="XMaxTol"/>
                                                        </MultiBinding>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </DataGridTextColumn.ElementStyle>
                                    </DataGridTextColumn>
    

    und:

        public class Windows37Conv : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                decimal messWert = 0, nennWert = 0, minTol = 0, maxTol = 0;
                bool result2 = false, result1 = false, result;
    
                if (values[0] != null)
                {
                    // Abfragen ob String leer ist
                    if (values[1].ToString() == "-1") return new SolidColorBrush(Colors.Black);
                    result1 = decimal.TryParse(values[0].ToString(), out messWert);
                }
                if (values[1] != null)
                {
                    result2 = decimal.TryParse(values[1].ToString(), out nennWert);
                }
    
                if (values.Length == 4)
                {
                    try
                    {
                        result = decimal.TryParse(values[2].ToString(), out minTol);
                    }
                    catch
                    {
                        Debug.Print($"Windows37Conv : IMultiValueConverter: Parse {values[0].ToString()}");
                    }
    
                    try
                    {
                        result = decimal.TryParse(values[3].ToString(), out maxTol);
                    }
                    catch
                    {
                        Debug.Print($"Windows37Conv : IMultiValueConverter: Parse funktioniert nicht.");
                    }
                }
    
                // wenn value 0 und value 1 eine zahl sind
                if (result1 && result2)
                {
                    if (messWert < (nennWert + minTol)) return new SolidColorBrush(Colors.Red);
                    if (messWert > (nennWert + maxTol)) return new SolidColorBrush(Colors.Red);
                    return new SolidColorBrush(Colors.Green);
                }
                else
                {
                    return new SolidColorBrush(Colors.Red);
                }
    
            }
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

    Ziel ist es, genau mit diesesm Converter die Hintergrundfarbe meiner TextBoxes zu steuern.

    Vielen Dank für Eure Hilfe.

    Viele Grüße

    Martin





    Donnerstag, 10. Januar 2019 14:22

Antworten

  • Hi,

    das kannst du zum Beispiel so machen, wenn du für jede TextBox unterschiedliche Messwerte/Grenzwerte hast:

            <TextBox Height="100" Width="400">
                <TextBox.Style>
                    <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                        <Setter Property="Background">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource conv}">
                                    <Binding Path="X_Mess_Kompensiert"/>
                                    <Binding Path="X_Soll"/>
                                    <Binding Path="XMinTol"/>
                                    <Binding Path="XMaxTol"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TextBox.Style>
            </TextBox>
    Gruß

    Stefan


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP


    • Bearbeitet Stefan Krömer Donnerstag, 10. Januar 2019 19:09
    • Als Antwort markiert mApO18 Freitag, 11. Januar 2019 06:52
    • Tag als Antwort aufgehoben mApO18 Freitag, 25. Januar 2019 12:58
    • Als Antwort markiert mApO18 Montag, 28. Januar 2019 06:53
    Donnerstag, 10. Januar 2019 19:07

Alle Antworten

  • Hi,

    das kannst du zum Beispiel so machen, wenn du für jede TextBox unterschiedliche Messwerte/Grenzwerte hast:

            <TextBox Height="100" Width="400">
                <TextBox.Style>
                    <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                        <Setter Property="Background">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource conv}">
                                    <Binding Path="X_Mess_Kompensiert"/>
                                    <Binding Path="X_Soll"/>
                                    <Binding Path="XMinTol"/>
                                    <Binding Path="XMaxTol"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TextBox.Style>
            </TextBox>
    Gruß

    Stefan


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP


    • Bearbeitet Stefan Krömer Donnerstag, 10. Januar 2019 19:09
    • Als Antwort markiert mApO18 Freitag, 11. Januar 2019 06:52
    • Tag als Antwort aufgehoben mApO18 Freitag, 25. Januar 2019 12:58
    • Als Antwort markiert mApO18 Montag, 28. Januar 2019 06:53
    Donnerstag, 10. Januar 2019 19:07
  • Hallo an Alle,

    ich muss nocheinmal den Thread reaktivieren, da mir noch eine Sache fehlt:

    Der Converter, den ich in der Textbox benutze funktioniert nicht, bzw. wird nicht aufgerufen, wenn sich XMess_Live ändert.


                                    <TextBox Text="{Binding XMess_Live}" FontWeight="Bold" MinWidth="250" FontSize="{StaticResource SchriftGroesseLiveAnzeige}" IsReadOnly="True">
                                        <TextBox.Style>
                                            <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                                                <Setter Property="Background">
                                                    <Setter.Value>
                                                        <MultiBinding Converter="{StaticResource conv}">
                                                            <Binding Path="X_Mess_Kompensiert"/>
                                                            <Binding Path="X_Soll"/>
                                                            <Binding Path="XMinTol"/>
                                                            <Binding Path="XMaxTol"/>
                                                        </MultiBinding>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </TextBox.Style>
                                    </TextBox>
    

    In meinem bestehenden Grid funktioniert es. Ich habe die Vermutung, dass er die Source

    ItemsSource="{Binding Pruefling.Messpunkte}" nicht erkennt. Wie definiert man die Source in einer TextBox korrekt? Ich hoffe ihr könnt mir helfen.

    P.S.: Im DataGrid funktioniert es:

                            <DataGrid Grid.Row="0" ItemsSource="{Binding Pruefling.Messpunkte}" AutoGenerateColumns="False" IsReadOnly="True" Width="1200" Height="370" HorizontalAlignment="Left" Grid.ColumnSpan="2">
                                <i:Interaction.Behaviors>
                                    <local:WindowBehavior/>
                                </i:Interaction.Behaviors>
                                <DataGrid.ColumnHeaderStyle>
                                    <Style TargetType="{x:Type DataGridColumnHeader}">
                                        <Setter Property="FontWeight" Value="Bold"/>
                                        <Setter Property="HorizontalAlignment" Value="Center"/>
                                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
                                        <Setter Property="FontSize" Value="{StaticResource SchriftGroesseTabelle}"/>
                                    </Style>
                                </DataGrid.ColumnHeaderStyle>
                                <DataGrid.RowStyle>
                                    <Style TargetType="{x:Type DataGridRow}">
                                        <Setter Property="FontWeight" Value="Bold"/>
                                        <Setter Property="FontSize" Value="{StaticResource SchriftGroesseTabelle}"/>
                                    </Style>
                                </DataGrid.RowStyle>
                                <DataGrid.Columns>
                                    <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                                    <DataGridTextColumn Header="XMess" Binding="{Binding X_Mess_Kompensiert}">
                                        <DataGridTextColumn.ElementStyle>
                                            <Style TargetType="TextBlock">
                                                <Setter Property="Background">
                                                    <Setter.Value>
                                                        <MultiBinding Converter="{StaticResource conv}">
                                                            <Binding Path="X_Mess_Kompensiert"/>
                                                            <Binding Path="X_Soll"/>
                                                            <Binding Path="XMinTol"/>
                                                            <Binding Path="XMaxTol"/>
                                                        </MultiBinding>
                                                    </Setter.Value>
                                                </Setter>
                                            </Style>
                                        </DataGridTextColumn.ElementStyle>
                                    </DataGridTextColumn>
    

    Freitag, 25. Januar 2019 12:57
  • Hi,

    der Converter funktioniert. Er wird genau dann ausgelöst, wenn sich einer der vier Werte ändert und wirkt auf Background...

    Wie soll der Converter auf eine Änderung von XMess_Live auslösen? Er hat ja keine Kenntnis von dieser Eigenschaft...

    Auf die Schnelle:

    Du könntest dem Converter die Eigenschaft übergeben, dann würde er jedes mal auslösen, obwohl der Wert im Converter nicht berücksichtigt wird...

                <TextBox Height="100" Width="400" KeyDown="TextBox_KeyDown" Text="{Binding XMess_Live}">
                    <TextBox.Style>
                        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                            <Setter Property="Background">
                                <Setter.Value>
                                    <MultiBinding Converter="{StaticResource conv}">
                                        <Binding Path="X_Mess_Kompensiert"/>
                                        <Binding Path="X_Soll"/>
                                        <Binding Path="XMinTol"/>
                                        <Binding Path="XMaxTol"/>
                                        <Binding Path="XMess_Live"/>
                                    </MultiBinding>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </TextBox.Style>
                </TextBox>
    ...müsstest dann nur deine Abfrage entsprechend ändern: if (values.Length == 5) ... die dann aber nicht mehr für dein DataGrid passt... Also vielleicht einen zweiten Converter oder die Abfrage auf 4 oder 5, aber das kannst nur du entscheiden.

    Gruß


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP



    Freitag, 25. Januar 2019 13:52
  • Hi,
    bei Dir scheint ein konzeptionelles Problem vorzuliegen.

    Deine TextBox ist auf der gleichen Ebene (gleiches Objekt) sowohl an XMess_Live als auch an X_Mess_Kompensiert, X_Soll, XMinTol, XMaxTol gebunden. Wenn sich jetzt der Wert von XMess_Live ändert, bleiben die anderen Werte unverändert. Wie soll dann eine Änderung von XMess_Live die Farbe verändern können?


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks

    Freitag, 25. Januar 2019 15:59
  • Hallo Stefan, danke für deine Antwort. Ich werde es gleich am Montag testen.

    Peter, auch dir vielen Dank für deine Bewertung. Leider versteh ich es nicht ganz. Es sollte doch auch möglich sein statt Xmess_Live gleich X_Mess_Kompensiert in der Textbox zu binden oder?

                <TextBox Height="100" Width="400" KeyDown="TextBox_KeyDown" Text="{Binding X_Mess_Kompensiert}">

    Es sind die gleichen Variablen. Ich habe XMess_Live nur verwendet, da die Textbox einen String braucht. Wenn ich allerdings X_Mess_Kompensiert binde, dann erkennt er die Variable nicht und zeigt nichts an. Ich habe die Vermutung , dass der Textbox der Kontext

    ItemsSource="{Binding Pruefling.Messpunkte}"

    fehlt.

    Viele Grüße
    Martin

    Samstag, 26. Januar 2019 19:28
  • Hallo Martin,

    wenn ich dich richtig verstehe, ist "Xmess_Live" lediglich eine Kopie/Krücke von "X_Mess_Kompensiert", weil die TextBox nichts anzeigt, wenn du "X_Mess_Kompensiert an die Text-Eigenschaft der TextBox bindest... Das macht irgendwie keinen Sinn.

    "X_Mess_Kompensiert" funktioniert am Converter, der den Background steuert, aber nicht an der Text-Eigenschaft? Das kann nicht sein, zumindest kann ich mir das nicht erklären!

    Wie ist denn deine Struktur, wie sind die Einschaften deklariert, wird ein PropertyChanged ausgelöst, wenn sich der Wert ändert?

    Gruß


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    Sonntag, 27. Januar 2019 08:25
  • Hi,
    wie Stefan geschrieben hat, scheint in Deiner Realisierung etwas mit dem PropertyChanged nicht richtig implementiert zu sein. Hier mal eine kleine Demo zu Deinem Szenario, die zeigt, dass es problemlos funktionieren kann. Ändere einfach mal die zufällig generierten Werte (in rechten Panel) und bei entsprechender Eingabe wird die TextBox auch Rot oder Grün.

    XAML:

    <Window x:Class="WpfApp1.Window88"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="Window88" Height="450" Width="800">
      <Window.Resources>
        <local:Window88VM x:Key="vm"/>
        <local:Window88Conv x:Key="conv"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition/>
          <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <DataGrid ItemsSource="{Binding ViewPreuflinge}" AutoGenerateColumns="False">
            <DataGrid.Columns>
              <DataGridTextColumn Header="Prüflings-ID" Binding="{Binding ID}"/>
            </DataGrid.Columns>
          </DataGrid>
          <DataGrid Grid.Row="1" ItemsSource="{Binding ViewMesspunkte}"/>
        </Grid>
        <StackPanel Grid.Column="1" DataContext="{Binding ViewMesspunkte.CurrentItem}">
          <StackPanel.Resources>
            <Style TargetType="{x:Type TextBox}">
              <Setter Property="Margin" Value="5"/>
            </Style>
          </StackPanel.Resources>
          <TextBox Text="{Binding ID}" />
          <TextBox Text="{Binding X_Mess_Kompensiert}" />
          <TextBox Text="{Binding X_Soll}" />
          <TextBox Text="{Binding XMinTol}" />
          <TextBox Text="{Binding XMaxTol}" />
        </StackPanel>
        <Grid Grid.Row="1" DataContext="{Binding ViewMesspunkte.CurrentItem}" Grid.ColumnSpan="2">
          <TextBox Grid.Row="1"
                 Text="{Binding X_Mess_Kompensiert}" 
                 FontWeight="Bold" MinWidth="250" 
                 FontSize="40"
                 IsReadOnly="True">
            <TextBox.Style>
              <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                <Setter Property="Background">
                  <Setter.Value>
                    <MultiBinding Converter="{StaticResource conv}">
                      <Binding Path="X_Mess_Kompensiert"/>
                      <Binding Path="X_Soll"/>
                      <Binding Path="XMinTol"/>
                      <Binding Path="XMaxTol"/>
                    </MultiBinding>
                  </Setter.Value>
                </Setter>
              </Style>
            </TextBox.Style>
          </TextBox>
        </Grid>
      </Grid>
    </Window>
    

    Dazu der ViewModel und Converter:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;
    
    namespace WpfApp1
    {
      public class Window88VM : INotifyPropertyChanged
      {
        public Window88VM()
        {
          Random rnd = new Random();
          for (int i = 1; i < 10; i++)
          {
            Window88Pruefling pr = new Window88Pruefling()
            {
              ID = i,
              Messpunkte = new ObservableCollection<Window88Messpunkt>()
            };
            for (int k = 1; k < rnd.Next(4, 11); k++)
            {
              pr.Messpunkte.Add(new Window88Messpunkt()
              {
                ID = k,
                X_Mess_Kompensiert = rnd.Next() / 100,
                X_Soll = rnd.Next() / 100,
                XMinTol = rnd.Next() / 100,
                XMaxTol = rnd.Next() / 100
              }
              );
            };
            _daten.Add(pr);
          }
          cvs1.Source = _daten;
          cvs1.View.CurrentChanged += (s1, e1) =>
          {
            Window88Pruefling pr = (s1 as ListCollectionView).CurrentItem as Window88Pruefling;
            cvs2 = new CollectionViewSource() { Source = pr.Messpunkte };
            OnPropertyChanged(nameof(ViewMesspunkte));
            //cvs2.View.CurrentChanged += (s2, e2) =>
            // {
            //   DetailMesspunkt = (s2 as ListCollectionView).CurrentItem as Window88Messpunkt;
            //   OnPropertyChanged(nameof(DetailMesspunkt));
            // };
          };
        }
        private ObservableCollection<Window88Pruefling> _daten = new ObservableCollection<Window88Pruefling>();
        private CollectionViewSource cvs1 = new CollectionViewSource();
        private CollectionViewSource cvs2 = new CollectionViewSource();
        public ICollectionView ViewPreuflinge { get { return cvs1.View; } }
        public ICollectionView ViewMesspunkte { get { return cvs2.View; } }
    
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this,
          new PropertyChangedEventArgs(propName));
      }
    
      public class Window88Pruefling
      {
        public int ID { get; set; }
    
        public ObservableCollection<Window88Messpunkt> Messpunkte { get; set; }
      }
    
      public class Window88Messpunkt : INotifyPropertyChanged
      {
        public int ID { get; set; }
    
        private decimal _x_Mess_Kompensiert;
        public decimal X_Mess_Kompensiert
        {
          get { return this._x_Mess_Kompensiert; }
          set { this._x_Mess_Kompensiert = value; OnPropertyChanged(); }
        }
    
        private decimal _x_Soll;
        public decimal X_Soll
        {
          get { return this._x_Soll; }
          set { this._x_Soll = value; OnPropertyChanged(); }
        }
    
        private decimal _xMinTol;
        public decimal XMinTol
        {
          get { return this._xMinTol; }
          set { this._xMinTol = value; OnPropertyChanged(); }
        }
    
        private decimal _xMaxTol;
        public decimal XMaxTol
        {
          get { return this._xMaxTol; }
          set { this._xMaxTol = value; OnPropertyChanged(); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        internal void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this,
          new PropertyChangedEventArgs(string.Empty));
      }
    
      public class Window88Conv : IMultiValueConverter
      {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
          decimal messWert = 0, nennWert = 0, minTol = 0, maxTol = 0;
          bool result2 = false, result1 = false, result;
    
          if (values[0] != null)
          {
            // Abfragen ob String leer ist
            if (values[1].ToString() == "-1") return new SolidColorBrush(Colors.Black);
            result1 = decimal.TryParse(values[0].ToString(), out messWert);
          }
          if (values[1] != null)
          {
            result2 = decimal.TryParse(values[1].ToString(), out nennWert);
          }
    
          if (values.Length == 4)
          {
            try
            {
              result = decimal.TryParse(values[2].ToString(), out minTol);
            }
            catch
            {
              Debug.Print($"Windows37Conv : IMultiValueConverter: Parse {values[0].ToString()}");
            }
    
            try
            {
              result = decimal.TryParse(values[3].ToString(), out maxTol);
            }
            catch
            {
              Debug.Print($"Windows37Conv : IMultiValueConverter: Parse funktioniert nicht.");
            }
          }
    
          // wenn value 0 und value 1 eine zahl sind
          if (result1 && result2)
          {
            if (messWert < (nennWert + minTol)) return new SolidColorBrush(Colors.Red);
            if (messWert > (nennWert + maxTol)) return new SolidColorBrush(Colors.Red);
            return new SolidColorBrush(Colors.Green);
          }
          else
          {
            return new SolidColorBrush(Colors.Red);
          }
    
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
    }


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP für Developer Technologies)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 27. Januar 2019 14:00