lecloneur lecloneur - 3 months ago 28
C# Question

Can't bind UserControl color property to ToggleButton background

I don't get it... I made a simple

UserControl
made of a togglebutton to open a popup containing a
CustomControl
, a colorpicker.

No problem to open/close the popup only if I write a color name for the toggle background color.

But, if I try to bind the UserControl Color property, to see the selected color when popup is closed, then the toggle button doesn't appear and is not working at all.

I must be missing a detail somwhere because I thought it was as simple as this :

<ToggleButton
x:Name="OpenColorPicker"
Style="{DynamicResource ToggleColorPickerStyle}"
Background="{Binding DataContext.SelectedColor, ElementName=ColorPickerWidget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>


Unfortunately not..

Here is the full XAML file :

<UserControl
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"
mc:Ignorable="d"
x:Class="CMiX.ColorSelector"
xmlns:colorPicker="clr-namespace:ColorPicker;assembly=ColorPicker"
xmlns:local="clr-namespace:CMiX"
Height="Auto" Width="Auto" d:DesignWidth="44.533" d:DesignHeight="24.933"
x:Name="ColorPickerWidget">

<UserControl.Resources>
<SolidColorBrush x:Key="BaseDarkColor" Color="#FF323232"/>
<local:ColorToBrushConverter x:Key="ColorToBrush"/>
<Style x:Key="ToggleColorPickerStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Red"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>

<Grid>
<ToggleButton x:Name="OpenColorPicker" Style="{DynamicResource ToggleColorPickerStyle}" Background="{Binding DataContext.SelectedColor, ElementName=ColorPickerWidget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Popup x:Name="PopupColorPicker" SnapsToDevicePixels="True" AllowsTransparency="True" IsOpen="{Binding IsChecked, ElementName=OpenColorPicker}" StaysOpen="True" Placement="Right">
<Border Margin="20, 20, 0, 20" Padding="5">
<colorPicker:ColorPicker x:Name="ColorPicker" Background="{StaticResource BaseDarkColor}" Width="420" Height="210" SelectedColor="{Binding DataContext.SelectedColor, ElementName=ColorPickerWidget, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<colorPicker:ColorPicker.Effect>
<DropShadowEffect BlurRadius="20" Opacity="1" Direction="0"/>
</colorPicker:ColorPicker.Effect>
</colorPicker:ColorPicker>
</Border>
</Popup>
</Grid>




The
UserControl
called ColorPickerWidget has this property :

public static readonly DependencyProperty
SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(Color), typeof(ColorSelector));
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}


Any idea ?

Thank you

EDIT __

I tried this :

<ToggleButton x:Name="OpenColorPicker" Background="{Binding SelectedColor, ElementName=ColorPicker, Converter={StaticResource ColorToBrush}}"/>


With this converter :

public class ColorToBrushConverter : IValueConverter
{
SolidColorBrush _red = new SolidColorBrush(),
_green = new SolidColorBrush(),
_blue = new SolidColorBrush(),
_alpha = new SolidColorBrush(),
_all = new SolidColorBrush();

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var color = (Color)value;
switch ((string)parameter)
{
case "r":
_red.Color = Color.FromRgb(color.R, 0, 0);
return _red;
case "g":
_green.Color = Color.FromRgb(0, color.G, 0);
return _green;
case "b":
_blue.Color = Color.FromRgb(0, 0, color.B);
return _blue;
case "a":
_alpha.Color = Color.FromArgb(color.A,
128, 128, 128);
return _alpha;
case "all":
_all.Color = Color.FromArgb(color.A, color.R, color.G, color.B);
return _all;

}
return Binding.DoNothing;
}

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


But same problem, the button background gets not color.

Answer

There are a few problems with your binding to ToggleButton.Background. First, you're binding to the ColorPicker's DataContext.SelectedColor. The DataContext is the viewmodel. It's probably the same DataContext as the ToggleButton has. So you don't need to go to the ColorPicker to find it. But the ColorPicker isn't binding its SelectedColor to a viewmodel property anyway, so that's probably just a misunderstanding. Get rid of DataContext in that binding, it just sends the Binding off on a wild goose chase to nowhere.

Then you got the name wrong. You named the colorpicker "ColorPicker", but you told the binding to look for "ColorPickerWidget". And Mode=TwoWay is pointless because the ToggleButton can't change it's Background and send the color back to the color picker. But it's background isn't a color anyway; it's a Brush. So that couldn't possibly work.

And because it's expecting a Brush, you need a converter to convert color to brush. Brush properties in XAML are very disorienting because you can give them stuff like the string "Green" or "#882266aa", and they work. But that's because they're associated with a TypeConverter behind the scenes that converts those strings to Brush. But that doesn't work if you bind a Color value to the property. It's not intuitively obvious.

Does this binding work for ToggleButton.Background?

Background="{Binding SelectedColor, ElementName=ColorPicker, Converter={StaticResource ColorToBrush}}"