none
WPF benutzerdefiníerten Button.Background in der cs Datei ändern RRS feed

  • Frage

  • Hallo an alle "beim Coding kriegt mich der Coronablues nicht" Freunde,

    versuche mich gerade in WPF einzuarbeiten. Dabei habe ich für einige Buttons in der App.XAML eine eigene, globale Farbe definiert:

        <Application.Resources>
            <Color x:Key="BtnColor">#FF092995</Color>
        </Application.Resources>

    die ich in der xaml Datei auch prima setzen kann. Jetzt wollte ich aber, dass bei Klick auf den Button, die Hintergrundfarbe geändert und beibehalten wird, bis ein anderer Button geklickt wurde.

    Leider kann man in der cs Datei nicht sagen: btnxYZ.BackGround.Color = irgendnefarbe, weil Backgrund offenbar keine Color Eigenschaft kennt.  

    Die Hilfe bietet mir leider nur 

    btn.Background = new LinearGradientBrush(Colors.LightBlue, Colors.SlateBlue, 90);

    oder

    btn.Background = Brushes.Red;

    an. Aber ich will ja kein Colors.LightBlue, sondern meine benutzerdefinierte Farbe.... (BtnColor)

    Eine direkte Zuweisung via btn.Background = #FF092995 funktioniert leider auch nicht. 

    Meine Frage ist, wie kann ich in der cs Datei dem Buttonhintergrund meine benutzerdefinierte Farbe zuweisen?

    Vielleicht noch eine Zusatzfrage, wenn Ihr Bock drauf habt, hatte auch versucht eine Schleife über die in der XAML Datei definierten Controls laufen zu lassen. Aber mit For Each var in This.Controls bin ich leider auch gescheitert.

    Vielen Dank schon mal in voraus!

    Ralf


    Freundliche Grüße Ralf


    • Bearbeitet Ralf A Samstag, 5. Dezember 2020 19:08
    Samstag, 5. Dezember 2020 19:04

Antworten

  • Hui Ralf,
    hier mal eine Demo zu meinem vorgeschlagenen Ansatz:

    <Window x:Class="WpfApp1.Window020"
            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:WpfApp020"
            mc:Ignorable="d"
            Title="Window020" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Window.Resources>
        <Style TargetType="Button">
          <Setter Property="Margin" Value="5"/>
          <Setter Property="Width" Value="100"/>
        </Style>
        <local:BackGroundConverter x:Key="conv"/>
      </Window.Resources>
      <StackPanel>
        <Button Content="Button1"
                Command="{Binding Cmd}" CommandParameter="Button1"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button1}"/>
        <Button Content="Button2"
                Command="{Binding Cmd}" CommandParameter="Button2"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button2}"/>
        <Button Content="Button3"
                Command="{Binding Cmd}" CommandParameter="Button3"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button3}"/>
        <Button Content="Button4"
                Command="{Binding Cmd}" CommandParameter="Button4"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button4}"/>
      </StackPanel>
    </Window>

    Der Code dazu:

    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfApp020
    {
      public class ViewModel : INotifyPropertyChanged
      {
        public string LastClicked { get; set; }
        public ICommand Cmd
        {
          get => new WpfApp1.Helper.RelayCommand((state) =>
            {
              LastClicked = state.ToString();
              OnPropertyChanged(nameof(LastClicked));
            });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    
      public class BackGroundConverter : IValueConverter
      {
        private SolidColorBrush brush1 = new SolidColorBrush(SystemColors.ControlLightColor);
        private SolidColorBrush brush2 = new SolidColorBrush((Color)Application.Current.Resources["BtnColor"]);
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => 
          (value != null && parameter != null && value.ToString() == parameter.ToString()) ? brush2 : brush1;
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
    }
    

    Und das Ergebnis:


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 6. Dezember 2020 15:14

Alle Antworten

  • HI,

    due kannst dir den Verweis auf die Farbressource holen und dann im Konstruktor von SolidColorBrush die Farbressource angeben.

    Ich würde aber ganz anders herangehen. Im ViewModel würde ich eine Eigenschaft vorsehen, die dem letzten geklickten Button entspricht. Die Hintergrundfarbe des Buttons wird an diese Eigenschaft gebunden, Zusätzlich wird ein Converter genutzt mit einem Converterparameter, entsprechend dem Button. Wenn also Eigenschaftswert und Parameterwert übereinstimmen, wird ein SoliColorBrush mit der Farbe der Ressource geliefert, sonst eine andere Farbe.

    Das Umschalten des Eigenschaftswertes wird in der Execute-Methode der gebundenen ICommand-Eigenschaft realisiert.

    Ich hoffe, das war verständlich.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 5. Dezember 2020 19:56
  • ...hab inzwischen eine Lösung gefunden. Sie löst zwar das Problem, beantwortet aber leider meine Frage nicht.

    Für die, die es interessiert:

    Zunächst hab ich eine private Variable vom Typ Button mit dem Namen lastBtn erstellt.

    Im Clickereignis der Buttons bin ich dann (exemplarisch) so vorgegangen:

    private void mnubtns_Click(object sender, RoutedEventArgs e)
            {
                Button btn = (Button)sender;
                if (lastBtn != null)
                {
                    lastBtn.Background = btn.Background; 
                }
                lastBtn = btn;
                btn.Background = Brushes.Yellow;
    
            }

    Falls aber trotzdem jemand eine Antwort auf meine Frage(n) hat.... ich würde mich freuen!


    Freundliche Grüße Ralf

    Samstag, 5. Dezember 2020 19:58
  • Hi Peter,

    vielen Dank für Deine Antwort! Klingt hochprofessionell... allerdings - ich bin nur Hobbyprogrammierer und ein ViewModel (das sicher angebracht wäre) hab ich auch keins. So richtig verstanden hab ich es also nicht, aber zumindest hast Du Wege aufgezeigt, wie man es machen sollte. Bis dahin werde ich aber noch eine Weile brauchen....;)


    Freundliche Grüße Ralf

    Samstag, 5. Dezember 2020 20:08
  • Hi,
    und warum gefällt dir mein universeller Lösungsweg nicht?

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 5. Dezember 2020 20:09
  • Hi Peter,

    ....smile... das ist keine Frage des nicht Gefallens. Deine Lösung ist mit Sicherheit die bessere Option. Deshalb hatte ich Deine Antwort ja auch als hilfreich bewertet.

    Das Problem liegt auf meiner Seite. Ich bin schlicht und ergreifend heillos mit Deiner Antwort überfordert. Während Du schon den Wasserstoffantrieb entwickelst, werkel ich noch am 2-Takt Ottomotor herum... :(

    Im Übrigen kam mein 2. Post (der mit meinem Lösungsvorschlag) gerade mal 2 Minuten nach Deinem an. Die Antworten hatten sich also überschnitten. Hab während des Schreibens Deinen Beitrag einfach nicht mitbekommen. War also keine Ignoranz...

    ...und...  ein Blick auf Deine Homepage lohnt sich! Kann ich nur empfehlen. Hat mir hier eine weitere Frage erspart! Danke für die vielen Beispiele!


    Freundliche Grüße Ralf





    • Bearbeitet Ralf A Sonntag, 6. Dezember 2020 13:15
    Sonntag, 6. Dezember 2020 12:48
  • Hui Ralf,
    hier mal eine Demo zu meinem vorgeschlagenen Ansatz:

    <Window x:Class="WpfApp1.Window020"
            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:WpfApp020"
            mc:Ignorable="d"
            Title="Window020" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Window.Resources>
        <Style TargetType="Button">
          <Setter Property="Margin" Value="5"/>
          <Setter Property="Width" Value="100"/>
        </Style>
        <local:BackGroundConverter x:Key="conv"/>
      </Window.Resources>
      <StackPanel>
        <Button Content="Button1"
                Command="{Binding Cmd}" CommandParameter="Button1"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button1}"/>
        <Button Content="Button2"
                Command="{Binding Cmd}" CommandParameter="Button2"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button2}"/>
        <Button Content="Button3"
                Command="{Binding Cmd}" CommandParameter="Button3"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button3}"/>
        <Button Content="Button4"
                Command="{Binding Cmd}" CommandParameter="Button4"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button4}"/>
      </StackPanel>
    </Window>

    Der Code dazu:

    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfApp020
    {
      public class ViewModel : INotifyPropertyChanged
      {
        public string LastClicked { get; set; }
        public ICommand Cmd
        {
          get => new WpfApp1.Helper.RelayCommand((state) =>
            {
              LastClicked = state.ToString();
              OnPropertyChanged(nameof(LastClicked));
            });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    
      public class BackGroundConverter : IValueConverter
      {
        private SolidColorBrush brush1 = new SolidColorBrush(SystemColors.ControlLightColor);
        private SolidColorBrush brush2 = new SolidColorBrush((Color)Application.Current.Resources["BtnColor"]);
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => 
          (value != null && parameter != null && value.ToString() == parameter.ToString()) ? brush2 : brush1;
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
    }
    

    Und das Ergebnis:


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Sonntag, 6. Dezember 2020 15:14
  • Hi Peter,

    erstmal sorry für die späte Antwort, hatte aber mit keiner mehr gerechnet und sie erst jetzt entdeckt. Und dann noch eine so ausführliche! Ein dickes Dankeschön an Dich!

    Hab es auch gleich ausprobiert, stelle mich aber offensichtlich zu blöd an.

    Wie bin ich vorgegangen?:

    • eine neue WPF Anwendung mit dem Namen WpfApp1 erstellt.
    • eine neues Fenster mit dem Namen Window020 eingefügt.
    • darin die Original XAML - Anweisungen durch Deine ersetzt.
    • Deine  xmlns:local="clr-namespace:WpfApp020" durch WpfApp1 ersetzt. Hab ja keinen WpfApp020 Namespace. Ist ja alles im gleichen Projekt.
    • eine neue Klasse Namens ViewModell erstellt.
    • Darin Deinen C# Code eingefügt und den Namespace angepasst.

    Wenn ich jetzt das Projekt erstelle, bekomme ich folgende Fehlermeldungen:

    • Die 'DataContext'-Eigenschaft unterstützt keine Werte des Typs 'ViewModel'. WpfApp1 C:\CS Test\WpfApp1\Window020.xam20
    • Der Name "ViewModel" ist im Namespace "clr-namespace:WpfApp1" nicht vorhanden. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml20
    • Der Name "BackGroundConverter" ist im Namespace "clr-namespace:WpfApp1" nicht vorhanden. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml 17 
    • und last but not least: DataContext ist nicht festgelegt.

    Hab auch versucht, die Klasse ViewModell in einen Ordner gleichen Namens zu packen oder den Code der Klasse ViewModell in in die Behinddatei von Window20.... alles vergebens... lediglich die Fehlermeldungen ändern sich geringfügig.

    Kannst Du mir bitte noch sagen, was wohin gehört, damit es funktioniert?

    Vielen Dank im voraus!

    PS: Vermutlich muss ich  <local:BackGroundConverter x:Key="conv"/> noch als Ressource in der App.xaml einfügen.... probier ich gleich mal...

    ...seufz.... aber wie?

      <Application.Resources>
            <local:BackGroundConverter x:Key="conv"></local:BackGroundConverter>
        </Application.Resources>

    funktioniert ebenfalls nicht...


    Freundliche Grüße Ralf







    • Bearbeitet Ralf A Mittwoch, 9. Dezember 2020 13:28
    Mittwoch, 9. Dezember 2020 13:05
  • Hi Ralf,
    du kannst meinen Code unverändert in ein Projekt kopieren.

    1. Neues WPF Projekt anlegen mit Namen (bzw. NameSpace WpfApp1)

    2. Window mit Namen Window020 hinzufügen.

    3. in App.xaml Wondow020 als Start-Uri eintragen.

    4. XAML-Code in Window020.xaml reinkopieren

    5. Klasse dem Projekt hinzufügen

    6. In Klassencode den von mir geposteten Programmcode kopieren (ersetzen)

    Starten und dann sollte kein Fehler kommen.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Mittwoch, 9. Dezember 2020 15:09
  • Hi Peter,

    "du kannst meinen Code unverändert in ein Projekt kopieren."

    leider nicht. Hab alles so gemacht und probiert... klappt nicht.

    Dann habe ich alle Vorkommen des Namespaces von WpfApp020 in WpfApp1 geändert. Mein Namespace ist ja nun einmal WpfApp1. Den Namespace  WpfApp020 gibt es ja bei mir nicht...

    Bekomme jetzt nur noch 2 Fehler angezeigt:

    • Der Name "ViewModel" ist im Namespace "clr-namespace:WpfApp1" nicht vorhanden. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml 10
    • Der Name "BackGroundConverter" ist im Namespace "clr-namespace:WpfApp1" nicht vorhanden. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml 17

    Die Klasse ViewModell liegt aber im angegebenen Namesspace WpfApp1 und der in Zeile 17 derWindow020.xaml angemeckerte BackGroundConverter ebenfalls in diesem Namespace, in der Klasse VieWModel

    Wie gesagt... wenn ich Deine Vorgehensweise unverändert übernehme, krieg ich noch mehr Fehler angezeigt. Unter anderem scheint da noch eine Klasse Helper zu fehlen.... in Zeile 17 der Klasse ViewModel:

     get => new WpfApp1.Helper.RelayCommand((state) =>

    Tut mir leid, dass ich Dir soviel Mühe mache, nur weil ich mich zu dämlich anstelle.... 



    Freundliche Grüße Ralf





    • Bearbeitet Ralf A Mittwoch, 9. Dezember 2020 15:58
    Mittwoch, 9. Dezember 2020 15:49
  • Hi Ralf,
    wenn du namensräume änderst, musst du das auch überall machen, also auch in xmlns-Anweisungen. Das hast du vermutlich vergessen.

    Mit der RealayCommand Klasse im Namensraum WpfApp1.Helper hast du Recht. Die habe ich nicht extra genannt, da es dazu Unmengen von Hinweise und Code im Internet gibt und eigentlich zum Grundwissen gehört. Ich nutze einen Ordner Helper im Projekt mit folgendem Code:

    using System;
    using System.Diagnostics;
    using System.Windows.Input;
    
    namespace WpfApp1.Helper
    {
      /// <summary>
      /// A command whose sole purpose is to relay its functionality 
      /// to other objects by invoking delegates. 
      /// The default return value for the CanExecute method is 'true'.
      /// <see cref="RaiseCanExecuteChanged"/> needs to be called whenever
      /// <see cref="CanExecute"/> is expected to return a different value.
      /// </summary>
      public class RelayCommand : ICommand
      {
        #region Private members
        /// <summary>
        /// Creates a new command that can always execute.
        /// </summary>
        private readonly Action<object> _execute;
    
        /// <summary>
        /// True if command is executing, false otherwise
        /// </summary>
        private readonly Predicate<object> _canExecute;
        #endregion
    
        /// <summary>
        /// Initializes a new instance of <see cref="RelayCommand"/> that can always execute.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        public RelayCommand(Action<object> execute) : this(execute, canExecute: null) { }
    
        /// <summary>
        /// Initializes a new instance of <see cref="RelayCommand"/>.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
          if (execute == null) throw new ArgumentNullException("execute");
          this._execute = execute;
          this._canExecute = canExecute;
        }
    
        /// <summary>
        /// Raised when RaiseCanExecuteChanged is called.
        /// </summary>
        public event EventHandler CanExecuteChanged;
    
        /// <summary>
        /// Determines whether this <see cref="RelayCommand"/> can execute in its current state.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        /// </param>
        /// <returns>True if this command can be executed; otherwise, false.</returns>
        public bool CanExecute(object parameter) => this._canExecute == null ? true : this._canExecute(parameter);
    
        /// <summary>
        /// Executes the <see cref="RelayCommand"/> on the current command target.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require data to be passed, this object can be set to null.
        /// </param>
        public void Execute(object parameter) => this._execute(parameter);
    
        /// <summary>
        /// Method used to raise the <see cref="CanExecuteChanged"/> event
        /// to indicate that the return value of the <see cref="CanExecute"/>
        /// method has changed.
        /// </summary>
        public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
      }
    }
    


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Mittwoch, 9. Dezember 2020 16:27
  • Hi Peter,

    "wenn du namensräume änderst, musst du das auch überall machen, also auch in xmlns-Anweisungen. Das hast du vermutlich vergessen."

    ...nee nee, das passte schon alles...

    "Mit der RealayCommand Klasse im Namensraum WpfApp1.Helper ... Die habe ich nicht extra genannt, ...da es eigentlich zum Grundwissen gehört. :"

    ...smile... Da hast Du offenbar mehr Vertrauen in mein Grundwissen als ich selbst...:) Mein Grundwissen beschränkt sich wirklich nur auf die Basics... erweitert sich aber Dank Dir gerade...

    Die RealayCommand Klasse hat es (beinahe) gebracht. Zumindest startet die Anwendung erst einmal ohne sofort abzubrechen. Das heißt, InitializeComponent(); von Window020 wird ausgeführt, bleibt dann aber hier hängen:

    brush1 hat den Wert {#FFE3E3E3} für Color (wird ja über eine Konstante definiert), brush2 ist, wie angezeigt, = null. 

    Hab mal versucht zu finden, wo die Werte eigentlich definiert werden, leider nix gefunden. Der für brush2 angegebene Parameter BtnColor muss offenbar noch in Ressources definiert werden. Also hab ich in der App.xaml das ergänzt:

        <Application.Resources>
            <SolidColorBrush x:Key="BtnColor">#FFCFCACA</SolidColorBrush>
        </Application.Resources>

    bekomme dann aber sofort wieder diese Fehlermeldungen:

    • Das Tag "ViewModel" ist im XML-Namespace "clr-namespace:WpfApp1" nicht vorhanden. Zeile 10 Position 10. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml 10
    • Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt. WpfApp1 C:\CS Test\WpfApp1\Window020.xaml 17

    Es ist zum Mäusemelken....


    Freundliche Grüße Ralf







    • Bearbeitet Ralf A Mittwoch, 9. Dezember 2020 18:15
    Mittwoch, 9. Dezember 2020 18:06
  • Hi Ralf,
    bezüglich BtnColor habe ich mich auf dein Ausgangsposting bezogen und bin davon ausgegangen, dass du das so in deinem Projekt auch definiert hast.

    Wenn die ViewModel-Klasse bei dir nicht im Namensraum WpfApp1 gefunden wird, wird sie wohl in einem anderen Namensraum sein. Das kann man einfach über den Class View prüfen. In meinem geposteten Beispiel ist die ViewModel Klasse im Namensraum WpfApp020. Möglicherweise hast du einen Buchstabendreher oder Vertipper im entsprechenden xmlns-Attribut.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Mittwoch, 9. Dezember 2020 18:42
  • ..ich denke nicht, dass es am Namensraum liegt.

    Warum?

    • als ich den Helper Ordner incl. enthaltener Klasse (nochmal Danke dafür) eingefügt habe, startete die App wenigstens. 
    • der Fehler taucht doch erst in der ViewModel Klasse die Anweisung
           

     public class BackGroundConverter : IValueConverter
        {
            private SolidColorBrush brush1 = new SolidColorBrush(SystemColors.ControlLightColor);
    private SolidColorBrush brush2 = new SolidColorBrush((Color)Application.Current.Resources["BtnColor"]);

    ausgeführt werden soll, weil der Parameter BtnColor zumindest für mich nirgendwo erkennbar deklariert und initialisiert wurde.

    Deshalb hatte ich ja in der App.xaml BtnColor als SolidColorBrush als Ressource hinzugefügt. Nur.... das war offensicjhtlich falsch, denn dadurch erhielt ich ja die alten Fehlermeldungen.

    Entferne ich  und die Ressource aus der App.xaml und versuche die Variable durch eine Konstante zu ersetzen:

    private SolidColorBrush brush2 = new SolidColorBrush(SystemColors.ActiveCaptionColorKey);

    oder so zu definieren:  private SolidColorBrush brush2 = new SolidColorBrush(#FFCFCACA);

    wird das nur mit Fehlermeldungen quittiert... 

    brush2  macht irgendwie Probleme.... und ich habe keine Ahnung warum...


    Freundliche Grüße Ralf


    • Bearbeitet Ralf A Mittwoch, 9. Dezember 2020 19:32
    Mittwoch, 9. Dezember 2020 19:30
  • Hi Ralf,
    mir ist unklar, was und warum du in meinem geposteten Code hgeändert hast. Hier nochmals in einem neuen Projekt mein Beispiel. Bitte kopiere das aber ohne Änderungen, die du nicht überblickst.

    App.xaml:

    <Application x:Class="Demo_Ralf_A.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:Demo_Ralf_A"
                 StartupUri="MainWindow.xaml">
      <Application.Resources>
        <Color x:Key="BtnColor">#FF092995</Color>
      </Application.Resources>
    </Application>
    

    MainWindow.xaml:

    <Window x:Class="WpfApp1.MainWindow"
            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="MainWindow" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Window.Resources>
        <Style TargetType="Button">
          <Setter Property="Margin" Value="5"/>
          <Setter Property="Width" Value="100"/>
        </Style>
        <local:BackGroundConverter x:Key="conv"/>
      </Window.Resources>
      <StackPanel>
        <Button Content="Button1"
                Command="{Binding Cmd}" CommandParameter="Button1"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button1}"/>
        <Button Content="Button2"
                Command="{Binding Cmd}" CommandParameter="Button2"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button2}"/>
        <Button Content="Button3"
                Command="{Binding Cmd}" CommandParameter="Button3"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button3}"/>
        <Button Content="Button4"
                Command="{Binding Cmd}" CommandParameter="Button4"
                Background="{Binding LastClicked, Converter={StaticResource conv}, ConverterParameter=Button4}"/>
      </StackPanel>
    </Window>

    Und Klassendatei classes.cs:

    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace WpfApp1
    {
      public class ViewModel : INotifyPropertyChanged
      {
        public string LastClicked { get; set; }
        public ICommand Cmd
        {
          get => new RelayCommand((state) =>
          {
            LastClicked = state.ToString();
            OnPropertyChanged(nameof(LastClicked));
          });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    
      public class BackGroundConverter : IValueConverter
      {
        private SolidColorBrush brush1 = new SolidColorBrush(SystemColors.ControlLightColor);
        private SolidColorBrush brush2 = new SolidColorBrush((Color)Application.Current.Resources["BtnColor"]);
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
          (value != null && parameter != null && value.ToString() == parameter.ToString()) ? brush2 : brush1;
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
          throw new NotImplementedException();
        }
      }
    
      public class RelayCommand : ICommand
      {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;
        public RelayCommand(Action<object> execute) : this(execute, canExecute: null) { }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
          if (execute == null) throw new ArgumentNullException("execute");
          this._execute = execute;
          this._canExecute = canExecute;
        }
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter) => this._canExecute == null ? true : this._canExecute(parameter);
        public void Execute(object parameter) => this._execute(parameter);
        public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
      }
    }


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Mittwoch, 9. Dezember 2020 21:19
  • Hi Peter,

    Du bist mein Held!

    Jetzt läuft's. Auch, wenn ich entgegen Deiner Empfehlung und entgegen Deinem Verständnis Änderungen vornehmen musste, damit es lauffähig ist. Du warst etwas inkonsequent bei der Namensraumvergabe... naja, war ja auch schon spät gestern....;)

    Nochmal Danke für Deine Geduld und Ausdauer! 


    Freundliche Grüße Ralf

    Donnerstag, 10. Dezember 2020 09:35
  • Hi Ralf,
    ich weiß zwar nicht, was du ändern musstest, aber bei mir läuft es genau so, wie ich es gepostet habe. Vermutlich hast du bei der Neuanlage des Projektes andere Namensräume angegeben als ich und musstest sie dann teilweise korrigieren. 

    Das ist aber egal, Hauptsache es funktioniert jetzt und du hast meine genutzte Logik verstanden.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Donnerstag, 10. Dezember 2020 16:25
  • Hi Peter,

    Problem ist ja gelöst, aber Deine Bemerkung, dass es ohne Änderung bei Dir läuft, das erstaunt mich jetzt wirklich. Im Beispiel verwendest Du 2 unterschiedliche Namensräume.... 

    Einmal: 

    <Application x:Class="Demo_Ralf_A.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:Demo_Ralf_A"
                 StartupUri="MainWindow.xaml">

    und dann wieder:

    <Window x:Class="WpfApp1.MainWindow"
            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"

    Auch in der ViewModel Klasse wieder:

    namespace WpfApp1

    Wie ist das möglich? Ich musste da überall Demo_Ralf_A draus machen...


    Freundliche Grüße Ralf

    Donnerstag, 10. Dezember 2020 16:45
  • Hi Ralf,
    jetzt ist alles klar. Ich hatte ein Projekt mit Namensraum Demo_Ralf_A angelegt, dann aber, um dich nicht zu verwirren, den Standard-Namensraum auf WpfApp1 in den Projekteigenschaften gesetzt. Alle weiteren Elemente (MainWindow und Klassen) wurden dann automatisch mit Namensraum WpfApp1 angelegt. Damit hatte ich zu Verwirrung bei dir gesorgt.

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Donnerstag, 10. Dezember 2020 19:05