WPF Binding to Child Collection - wpf

if the parent's datasource has a child property that is a collection (let's say it is called ChildCollection) is there a trick to reference it?
So, this code sample is basically what I am attempting to do. But when I use this approach I do not get any data to my child controls.
<UserControl>
<UserControl.Resources>
<sample:Data x:Key="MyData" />
</UserControl.Resources>
<Canvas DataContext="{StaticResource MyData}">
<TextBlock Text="{Binding Title}" />
<My:UserControl DataContext="{Binding ChildCollection}" />
</Canvas>
</UserControl>
My dependency property looks like this:
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(IEnumerable), typeof(ButtonList),
new UIPropertyMetadata(new PropertyChangedCallback(DataChanged)));
public DoubleCollection Data
{
get { return (DoubleCollection)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
static void DataChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as FrameworkElement).DataContext = e.NewValue;
}
public void SetData(IEnumerable data)
{
(View as CollectionViewSource).Source = data;
}
Thank you in advance for your help.

If I understand your code right, you want the collection to be in the UserControl's DataProperty.
To achieve this, you have to do the Binding like this:
<Canvas DataContext="{StaticResource MyData}">
<TextBlock Text="{Binding Title}" />
<My:UserControl Data="{Binding ChildCollection}" />
</Canvas>
Instead of:
<Canvas DataContext="{StaticResource MyData}">
<TextBlock Text="{Binding Title}" />
<My:UserControl DataContext="{Binding ChildCollection}" />
</Canvas>
Hope this is helpful. Also: I dont know if a canvas inherits it's DataContext to childs. Use a Panel instead (Grid/Stackpanel/WrapPanel).
Jan

Below is a working sample with a user control (ButtonList) that has a DP of type IEnumerable<double> and creates a button for each double value. Compare it to you code to see what you are doing incorrectly.
XAML:
<UserControl x:Class="UserControlDemo.ButtonList"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UserControlDemo="clr-namespace:UserControlDemo"
Name="_buttonList">
<ItemsControl ItemsSource="{Binding Path=Data, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControlDemo:ButtonList}}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace UserControlDemo
{
public partial class ButtonList : UserControl
{
public ButtonList()
{
InitializeComponent();
}
public IEnumerable<double> Data
{
get { return (IEnumerable<double>)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data",
typeof(IEnumerable<double>),
typeof(ButtonList),
new UIPropertyMetadata(new List<double>()));
}
}
Usage:
<UserControlDemo:ButtonList Data="{Binding Path=Numbers}" />

Related

ComboBox in my WPF DataGrid won't display any items

I have a WPF user control that contains a DataGrid. This DG contains several columns including a ComboBox for states. The list of states is populated and stored as a property in my ViewModel.
I am trying to bind the StateList Property to the ItemsSource of my Combobox but when I run the form and try to edit the DG, the combobox does not contain any values, the combobox is empty.
Here is the XAML for the usercontrol.
<UserControl x:Class="myproject.View.ucContactView"
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" d:DesignHeight="475" d:DesignWidth="977">
<UserControl.Resources>
<ResourceDictionary Source="/Templates/MyResourceDictionary.xaml"/>
</UserControl.Resources>
<Grid DataContext="{Binding ViewModel}">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding AddressCollectionViewSource.View}">
<DataGridTemplateColumn Header="State" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StateDescription}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Name="cboState"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid>
</Grid>
</UserControl>
The odd thing is that if I create another combobox on this usercontrol with the exact same combobox, this combobox works as expected.
<!-- this works as long as it's not in the DG -->
<StackPanel Height="126" HorizontalAlignment="Left" Margin="766,275,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="200" >
<ComboBox Name="cboState2"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
Why won't the combobox in the DG display the values from the StateList property? Any why does the separate combobox work properly?
It's not working because your ComboBox is looking for StateList as a property of the DataContext of the DataGrid. That is, it's trying to bind to ViewModel.AddressCollectionViewSource.View.StateList when it needs to be binding to ViewModel.StateList. Check your output window while debugging and I bet you'll see a binding error to the effect of Could not find property StateList on object AddressCollectionViewSource (or maybe ICollection).
Try this instead:
<ComboBox Name="cboState2"
SelectedValuePath="StateKey"
ItemTemplate="{StaticResource dtStateTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type DataGrid}}, Path=DataContext.StateList}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
if your viewmodel is a property at the window you can do this
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ViewModel.StateList, Mode=OneWay}"
<Window x:Class="WpfStackOverflowSpielWiese.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2"
Height="300"
Width="300"
x:Name="window">
<Grid DataContext="{Binding ElementName=window, Path=ViewModel}">
<DataGrid x:Name="grid"
AutoGenerateColumns="False"
ItemsSource="{Binding AddressCollectionViewSource, Mode=OneWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="State"
Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding StateKey}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox Name="cboState"
SelectedValuePath="StateKey"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=ViewModel.StateList, Mode=OneWay}"
SelectedItem="{Binding StateKey, Mode=TwoWay}"
Width="100" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfStackOverflowSpielWiese
{
/// <summary>
/// Interaction logic for Window2.xaml
/// </summary>
public partial class Window2 : Window
{
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(ViewModelClass), typeof(Window2), new PropertyMetadata(default(ViewModelClass)));
public ViewModelClass ViewModel {
get { return (ViewModelClass)this.GetValue(ViewModelProperty); }
set { this.SetValue(ViewModelProperty, value); }
}
public Window2() {
this.InitializeComponent();
this.grid.Items.Clear();
this.ViewModel = new ViewModelClass();
}
}
public class StateClass : DependencyObject
{
public static readonly DependencyProperty StateKeyProperty =
DependencyProperty.Register("StateKey", typeof(string), typeof(ViewModelClass), new PropertyMetadata(default(string)));
public string StateKey {
get { return (string)this.GetValue(StateKeyProperty); }
set { this.SetValue(StateKeyProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(string), typeof(StateClass), new PropertyMetadata(default(string)));
public string State {
get { return (string)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
}
public class ViewModelClass : DependencyObject
{
public static readonly DependencyProperty StateListProperty =
DependencyProperty.Register("StateList", typeof(ObservableCollection<string>), typeof(ViewModelClass), new PropertyMetadata(default(ObservableCollection<string>)));
public static readonly DependencyProperty AddressCollectionViewSourceProperty =
DependencyProperty.Register("AddressCollectionViewSource", typeof(ObservableCollection<StateClass>), typeof(ViewModelClass), new PropertyMetadata(default(ObservableCollection<StateClass>)));
public ObservableCollection<StateClass> AddressCollectionViewSource {
get { return (ObservableCollection<StateClass>)this.GetValue(AddressCollectionViewSourceProperty); }
set { this.SetValue(AddressCollectionViewSourceProperty, value); }
}
public ObservableCollection<string> StateList {
get { return (ObservableCollection<string>)this.GetValue(StateListProperty); }
set { this.SetValue(StateListProperty, value); }
}
public ViewModelClass() {
this.StateList = new ObservableCollection<string>(new[] {"one", "two"});
this.AddressCollectionViewSource = new ObservableCollection<StateClass>(new[] {new StateClass {State = "state", StateKey = "one"}});
}
}
}

Repeated children in WPF TreeView

Solved it myself. It was the way I initialised the Settings collection. Specifying a default when registering it as a DependencyProperty causes all of the Settings to refer to the same collection object. Adding a constructor to Category and explicitly initialising Settings resolves the issue.
A class Category specifies a name and a collection of Settings objects.
using System.Collections.ObjectModel;
using System.Windows;
namespace CasEdit
{
public class Categories : ObservableCollection<Category> { }
public class Category : DependencyObject
{
public string Caption
{
get { return (string)GetValue(CategoryProperty); }
set { SetValue(CategoryProperty, value); }
}
public static readonly DependencyProperty CategoryProperty =
DependencyProperty.Register("Caption", typeof(string), typeof(Category),
new UIPropertyMetadata("Category name not set"));
public Settings Settings
{
get { return (Settings)GetValue(SettingsProperty); }
}
public static readonly DependencyProperty SettingsProperty =
DependencyProperty.Register("Settings", typeof(Settings), typeof(Category),
new UIPropertyMetadata(new Settings()));
}
}
The following XAML defines templates, UI and some test data.
<Window x:Class="CasEdit.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CasEdit="clr-namespace:CasEdit"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type CasEdit:Category}" ItemsSource="{Binding Settings}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Caption}" />
<Button Content="Gratuitous button" Margin="3" Click="Button_Click"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type CasEdit:Setting}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Caption}" Margin="3" />
<TextBlock Text="{Binding Template}" Margin="3" />
<TextBox Text="{Binding Editor}" Margin="3" />
<TextBlock Text="{Binding CasDevice}" Margin="3" />
</StackPanel>
</DataTemplate>
<CasEdit:Categories x:Key="cats">
<CasEdit:Category Caption="1st category">
<CasEdit:Category.Settings>
<CasEdit:Setting Caption="Setting 1-1" />
<CasEdit:Setting Caption="Setting 1-2" />
</CasEdit:Category.Settings>
</CasEdit:Category>
<CasEdit:Category Caption="2nd category" >
<CasEdit:Category.Settings>
<CasEdit:Setting Caption="Setting 2-1" />
</CasEdit:Category.Settings>
</CasEdit:Category>
<CasEdit:Category Caption="3rd category" >
<CasEdit:Category.Settings>
<CasEdit:Setting Caption="Setting 3-1" />
</CasEdit:Category.Settings>
</CasEdit:Category>
</CasEdit:Categories>
</Window.Resources>
<Grid>
<TreeView x:Name="tree" ItemsSource="{Binding Source={StaticResource cats}}" />
</Grid>
</Window>
You would expect a tree like this
1st category
Setting 1-1
Setting 1-2
2nd Category
Setting 2-1
3rd category
Setting 3-1
but what I get is this
which is very confusing. Where have I gone astray, that each category shows all of the settings?
The last parameter here is telling making it so that every instance of Category has it's Settings property initialized to point to the same object:
public static readonly DependencyProperty SettingsProperty =
DependencyProperty.Register("Settings", typeof(Settings), typeof(Category),
new UIPropertyMetadata(new Settings()));
Instead, do this:
public static readonly DependencyProperty SettingsProperty =
DependencyProperty.Register("Settings", typeof(Settings), typeof(Category),
new UIPropertyMetadata(null));
public Category()
{
Settings = new Settings();
}

Two views of the same ViewModel within a user control

I have a re-usable usercontrol with a viewmodel behind it. I'm trying to switch between different views of the same data. Currently trying to use a Mode property on the VM to accomplish this.
I've created a DataTemplateSelector like so:
<UserControl x:Class="MyUserControl">
<UserControl.Resources>
<DataTemplate x:Key="ColumnTemplate">
<StackPanel>
<Label Text="{Binding Name}"></Label>
<Label Text="{Binding Address}"></Label>
<Label Text="{Binding Occupation}"></Label>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="AvatarTemplate">
<StackPanel>
<Image Source="{Binding ProfilePicture}"></Image>
<Label Text="{Binding Name}"></Label>
</StackPanel>
</DataTemplate>
<local:DisplayTemplateSelector ColumnTemplate="{StaticResource ColumnTemplate}" AvatarTemplate="{StaticResource AvatarTemplate}" x:Key="displayTemplateSelector" />
</UserControl.Resources>
<Grid>
<ContentControl Name="cpDisplay" Content="{Binding}" ContentTemplateSelector="{StaticResource displayTemplateSelector}" />
</Grid>
</UserControl>
With the class:
class DisplayTemplateSelector : DataTemplateSelector
{
public DataTemplate ColumnTemplate {get;set;}
public DataTemplate AvatarTemplate {get;set;}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
MainViewModel vm = (MainViewModel)item;
switch (vm.Mode)
{
case MainViewModel.DisplayMode.Column:
return ColumnTemplate;
case MainViewModel.DisplayMode.Avatar:
return AvatarTemplate;
default:
return AvatarTemplate;
}
}
}
This usercontrol sits in MyWindow:
<Grid>
<controls:MyUserControl x:Name="MyUserControl" DataContext="{Binding}" Margin="0"/>
</Grid>
Which is instantiated with my viewmodel:
MyWindow w = new MyWindow(_vm);
w.Show();
The problem I have is that item is null during MainViewModel vm = (MainViewModel)item. It's like I'm trying to set the datatemplate based on data, before the data is bound?
Is there anyway to choose the desired datatemplate not based on the dataobject - but as a property or similar on the usercontrol?
There are many ways, but here are a couple:
<!-- assumes you have a data template selector implementation available as resource MyContentSelector -->
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplateSelector="{StaticResource MyContentSelector}"/>
or:
<!-- assumes you have appropriate boolean properties on your VM -->
<Grid>
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource column}" Visibility="{Binding IsColumnVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource avatar}" Visibility="{Binding IsAvatarVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
See DataTemplateSelector Class
DataTemplateSelector
Ok, finally got this to work how I needed by using a property on the usercontrol and some code behind:
public enum DisplayMode
{
Column,
Avatar
}
public DisplayMode Mode { get; set; }
public MyUserControl()
{
InitializeComponent();
Mode = DisplayMode.Avatar;
}
private void MyUserControl_Loaded(object sender, RoutedEventArgs e)
{
switch (Mode)
{
case DisplayMode.Column:
cpDisplay.ContentTemplate = (DataTemplate)this.Resources["ColumnTemplate"];
cpDisplay.ApplyTemplate();
break;
case DisplayMode.Avatar:
cpDisplay.ContentTemplate = (DataTemplate)this.Resources["AvatarTemplate"];
cpDisplay.ApplyTemplate();
break;
}
}
I removed the DataTemplateSelector code and simply defined the datatemplates and used:
<ContentPresenter Name="cpDisplay" Content="{Binding}" />

In WPF how to change a DataTemplate's Textblock's text binding in code?

I have a ListBox whose ItemsSource is bound to a list of objects. The Listbox has a ItemTemplate with a DataTemplate containing a TextBlock. The textblock's Text is bound to the object's Name property (i.e. Text="{Binding Name}").
I would like to provide a radio button to show different views of the same list. For example allow a user to toggle between the Name property and an ID property.
I found a SO answer for this at 2381740 but I also have border and a textbox style set in data template (see code below).
Is there anyway to just reset the Textblock binding? I don't want to have to recreate the entire datatemplate. Actually I'm not even sure how to do that, is there an easy way to translating xaml to code?.
Thanks
Cody
<DataTemplate>
<Border Margin="0 0 2 2"
BorderBrush="Black"
BorderThickness="3"
CornerRadius="4"
Padding="3">
<TextBlock Style="{StaticResource listBoxItemStyle}"
Text="{Binding Name}" />
</Border>
</DataTemplate>
Wallstreet Programmer's solution works well for you because you are using radio buttons. However there is a more general solution that I thought I should mention for future readers of this question.
You can change your DataTemplate to use plain "{Binding}"
<DataTemplate x:Key="ItemDisplayTemplate">
<Border ...>
<TextBlock ...
Text="{Binding}" />
</Border>
</DataTemplate>
Then in code you don't have to recreate a full DataTemplate. All you have to do is recreate this:
<DataTemplate>
<ContentPresenter Content="{Binding Name}" ContentTemplate="{StaticResource ItemDisplayTemplate}" />
</DataTemplate>
which is easy:
private DataTemplate GeneratePropertyBoundTemplate(string property, string templateKey)
{
var template = FindResource(templateKey);
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(ContentPresenter));
factory.SetValue(ContentPresenter.ContentTemplateProperty, template);
factory.SetBinding(ContentPresenter.ContentProperty, new Binding(property));
return new DataTemplate { VisualTree = factory };
}
This is particularly convenient if you have many properties, even in your radio button example.
Just make it simple for yourself and use two textblocks and hide one of them.
XAML:
<Window x:Class="Test.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<StackPanel>
<RadioButton Name="nameRadioBtn" Content="Name" IsChecked="True"/>
<RadioButton Name="lengthRadioBtn" Content="Length" />
<ListBox
ItemsSource="{Binding Path=Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<Grid>
<TextBlock
Text="{Binding .}"
Visibility="{Binding Path=IsChecked, ElementName=nameRadioBtn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
Text="{Binding Path=Length}"
Visibility="{Binding Path=IsChecked, ElementName=lengthRadioBtn,
Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
Code behind:
using System.Collections.Generic;
using System.Windows;
namespace Test
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<string> Items
{
get
{
return new List<string>() {"Bob", "Sally", "Anna"};
}
}
}
}
You can also use a value converter to pick any property of your data object. You will need to bind to the whole object instead of individual properties. If your data object implements INotifyPropertyChanged then this solution will not work for you.
XAML
<Window x:Class="Test.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Test="clr-namespace:Test"
Height="300" Width="300">
<Window.Resources>
<Test:PropertyPickerConverter x:Key="PropertyPickerConverter" />
</Window.Resources>
<StackPanel>
<RadioButton Content="Name" Click="OnRadioButtonClick" IsChecked="True"/>
<RadioButton Content="Length" Click="OnRadioButtonClick" />
<ListBox
ItemsSource="{Binding Path=Items}"
Name="_listBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="1">
<StackPanel>
<TextBlock
Text="{Binding ., Converter={StaticResource PropertyPickerConverter}}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
code behind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Test
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
_propertyPickerConverter = FindResource("PropertyPickerConverter") as PropertyPickerConverter;
_propertyPickerConverter.PropertyName = "Name";
DataContext = this;
}
public IEnumerable<string> Items
{
get
{
return new List<string>() {"Bob", "Sally", "Anna"};
}
}
private void OnRadioButtonClick(object sender, RoutedEventArgs e)
{
_propertyPickerConverter.PropertyName = (sender as RadioButton).Content as string;
_listBox.Items.Refresh();
}
private PropertyPickerConverter _propertyPickerConverter;
}
public class PropertyPickerConverter : IValueConverter
{
public string PropertyName { get; set; }
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string item = value as string;
switch (PropertyName)
{
case "Name": return item;
case "Length": return item.Length;
default: return null;
}
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
}

Getting events from the WPF Checked ComboBox

Near total WPF noob. So I hooked up a combobox to have checkboxes using the following item template:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="{Binding IsSelected}"
Width="20" Name="chkDayName" Click="chkDayName_Click"/>
<TextBlock Text="{Binding DayOfWeek}"
Width="100" Name="txtDayName" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
On the actual event of a person clicking a checkbox, i catch the event in chkDayName_Click method. I have the following questions:
How do I find out values of the corresponding TextBlock in the item template?
How do i find out the index of the item that was clicked?
Is there a way to get to the parent?
Thanks.
If I understand it you want to know which combobox items are checked? You can use the chkDayName_Click for that and add the name of the day as Tag of the CheckBox. This feels very Winforms. In WPF you normally let your databinding handle functionality like this. Below is some code that will display selected item in a textbox and a list of checked weekdays.
XAML:
<Window x:Class="DayComboBoxDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<CollectionViewSource x:Key="checkedWeekdays" Source="{Binding Path=WeekDays}" Filter="IsCheckedFilter" />
</Window.Resources>
<StackPanel>
<ComboBox
ItemsSource="{Binding Path=WeekDays}"
SelectedItem="{Binding Path=SelectedWeekDay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox
IsChecked="{Binding Path=IsChecked}"
Width="20" Click="chkDayName_Click"/>
<TextBlock
Text="{Binding DayOfWeek}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding Path=SelectedWeekDay.DayOfWeek}" />
<ListBox
DisplayMemberPath="DayOfWeek"
ItemsSource="{Binding Source={StaticResource checkedWeekdays}}" />
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace DayComboBoxDemo
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
List<WeekDay> weekDays = new List<WeekDay>();
foreach (DayOfWeek dayOfWeek in System.Enum.GetValues(typeof(DayOfWeek)))
{
weekDays.Add(new WeekDay() { DayOfWeek = dayOfWeek });
}
WeekDays = weekDays;
_checkedWeekdays = FindResource("checkedWeekdays") as CollectionViewSource;
DataContext = this;
}
public IEnumerable<WeekDay> WeekDays { get; set; }
public WeekDay SelectedWeekDay
{
get { return (WeekDay)GetValue(SelectedWeekDayProperty); }
set { SetValue(SelectedWeekDayProperty, value); }
}
public static readonly DependencyProperty SelectedWeekDayProperty =
DependencyProperty.Register("SelectedWeekDay",
typeof(WeekDay),
typeof(Window1),
new UIPropertyMetadata(null));
private void chkDayName_Click(object sender, RoutedEventArgs e)
{
_checkedWeekdays.View.Refresh();
}
private void IsCheckedFilter(object sender, FilterEventArgs e)
{
WeekDay weekDay = e.Item as WeekDay;
e.Accepted = weekDay.IsChecked;
}
private CollectionViewSource _checkedWeekdays;
}
public class WeekDay
{
public DayOfWeek DayOfWeek { get; set; }
public bool IsChecked { get; set; }
}
}
You can try ComboBox's SelectedIndex or SelectedValue to tell the SelectedItem. In the MVVM fashion, you can have a two-way binding between SelectedIndex and one of you ViewModel properties.

Resources