Laden...

WPF : ComboBox für Hintergrundfarben-Settings

Erstellt von Kingkook vor 11 Jahren Letzter Beitrag vor 10 Jahren 3.247 Views
K
Kingkook Themenstarter:in
69 Beiträge seit 2011
vor 11 Jahren
WPF : ComboBox für Hintergrundfarben-Settings

Liebe Com,

ich habe für meine Anwendung folgende ApplicationSettings :

MySetting.settings


Name      Type                         Value
myColor   System.Windows.Media.Color   #FFFFFF

AutoGenerated Code :

MySettings.Desinger.cs

    [global::System.Configuration.UserScopedSettingAttribute()]
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [global::System.Configuration.DefaultSettingValueAttribute("#FFFFFFFF")]
    public global::System.Windows.Media.Color myColor {
        get {
            return ((global::System.Windows.Media.Color)(this["myColor"]));
        }
        set {
            this["myColor"] = value;
        }
    }

Diesen Wert will ich in einem Einstellungsfenster verändern:

configWindow.xaml

<Window x:Class="myApp.configWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:properties="clr-namespace:myApp.Properties"
    xmlns:converter="clr-namespace:myApp.Converter"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"      
    Title="myApp" Height="557" Width="626">
<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    <converter:myColorConverter x:Key="ColorConverter"/>
    <properties:MySettings x:Key="config"/>
    <ObjectDataProvider ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="colorPropertiesOdp" />
</Window.Resources>
<Grid Height="524" Width="615" DataContext="{StaticResource config}">
    <TabControl Height="508" HorizontalAlignment="Left" Name="tabControl1" VerticalAlignment="Top" Width="616" BorderThickness="0" Margin="1,11,0,0">
        <TabItem Header="options" Name="tabItemOptions">
            <Grid Height="484">
                <GroupBoxHeight="330" Margin="6,149,15,0" Name="groupBox2" >
                    <Grid Height="313">
                        <ComboBox x:Name="comboBoxMyColor" Height="23" HorizontalAlignment="Left" Margin="302,34,0,0" VerticalAlignment="Top" Width="120" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}" SelectedItem="{Binding Path=Default.myColor, Converter={StaticResource ColorConverter}, Mode=TwoWay}">
                            <ComboBox.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal">
                                        <Label Background="{Binding Path=Name}" Content="{Binding Path=Name}" Height="{Binding ActualHeight, ElementName=comboBoxNotificationColor}" Width="{Binding ActualWidth, ElementName=comboBoxNotificationColor}"/>
                                    </StackPanel>
                                </DataTemplate>
                            </ComboBox.ItemTemplate>
                        </ComboBox>
                    </Grid>
                </GroupBox>
            </Grid>
        </TabItem>
    </TabControl>
</Grid>

Doch wenn ich den Wert in der Combobox ändere, werden die Einstellungen nicht verändert. Bei anderen Controls (z.B. Slider,Checkbox...) funktioniert es ohne Probleme.Wo liegt mein Fehler?

Gruß und Danke
Kooki

5.299 Beiträge seit 2008
vor 10 Jahren

Die Items, die dein ObjectDataProvider liefert sind vom Typ PropertyInfo.
Die Property, an die du das SelectedItem binden willst, ist aber vom Typ Color.
Das müsste einen BindingMismatch geben.

Der frühe Apfel fängt den Wurm.

K
Kingkook Themenstarter:in
69 Beiträge seit 2011
vor 10 Jahren

Sehr gutes Argument 😉

Ich habe jetzt einen Converter hinzugefügt, um aus den PropertyInfo's eine List<Color> zu erstellen.


    public class PropertyInfo2ColorListConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            List<Color> colors = new List<Color>();

            if (value == null)
                return null;

            System.Collections.IEnumerator enumerator = ((System.Collections.IEnumerable)value).GetEnumerator();
            while (enumerator.MoveNext())
            {
                Object current = ((PropertyInfo)enumerator.Current).GetValue(enumerator.Current, null);
                colors.Add((Color)current);
            }
            return colors;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException(); 
        }
    }

Damit habe ich weiterhin alle Farben in der ComboBox.
Leider wird bei der Auswahl einer neuen Farbe immernoch nicht die Konfiguration geändert. Fehlt noch was?

Gruß Kooki

5.299 Beiträge seit 2008
vor 10 Jahren

du brauchst nicht einen Converter, der aus einem enumerable eine enumerable<Color> macht, sondern einen Converter, der aus genau einem PropertyInfo genau eine Color macht.
Und Vorsicht! wenn du bidirektional bindest (wovon ich bei Settings ausgehe), muß ConvertBack auch unterstützt werden.

Imo wäre es einfacher, ein richtiges Viewmodel zu coden, was halt eine IList<Color> bereitstellt.
Dafür könnteste dann den ObjectDataProvider rauswerfen.

Der frühe Apfel fängt den Wurm.

K
Kingkook Themenstarter:in
69 Beiträge seit 2011
vor 10 Jahren

du brauchst nicht einen Converter, der aus einem enumerable eine enumerable<Color> macht, sondern einen Converter, der aus genau einem PropertyInfo genau eine Color macht.

Ja, das sehe ich ein. Aber meine ComboBox enthält jetzt eine Liste mit Werten vom Typ Color. SelectedValue ist demnach ja auch vom Typ Color. wenn ich zum Beispiel folgendes xaml habe :

                                <ComboBox x:Name="comboBoxNotificationColor" ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}, Converter={StaticResource PropertyConverter}}" SelectedValue="{Binding Path=Default.NotificationColor, Converter={StaticResource TestConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

und hinter "TestConverter" folgender Code steckt :

    public class TestConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    }

Dann sehe ich beim debuggen, dass der "value" beim Convert als auch beim ConvertBack vom typ Color ist. Ich habe auch überlegt gehabt, ein Viewmodel einzubinden, aber die Settings selbst reagieren doch auf PropertyChanged. Es klappt ja bei allen anderen Werten, und auch bei der Combobox werden jetzt Colors rein und rausgereicht. Brauch ich denn dafür wirklich zwangsläufig noch ein extra Viewmodel?

5.299 Beiträge seit 2008
vor 10 Jahren
<Window x:Class="SettingsBinding.Views.Window1"
        Title="Window1" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:SettingsBinding"         
    xmlns:props="clr-namespace:SettingsBinding.Properties"         
        MinWidth="200" Height="300">
  <FrameworkElement.DataContext>
    <my:MainViewModel />
  </FrameworkElement.DataContext>
  <FrameworkElement.Resources>
    <my:Color2Brush x:Key="Color2Brush" />
  </FrameworkElement.Resources>
    <Grid>
    <ListBox ItemsSource="{Binding Path=KnownColors}" 
        SelectedItem="{Binding Path=myColor, Source={x:Static props:Settings.Default}}" >
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <StackPanel Orientation="Horizontal">
            <Label Background="{Binding Converter={StaticResource Color2Brush}}" Content="{Binding}" />
          </StackPanel>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ListBox>
  </Grid>
</Window>

using System.Windows.Media;
using System.Windows.Data;
using System.Globalization;
using System;

namespace SettingsBinding {

   public class MainViewModel {
      private static Color[] _KnownColors = Array.ConvertAll(typeof(Colors).GetProperties(), pi => (Color)pi.GetValue(null, null));
      public Color[] KnownColors { get { return _KnownColors; } }
   }

   public class Color2Brush : IValueConverter {
      object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) {
         return new SolidColorBrush((Color)value);
      }
      object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
         return ((SolidColorBrush)value).Color;
      }
   }

}

namespace SettingsBinding {
   /// <summary>
   /// Interaktionslogik für "App.xaml"
   /// </summary>
   public partial class App : Application {
      public App() {
         this.Exit += (s,e) => SettingsBinding.Properties.Settings.Default.Save();
      }
   }
}

Der frühe Apfel fängt den Wurm.

K
Kingkook Themenstarter:in
69 Beiträge seit 2011
vor 10 Jahren

Hallo ErfinderDesRades,

danke für deine Hilfe, habe deine Lösung etwas abgewandelt übernommen. Ich arbeite weiterhin mit dem ObjectDataProvider, statt dem Color[] KnownColors.
Eigentlich hat ein zusätzliches Attribut gereicht, um die Datenbindung zu setzten :

Source={x:Static props:Settings.Default}

Ich bin mir zwar nicht sicher, warum er diese Sourceangabe benötigt, da die anderen Controls auch keinen Source Eintrag verlangen, aber es funktioniert jetzt ohne Probleme 😃

Danke nochmal, und kann gern geschlossen werden!

Gruß Kooki