CheckBox ListView SelectedValues DependencyProperty Binding - wpf

I am writing a custom control that is a ListView that has a CheckBox on each item in the ListView to indicate that item is Selected. I was able to do so with the following XAML.
<ListView x:Class="CheckedListViewSample.CheckBoxListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Setter Property="SelectionMode" Value="Multiple" />
<Style.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListViewItem}}}">
<CheckBox.Content>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</CheckBox.Content>
</CheckBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
</Style>
</ListView.Style>
</ListView>
I however am trying to attempt one more feature. The ListView has a SelectedItems DependencyProperty that returns a collection of the Items that are checked. However, I need to implement a SelectedValues DependencyProperty. I also am implementing a SelectedValuesPath DependencyProperty. By using the SelectedValuesPath, I indicate the path where the values are found for each selected item. So if my items have an ID property, I can specify using the SelectedValuesPath property "ID". The SelectedValues property would then return a collection of ID values. I have this working also using this code in the code-behind:
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
namespace CheckedListViewSample
{
/// <summary>
/// Interaction logic for CheckBoxListView.xaml
/// </summary>
public partial class CheckBoxListView : ListView
{
public static DependencyProperty SelectedValuesPathProperty =
DependencyProperty.Register("SelectedValuesPath",
typeof(string),
typeof(CheckBoxListView),
new PropertyMetadata(string.Empty, null));
public static DependencyProperty SelectedValuesProperty =
DependencyProperty.Register("SelectedValues",
typeof(IList),
typeof(CheckBoxListView),
new PropertyMetadata(new List<object>(), null));
[Category("Appearance")]
[Localizability(LocalizationCategory.NeverLocalize)]
[Bindable(true)]
public string SelectedValuesPath
{
get
{
return ((string)(base.GetValue(CheckBoxListView.SelectedValuesPathProperty)));
}
set
{
base.SetValue(CheckBoxListView.SelectedValuesPathProperty, value);
}
}
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Category("Appearance")]
public IList SelectedValues
{
get
{
return ((IList)(base.GetValue(CheckBoxListView.SelectedValuesPathProperty)));
}
set
{
base.SetValue(CheckBoxListView.SelectedValuesPathProperty, value);
}
}
public CheckBoxListView()
: base()
{
InitializeComponent();
base.SelectionChanged += new SelectionChangedEventHandler(CheckBoxListView_SelectionChanged);
}
private void CheckBoxListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
List<object> values = new List<object>();
foreach (var item in SelectedItems)
{
if (string.IsNullOrWhiteSpace(SelectedValuesPath))
{
values.Add(item);
}
else
{
try
{
values.Add(item.GetType().GetProperty(SelectedValuesPath).GetValue(item, null));
}
catch { }
}
}
base.SetValue(CheckBoxListView.SelectedValuesProperty, values);
e.Handled = true;
}
}
}
My problem is that my binding only works one way right now. I'm having trouble trying to figure out how to implement my SelectedValues DependencyProperty so that I could Bind a Collection of values to it, and when the control is loaded, the CheckBoxes are checked with items that have values that correspond to the SelectedValues.
I've considered using the PropertyChangedCallBack event, but can't quite figure out how I could write that to achieve my goal.
I'm also unsure of how I find the correct ListViewItem to set it as Selected.
And lastly, if I can find the ListViewItem and set it to be Selected, won't that fire my SelectionChanged event each time I set a ListViewItem to be Selected?

Go through the below links.
Sync SelectedItems in a muliselect listbox with a collection in ViewModel
http://alexshed.spaces.live.com/blog/cns!71C72270309CE838!149.entry
http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

Related

Send Value from View Model to UserControl Dependency Property WPF

I have a dependency property in a UserControl with a property called SelectedColor. From my main app, the view of the window that uses this my code is:
<controls:ColorPicker SelectedColor="{Binding MyCanvas.CanvasBackgroundColor}" />
And the code from the view model is:
public MyCanvas { get; set; }
public MyWindowViewModel(MyCanvas myCanvas)
{
MyCanvas = myCanvas;
}
And then the XAML for my UserControl is:
<UserControl . . .>
<Button Click="Button_Click">
<Button.Style>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding SelectedColor}" BorderBrush="Black" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</UserControl>
And the code-behind:
public ColorPicker()
{
InitializeComponent();
DataContext = this;
}
public SolidColorBrush SelectedColor
{
get { return (SolidColorBrush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(nameof(SelectedColor), typeof(SolidColorBrush), new UIPropertyMetadata(null));
I think the problem might be with the line in the code-behind DataContext = this;. Is it correct that declaring this creates an entirely new context for the instance of this user control in the main app and therefore any values sent to it from the view model would be re-initialized? If so, how can I send the value over without it being re-declared? I also need the DataContext = this line because without it some functionality within my UserControl will no longer work.
Has anyone encountered this before?
Thanks in advance!
DataContext = this sets the DataContext of the UserControl to itself. You don't want to do this. Instead you could bind to a property of the UserControl using a {RelativeSource} without setting the DataContext property:
<Border Background="{Binding SelectedColor, RelativeSource={RelativeSource AncestorType=UserControl}}"
BorderBrush="Black" BorderThickness="1" />
Code-behind:
public ColorPicker()
{
InitializeComponent();
}
public SolidColorBrush SelectedColor
{
get { return (SolidColorBrush)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(nameof(SelectedColor), typeof(SolidColorBrush), new UIPropertyMetadata(null));

WPF converter implied in custom control?

In my WPF Custom Control which draws a pie chart, I successfully made it draw a pie chart given a set of values in a string, for example "10 20 30" would draw a pie chart with correct proportions. I bound the DrawingImage's drawing property to a converter to convert from the string into a DrawingGroup. This worked great, but I am trying to bypass the need for a converter.
Here is my MainWindow:
<Grid Margin="10">
<local:PieChart DrawingCode="289 666 1337 780" Width="400" Height="400" RingWidth="300" Background="White" />
</Grid>
Here is my template for the custom control:
<Style TargetType="{x:Type local:PieChart}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PieChart}">
<Grid>
<Image Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<Image.Source>
<DrawingImage Drawing="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DrawingCode}" />
</Image.Source>
</Image>
<Ellipse Width="{TemplateBinding RingWidth}" Height="{TemplateBinding RingWidth}" Fill="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And finally, here is my attempt at the Custom Control class:
public class PieChart : Control
{
public static readonly DependencyProperty DrawingCodeProperty = DependencyProperty.Register("DrawingCode", typeof(string), typeof(PieChart), new UIPropertyMetadata(null));
public static readonly DependencyProperty RingWidthProperty = DependencyProperty.Register("RingWidth", typeof(double), typeof(PieChart), new UIPropertyMetadata(null));
static PieChart()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PieChart), new FrameworkPropertyMetadata(typeof(PieChart)));
}
public string DrawingCode
{
get { return DrawingCodeConverter((string)GetValue(DrawingCodeProperty)); }
set { SetValue(DrawingCodeProperty, value); }
}
public double RingWidth
{
get { return (double)GetValue(RingWidthProperty); }
set { SetValue(RingWidthProperty, this.Width - value); }
}
public DrawingGroup DrawingCodeConverter(string value)
{
// This converter works but is long so I removed it from the post.
}
}
I am pretty sure the problem is somewhere in the data types I should be using. Also, if there is a completely different way to do this that I am ignorant of, please let me know. Also note that RingWidth is not the problem, it is DrawingCode.
The getter and setter of the CLR wrapper of a dependency property may be bypassed when the property is accessed in XAML or by a Binding, Style Setter, Animation, etc. WPF then calls GetValue and SetValue directly. The reason is explained in XAML Loading and Dependency Properties
You must therefore not call anything else than GetValue and SetValue in the getter and setter. Instead, declare your dependency property with a PropertyChangedCallback like this:
public static readonly DependencyProperty DrawingCodeProperty =
DependencyProperty.Register(
"DrawingCode",
typeof(string),
typeof(PieChart),
new FrameworkPropertyMetadata(DrawingCodePropertyChanged));
public string DrawingCode
{
get { return (string)GetValue(DrawingCodeProperty); }
set { SetValue(DrawingCodeProperty, value); }
}
private static void DrawingCodePropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var pieChart = (PieChart)o;
pieChart.SetDrawingCode((string)e.NewValue);
}
private void SetDrawingCode(string drawingCode)
{
var drawingGroup = DrawingCodeConverter(drawingCode);
// do something with drawingGroup
}

WPF TemplateBinding to an ObservableCollection

I'm creating a content control that contains another usercontrol. We'll call them InnerControl and OuterControl. The InnerControl has a dependency property of type ObservableCollection called "Items." I'm trying to bind that to an identical Dependency Property in the OuterControl. Here is a stub of the InnerControl code:
public class InnerControl : UserControl {
public InnerControl() {
InnerItems = new ObservableCollection<string>();
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata());
}
The outer control contains an identical Items property:
public class OuterControl : ContentControl {
public OuterControl() {
OuterItems = new ObservableCollection<string>();
}
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl),
new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata());
}
Then I'm defining the OuterControl's appearance in the generic.xaml file:
<Style TargetType="{x:Type userControls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type userControls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
<ContentPresenter Grid.Row="1" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The really important part of the above code I want to call your attention to is:
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
What I expect to happen is that when items are added to the OuterItems collection of the OuterControl, those same items will be added to the InnerControl.InnerItems collection. However, that doesn't happen, and I can't figure out why.
I've also tried a relative binding so that I could experiment with using TwoWay mode and so on. Something like this:
InnerItems="{Binding OuterItems, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
But so far that hasn't worked either.
UPDATE
Everything that I thought solved this problem so far has only exposed new problems, so I've removed my previous updates. What I'm stuck with at this point is:
If I initialize InnerItems in the constructor, then the TemplateBinding doesn't seem to work (the items never get updated)
If I don't initialize InnerItems at all, the TemplateBinding works. However, if InnerControl is just used by itself in the Designer, it breaks, because InnerItems is null when the designer tries to add items to it.
When you have a collection type dependency property, you must not use an instance of the collection class as default value of the property. Doing so will make all instances of the control that owns the property use the same collection instance.
So your property metadata
new PropertyMetadata(new ObservableCollection<string>())
should be replaced by
new PropertyMetadata(null)
or you do not specify any metadata at all
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register(
"InnerItems", typeof(ObservableCollection<string>), typeof(InnerControl));
Now you would somehow have to initialize the property value. As usual, you'll do it in the control's constructor, like
public InnerControl()
{
InnerItems = new ObservableCollection<string>();
}
When you now bind the property of the control like
<local:InnerControl InnerItems="{Binding ...}" />
the value set in the constructor is replaced by the value produced by the Binding.
However, this does not happen when you create the Binding in a Style Setter, because values from Style Setters have lower precedence than so-called local values (see Dependency Property Value Precedence).
A workaround is to set the default value by the DependencyObject.SetCurrentValue() method, which does not set a local value:
public InnerControl()
{
SetCurrentValue(InnerItemsProperty, new ObservableCollection<string>());
}
I find it quite likely that #Clemens comment has the right answer. Anyhow, I tested your solution using the code below and it worked fine for me.
Check how you are binding and adding items. You did not post that code in your question.
OuterControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class OuterControl : UserControl
{
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl), new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
InnerControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class InnerControl : UserControl
{
static InnerControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InnerControl), new FrameworkPropertyMetadata(typeof(InnerControl)));
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TemplateBindingTest.Controls">
<Style TargetType="{x:Type controls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding OuterItems}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Red">
<controls:InnerControl Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}">Inner Control</controls:InnerControl>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:InnerControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:InnerControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding InnerItems}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="TemplateBindingTest.MainWindow"
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"
xmlns:controls="clr-namespace:TemplateBindingTest.Controls"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:OuterControl OuterItems="{Binding OuterItems}">Outer Control</controls:OuterControl>
<Button Grid.Row="1" Content="Add" Click="Button_Click" HorizontalAlignment="Left" />
</Grid>
</Window>
MainWindow.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
namespace TemplateBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> _OuterItems;
public MainWindow()
{
InitializeComponent();
DataContext = this;
_OuterItems = new ObservableCollection<string>(new List<string>()
{
"Test 1",
"Test 2",
"Test 3",
});
}
public ObservableCollection<string> OuterItems
{
get
{
return _OuterItems;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_OuterItems.Add(System.IO.Path.GetRandomFileName());
}
}
}

Cannot set Expression. It is marked as 'NonShareable' and has already been used

iv'e created a custom control deriving from ItemsControl .
public class CustsomItemsControl : ItemsControl
{ }
XAML :
<local:CustsomSelectorControl ItemsSource="{Binding People}">
<local:CustsomSelectorControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
</local:CustsomSelectorControl.ItemTemplate>
</local:CustsomSelectorControl>
The Control Template :
<Style TargetType="{x:Type local:CustsomItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustsomItemsControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
in DataContext :
public MainWindow()
{
InitializeComponent();
People = new ObservableCollection<Person>();
People.Add(new Person("A"));
People.Add(new Person("B"));
People.Add(new Person("C"));
}
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("People"));
}
}
The items are never set , iv'e observed this with snoop ,the ItemsSource property
is marked in RED but there are no BindingErrors , when i delve the BindingExpression
i get an ArgumentExpression:
Cannot set Expression. It is marked as 'NonShareable' and has already been used.
Set the DataContext properly:
public MainWindow()
{
InitializeComponent();
DataContext = this; //This is what you're missing
...
}
Still, you need to have a really strong reason to subclass ItemsControl.

WPF - Selector - Custom Control - SelectionChanged Event Not Firing

I am new to custom control creation. I have laid some groundwork for a new custom control based on the Selector class. My understanding was that I should use this class since I needed the control to have an Items collection and the ability to handle selections. I believe that changing the ItemTemplate may have overriden some of this ability because I do not receive the SelectionChanged event at the control level or application level. I would think if I'm right that there is some sort of SelectionRegion XAML tag that I can put the DataTemplate innards into. I have not had luck in finding anything like this. After looking through Google for a while, I am ready to just ask. What am I missing? Below is the ItemTemplate markup. Thanks for any help. Thanks even more if you can tell me why the Text in TextBlock is enclosed in parentheses even though the data isn't.
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding}" Foreground="Black" Background="White" MinHeight="12" MinWidth="50"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
At the request of a commenter, here is the complete XAML for the control so far:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SourceMedicalWPFCustomControlLibrary">
<Style TargetType="{x:Type local:MultiStateSelectionGrid}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding Code}" Foreground="Black" Background="White" MinHeight="12" MinWidth="50" Padding="2" ToolTip="{Binding Description}"/>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MultiStateSelectionGrid}">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0" Content="{TemplateBinding Content}"/>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And the anemic code-behind as well:
namespace SourceMedicalWPFCustomControlLibrary
{
public class MultiStateSelectionGridState
{
public Brush Background { get; set; }
public Brush Foreground { get; set; }
public Brush Border { get; set; }
public string Text { get; set; }
public MultiStateSelectionGridState()
{
Background = Brushes.White;
Foreground = Brushes.Black;
Border = Brushes.Black;
Text = String.Empty;
}
};
public class MultiStateSelectionGrid : Selector
{
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(MultiStateSelectionGrid),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public object Content
{
get { return (object)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty StatesProperty =
DependencyProperty.Register("States", typeof(List<MultiStateSelectionGridState>), typeof(MultiStateSelectionGrid),
new FrameworkPropertyMetadata(new List<MultiStateSelectionGridState>(),
FrameworkPropertyMetadataOptions.AffectsRender));
public List<MultiStateSelectionGridState> States
{
get { return (List<MultiStateSelectionGridState>)GetValue(StatesProperty); }
set { SetValue(StatesProperty, value); }
}
static MultiStateSelectionGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiStateSelectionGrid), new FrameworkPropertyMetadata(typeof(MultiStateSelectionGrid)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.SelectionChanged += new SelectionChangedEventHandler(MultiStateSelectionGrid_SelectionChanged);
}
void MultiStateSelectionGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MessageBox.Show("Hi");
}
}
}
here is what I do. I use the apply template function of the custom control and add a handlerto the selection chnaged event of the control I want.
simple sample here:
public event EventHandler<SelectionChangedEventArgs> YourControlSelectionChanged;
private void Selector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ListSelectionChanged != null) {
ListSelectionChanged(sender, e);
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//find or declare your control here, the x:name in xaml should be YourControl
YourControl== this.Template.FindName("YourControl", this) as YourControlType
YourControl.SelectionChanged += ResultListBox_SelectionChanged;
}
you can then bind to the name of the public event (YourControlSelectionChanged) you declared in your custom control class in xaml.
hope this helps.
From reading some full code examples of different controls, I believe my answer is that I am doing this all wrong. Instead, I need to have control that has a Selector like a ListBox in the ControlTemplate. THEN, #JKing 's advice would help me get to where I need to be. The answer to the actual question asked though is the aforementioned change from using Selector as a base class to having a selector in the template for the control. Thanks for the help.

Resources