Reputation: 2256
I'm trying to build a simple Color Picker as a WPF UserControl, but I'm having trouble getting the selected color back to the main window.
My main window XAML looks like this:
<Window x:Class="ColorPicker.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:ColorPicker"
Title="TestWindow" SizeToContent="WidthAndHeight">
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding MyColor}" Margin="10,10,10,10" Width="100" Height="300"/>
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" Width="200" Height="300"/>
</StackPanel>
</Window>
and the view model and code-behind:
namespace ColorPicker
{
public class TestViewModel
{
public TestViewModel()
{
MyColor = new SolidColorBrush(Color.FromRgb(255, 0, 0));
}
public Brush MyColor { get; set; }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
}
The WPFColorPicker usercontrol XAML is:
<UserControl x:Class="ColorPicker.WPFColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Rectangle x:Name="rtlfill" Fill="{Binding SelectedColor}" HorizontalAlignment="Stretch" Height="60" Margin="10,10,10,10" Stroke="Black" VerticalAlignment="Top"/>
<ListBox HorizontalAlignment="Stretch" SelectedValue="{Binding SelectedColor}" VerticalAlignment="Stretch" Margin="0,0,0,81" ScrollViewer.HorizontalScrollBarVisibility="Disabled" x:Name="colorList">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Fill="{Binding .}" Margin="0,0,0,5" Width="20" Height="20" Stroke="#FF211E1E" OpacityMask="Black" StrokeThickness="1" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</UserControl>
and code-behind:
namespace ColorPicker
{
public partial class WPFColorPicker : UserControl
{
List<Brush> brushList;
public WPFColorPicker()
{
InitializeComponent();
brushList = new List<Brush>() {
new SolidColorBrush(Color.FromRgb(255, 0, 0)),
new SolidColorBrush(Color.FromRgb( 0,255, 0)),
new SolidColorBrush(Color.FromRgb( 0, 0,255))};
this.colorList.ItemsSource = brushList;
DataContext = this;
}
public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor",
typeof(Brush),
typeof(WPFColorPicker),
new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Red),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public Brush SelectedColor
{
get { return (Brush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
}
}
So, the problem I'm having is that binding on SelectedColor (using MyColor from TestViewModel) doesn't work.
From looking at other questions on StackOverflow and various tutorials, I think have the UserControl set up correct to bind SelectedColor as a TwoWay DependencyProperty, but its not working.
Can someone provide me some insight?
Upvotes: 1
Views: 1155
Reputation: 33364
Each FrameworkElement
can have only one DataContext
so when you do
DataContext = this;
in UserControl
constructor you overwrite DataContext
normally inherited through visual tree and that affects default binding context for WPFColorPicker
and all children, including
<view:WPFColorPicker SelectedColor="{Binding MyColor, Mode=TwoWay}" .../>
Remove that line from WPFColorPicker
constructor and instead give UserControl
some name
<UserControl x:Class="ColorPicker.WPFColorPicker" ... x:Name="myUserControl">
and change bindings inside UserControl
to use that name
<Rectangle ... Fill="{Binding ElementName=myUserControl, Path=SelectedColor}"/>
<ListBox ... SelectedValue="{Binding ElementName=myUserControl, Path=SelectedColor}">
EDIT
As a side note you need to be aware that Brush
is compared by reference so ListBox.SelectedValue
will not preselect value unless it's one of instances from the brushList
, which is not possible as you create the list every time. Basically two different instances of SolidColorBrush
, even with the same Color
, are different for equality check
Upvotes: 3
Reputation: 9713
Assuming that your Window's DataContext is set to your ViewModel.
Your colour picker is a UserControl
, therefore the following binding:
SelectedColor="{Binding MyColor, Mode=TwoWay}"
Is trying to find MyColor
in the DataContext of the UserControl, not the parent window.
You can either give the window a name and use ElementName binding:
SelectedColor="{Binding DataContext.MyColor, ElementName=windowName, Mode=TwoWay}"
Or even better, use a relative source binding:
SelectedColor="{Binding DataContext.MyColor, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay}"
Upvotes: 0