0

To have it simple I have a collection of colors and a combobox that is binded to it. Working, no prop.

But when I want to expand the colors a bit with some Gradient Features, the binding does not work and I have tried numerous things. I really dont see the big difference.

This is what I have and its working:

XAML

<ComboBox x:Name="colorCombo" Style="{StaticResource myComboBoxStyle}" Height="25" ItemsSource="{Binding ColorCollection}"      HorizontalAlignment="Left" Margin="5" Grid.Row="3" Grid.Column="4" Width="110">
<ComboBox.ItemTemplate>
    <DataTemplate>
        <Border Height="15" Width="{Binding ElementName=colorCombo, Path=Width}" Background="{Binding Converter={StaticResource ColorToBrushConverter} }"/>
    </DataTemplate>
</ComboBox.ItemTemplate>

ViewModel:

private Collection<Color> _colorCollection;
public Collection<Color> ColorCollection
{
  get { return _colorCollection; }
  set
  {
    _colorCollection = value;
    this.NotifyPropertyChanged( x => x.ColorCollection );
  }
}

OnLoad of the viewModel the collection gets filled, so dont worry about that. Again this is working!!

Now why would this not work:

XAML

        <ComboBox x:Name="colorCombo2" Style="{StaticResource myComboBoxStyle}" Height="25" ItemsSource="{Binding ColorCollection2}" HorizontalAlignment="Right" Margin="5" Grid.Row="3" Grid.Column="4" Width="110">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <Border Height="15" Width="{Binding ElementName=colorCombo2, Path=Width}" BorderBrush="{Binding BorderColor, Converter={StaticResource ColorToBrushConverter}}" >
                            <Border.Background >
                                <LinearGradientBrush EndPoint="0.504,1.5" StartPoint="0.504,0.03">
                                    <GradientStop Color="{Binding Color1, Converter={StaticResource ColorToBrushConverter}}" Offset="0"/>
                                    <GradientStop Color="{Binding Color2, Converter={StaticResource ColorToBrushConverter}}" Offset="0.567"/>
                                </LinearGradientBrush>
                            </Border.Background>
                        </Border>
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>

ViewModel:

private Collection<ColorGradientHelper> _colorCollection2;
public Collection<ColorGradientHelper> ColorCollection2
{
  get { return _colorCollection2; }
  set
  {
    _colorCollection2 = value;
    this.NotifyPropertyChanged( x => x.ColorCollection2 );
  }
}

HelperClass:

Public class ColorGradientHelper:ObservableBase {

private Color _color1;
public Color Color1
{
  get { return _color1; }
  set
  {
    _color1 = value;
    this.NotifyPropertyChanged( x => x.Color1 );
  }
}

private Color _color2;
public Color Color2
{
  get { return _color2; }
  set
  {
    _color2 = value;
    this.NotifyPropertyChanged( x => x.Color2 );
  }
}

private Color _borderColor;
public Color BorderColor
{
  get { return _borderColor; }
  set
  {
    _borderColor = value;
    this.NotifyPropertyChanged( x => x._borderColor );
  }
}

Converter:

public class ColorToBrushConverter : IValueConverter {
public object Convert( object value, Type targetType, object parameter, CultureInfo culture ) {
  System.Drawing.Color col = (System.Drawing.Color) value;
  Color c = Color.FromArgb( col.A, col.R, col.G, col.B );
  return new SolidColorBrush( c );
}

public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture ) {
  SolidColorBrush c = (SolidColorBrush) value;
  System.Drawing.Color col = System.Drawing.Color.FromArgb( c.Color.A, c.Color.R, c.Color.G, c.Color.B );
  return col;
}

}

user1702369
  • 1,089
  • 2
  • 13
  • 31
  • 1
    "Now why would this not work" - How do you know it's not working? What makes you think it's a binding error and not exception in the converter for example? Please, provide more info about the error. – 3615 Apr 19 '17 at 11:25
  • Thats the thing, there is no error in the output window + it is hitting the converter with no errors. – user1702369 Apr 19 '17 at 11:34
  • If it's hitting the converter, then the bindings should be working. Could you also post the ColorToBrushConverter? – 3615 Apr 19 '17 at 11:35
  • That was my thought too, but the first try (ColorCollection) is using the same converter and that is working. I have update my question with the converter – user1702369 Apr 19 '17 at 11:49
  • 2
    Well, in the second case you are binding to [GradientStop.Color](https://msdn.microsoft.com/en-us/library/system.windows.media.gradientstop.color(v=vs.110).aspx) that is System.Windows.Media.Color and not System.Drawing.Color. So your converter should throw cast exception. – 3615 Apr 19 '17 at 11:54
  • WTF, ok thanks. I will quickly look into that. I think you are correct. All these colors are confusing as hell. Although my converter is not throwing a casting error. – user1702369 Apr 19 '17 at 11:58
  • Looking deeper into GradientStop.Color, I've found this [answer](http://stackoverflow.com/a/1514722/5246145). I think it explains your problem. – 3615 Apr 19 '17 at 12:01
  • Thank you, it is solved. You pointed me in the right direction – user1702369 Apr 19 '17 at 12:38
  • Glad to hear! Btw, I've found a way to work around the databinding problem mentioned before. I'll post my workaround a bit later, maybe it would help someone else. – 3615 Apr 19 '17 at 12:40
  • Thank you I defentily will come back to this post – user1702369 Apr 19 '17 at 12:41

1 Answers1

2

As Shawn Wildermuth states:

GradientStop does not derive from FrameworkElement therefore cannot be data bound.

A workaround is the clever use of Tag property of FrameworkElement. Finally, inspired by rmoore's answer I've ended up with this solution:

XAML

<ComboBox x:Name="colorCombo2" Height="25" ItemsSource="{Binding ColorCollection}" HorizontalAlignment="Right" Margin="5" Width="110">
  <ComboBox.ItemTemplate>
    <DataTemplate>
      <Grid>
        <Grid.Resources>
          <local:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
        </Grid.Resources>
        <Border Height="20" Width="{Binding ElementName=colorCombo2, Path=Width}" 
                BorderThickness="1"
                BorderBrush="{Binding BorderColor, Converter={StaticResource ColorToBrushConverter}}">
          <Border.Background>
            <LinearGradientBrush EndPoint="0.504,1.5" StartPoint="0.504,0.03">
              <GradientStop Color="{Binding ElementName=Border1, Path=Tag}" Offset="0" />
              <GradientStop Color="{Binding ElementName=Border2, Path=Tag}" Offset="0.567" />
            </LinearGradientBrush>
          </Border.Background>
        </Border>
        <Grid Visibility="Collapsed">
          <FrameworkElement Tag="{Binding Color1}" x:Name="Border1" />
          <FrameworkElement Tag="{Binding Color2}" x:Name="Border2" />
        </Grid>
      </Grid>
    </DataTemplate>
  </ComboBox.ItemTemplate>
</ComboBox>

ViewModel (quite identical with OP's ViewModel)

public partial class MainWindow6 : Window, INotifyPropertyChanged {
    public MainWindow6() {
        DataContext = this;
        InitializeComponent();
        var colors = new Collection<ColorGradientHelper>();
        colors.Add(new ColorGradientHelper {
            BorderColor = Colors.Orange,
            Color1 = Colors.Purple,
            Color2 = Colors.White
        });

        colors.Add(new ColorGradientHelper {
            BorderColor = Colors.Orange,
            Color1 = Colors.Black,
            Color2 = Colors.Yellow
        });
        ColorCollection = colors;
    }

    private Collection<ColorGradientHelper> _colorCollection;

    public Collection<ColorGradientHelper> ColorCollection {
        get {
            return _colorCollection;
        }
        set {
            _colorCollection = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

And that output looks like this:

enter image description here

Community
  • 1
  • 1
3615
  • 3,787
  • 3
  • 20
  • 35
  • FrameworkElement is not required for binding its DependencyObject otherwise good answer – MikeT Jun 21 '17 at 09:30
  • you quote that you can't bind anything that doesn't derive from framework element, where it is actually the higher level DependencyObject that is required to enable binding, this is why you can bind to triggers even though they aren't FrameworkElement – MikeT Jun 21 '17 at 09:40
  • @MikeT, wait, but GradientStop does derive from DependencyObject... GradientStop -> Animatable -> Freezable -> DependencyObject – 3615 Jun 21 '17 at 09:45
  • yes otherwise you couldn't bind GradientStop.Color to FrameworkElement .Tag – MikeT Jun 21 '17 at 09:53