Как привязать цвета GradientStop или свойство GradientStops в Silverlight?

Я хочу иметь возможность иметь динамический градиент в Silverlight, например:

<RadialGradientBrush GradientOrigin="0.20,0.5" Center="0.25,0.50" 
                     RadiusX="0.75" RadiusY="0.5">
  <GradientStop Color="{Binding Path=GradientStart}" Offset="0" />
  <GradientStop Color="{Binding Path=GradientEnd}" Offset="1" />
</RadialGradientBrush>

Я привязан к двум свойствам, которые возвращают тип "Цвет", однако я всегда получаю это сообщение:

AG_E_PARSER_BAD_PROPERTY_VALUE

Если я пытаюсь привязать коллекцию GradientStop, у нее также возникает та же проблема, каково решение этой проблемы, которое:

  1. Позволяет изменить начало и конец градиента во время выполнения
  2. Работает в Silverlight 3.0 и не является решением WPF

Если есть обходной путь или в любом случае для дублирования этого поведения, это было бы приемлемо, у меня есть решения, которые работают с LinearGradients, так как я могу просто привязать кое-что свойство "Fill" к этому - однако в этой ситуации это не сработает, плюс может другие типы градиентов, которые я могу использовать, и другие, которые могут использоваться в будущем, к которым будет применяться это решение / альтернатива.

4 ответа

Решение

Проблема в том, что GradientStop не является производным от FrameworkElement, поэтому не может быть привязан к данным. К сожалению, это означает, что вы должны установить его из кода.

Чтобы действительно это произошло, у вас есть два варианта.

Свяжите отображаемые элементы Свойство Brush со свойством Brush в данных

У источника данных есть свойство, которое показывает, какую кисть вы хотите использовать для каждого элемента, и вы привязываете свойство отображаемого элемента, который берет кисть, скажем, Fill имущество. Это работает, если набор различных значений, которые вы имели бы для пар значений Start и Stop, невелик. Вы создадите экземпляр каждой кисти для каждой пары, а затем элемент данных предоставит правильный.

Привязка отображаемых элементов к свойству Brush с помощью конвертера значений

Если значения Start и Stop имеют большую переменную, вам потребуется новый экземпляр типа Brush для каждого отображаемого элемента. В этом случае вы бы связали свойство кисти отображаемых элементов с помощью конвертера значений, например:

 <Rectangle Fill="{Binding Converter={StaticResource MyBrushBuilder} }" ... >

Смотрите этот ответ для полного описания построения конвертера.

В этом случае реализация метода преобразования будет выглядеть так:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
  YourItemsType item = (YourItemsType)value;

  var start = new GradientStop();
  start.Offset = 0;
  start.Color = item.GradientStart;

  var stop = new GradientStop();
  stop.Offset = 1;
  stop.Color = item.GradientStop;

  var result = new RadialGradientBrush();
  result.GradientOrigin = new Point(0.20, 0.5);
  result.Center = new Point(0.25, 0.5);
  result.RadiusX = 0.75;
  result.RadiusY = 0.5;
  result.GradientStops = new GradientStopCollection();
  result.GradientStops.Add(start);
  result.GradientStops.Add(stop);

  return result;
}

Предостережение

Всякий раз, когда происходит связывание данных, создается целая куча кистей по одной для каждого элемента. Это может быть дорого и нежелательно. Следовательно, если этот подход с привязкой к конвертеру будет сочтен необходимым, я рекомендую вам использовать статический словарь кистей. Ключом в этом словаре будет хэш двух цветов. Вы будете создавать новую кисть только при необходимости и повторно использовать ранее созданную кисть, когда это возможно.

Подтвердили ли вы тип, используемый в качестве DataContext где определяется ваша градиентная кисть? Как вы не указали Source в вашей привязке, он будет использовать DataContext по умолчанию.

Довольно старый пост, но это возможно (сейчас), так что вот мое решение. Мой XAML-код:

<Ellipse.Resources>
    <local:ColorConverter x:Key="ColorConverter"/>
</Ellipse.Resources>
<Ellipse.Fill>
    <RadialGradientBrush>
        <GradientStop Color="{Binding MenuColor2, Source={x:Static p:Settings.Default}, Converter={StaticResource ColorConverter}}" Offset="1" />
        <GradientStop Color="{Binding MenuColor1, Source={x:Static p:Settings.Default}, Converter={StaticResource ColorConverter}}" Offset="0.85" />
    </RadialGradientBrush>
</Ellipse.Fill>

А вот и мой C#-код.

[ValueConversion(typeof(System.Drawing.Color), typeof(System.Windows.Media.Color))]
public class ColorConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is System.Drawing.Color)
        {
            var clr = (System.Drawing.Color)value;
            return System.Windows.Media.Color.FromArgb(clr.A, clr.R, clr.G, clr.B);
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is Color)
        {
            var clr = (Color)value;
            return System.Drawing.Color.FromArgb(clr.A, clr.R, clr.G, clr.B);
        }
        return value;
    }
}
Другие вопросы по тегам