Binding to the selected item in an ItemsControl
I created a custom ComboBox
as follows: (note, code is not correct but you should get the general idea.) The ComboBox
contains 2 dependency properties which matter: TitleText
and DescriptionText
.
<Grid>
<TextBlock x:Name="Title"/>
<Grid x:Name="CBG">
<ToggleButton/>
<ContentPresenter/>
<Popup/>
</Grid>
</Grid>
I want to use this ComboBox
to display a wide range of options. I created a class called Setting which inherits from DependencyObject
to create usable items, I created a DataTemplate
to bind the contents of this Settings
object to my ComboBox
and created a UserControl
which contains an ItemsControl
which has as a template my previously mentioned DataTemplate
. I can fill it with Setting
objects.
<DataTemplate x:Key="myDataTemplate">
<ComboBox TitleText="{Binding Title}" DescriptionText="{Binding DescriptionText}"/>
</DataTemplate>
<UserControl>
<Grid>
<StackPanel Grid.Column="0">
<ItemsControl Template="{StaticResource myDataTemplate}">
<Item>
<Setting Title="Foo" Description="Bar">
<Option>Yes</Option><Option>No</Option>
</Setting>
</Item>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock x:Name="Description"/>
</StackPanel>
</Grid>
</UserControl>
I would like to have the DescriptionText
of the selected ComboBox
(selected by either the IsFocus
of the ComboBox
control or the IsOpen
property of the popup) to be placed in the Description
TextBlock
in my UserControl
.
One way I managed to achieve this was replacing my ItemsControl
by a ListBox
but this caused several issues: it always showed a scrollbar even though I disabled it, it wouldn't catch focus when my popup was open but only when I explicitly selected the item in my ListBox
, when I enabled the OverridesDefaultStyle
property the contents of the ListBox
wouldn't show up at all, I had to re-theme the ListBox
control to match my UserControl
layout...
What's the best and easiest way to get my DescriptionText
to show up without using a ListBox
or creating a custom Selector
control (as that had the s开发者_如何学编程ame effect as a ListBox
)?
The goal at the end is to loop through all the items (maybe get them into an ObservableCollection
or some sort and to save them into my settings file.
I think I know what you are trying to do. Here is a possible solution.
You should use a ListBox (or anything deriving from Selector control) in order to use the SelectedItem property.
<UserControl>
<Grid>
<StackPanel Grid.Column="0">
<ListBox x:Name="SettingListBox" Template="{StaticResource myDataTemplate}">
<Item>
<Setting Title="Foo" Description="Bar">
<Option>Yes</Option><Option>No</Option>
</Setting>
</Item>
</ListBox >
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock x:Name="Description"
Text="{Binding SelectedItem.Description, ElementName=SettingListBox}"/>
</StackPanel>
</Grid>
</UserControl>
To solve the problem where your ListBox does not focus on the item when you open the ComboBox drop-down menu, I have an attached property that will fix that for you.
public class ListBoxHelper
{
#region Dependency Property
public static bool GetCanFocusParent(DependencyObject obj)
{
return (bool)obj.GetValue(CanFocusParentProperty);
}
public static void SetCanFocusParent(DependencyObject obj, bool value)
{
obj.SetValue(CanFocusParentProperty, value);
}
// Using a DependencyProperty as the backing store for CanFocusParent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CanFocusParentProperty =
DependencyProperty.RegisterAttached("CanFocusParent", typeof(bool), typeof(ListBoxHelper), new UIPropertyMetadata(false, OnCanFocusParentChanged));
#endregion
private static void OnCanFocusParentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var element = obj as UIElement;
if(element == null) return;
if((bool)args.NewValue)
element.PreviewMouseDown += FocusOnParent;
else
element.PreviewMouseDown -= FocusOnParent;
}
private static void FocusOnParent(object sender, RoutedEventArgs e)
{
var listBoxItem = VisualUpwardSearch<ListBoxItem>(sender as DependencyObject) as ListBoxItem;
if (listBoxItem != null) listBoxItem.IsSelected = true;
}
public static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
{
while (source != null && source.GetType() != typeof(T))
source = VisualTreeHelper.GetParent(source);
return source;
}
}
What this little class does is help your control to focus on the ListBox selected item when you activate a control inside it (ie. your ComboBox). It works when the Mouse is clicked inside the ListBox item.
Now all you have to do is attach it to your ComboBox like so:
<DataTemplate x:Key="myDataTemplate">
<ComboBox
TitleText="{Binding Title}"
DescriptionText="{Binding DescriptionText}"
CotrolHelper:ListBoxHelper.CanFocusParent="true"/>
</DataTemplate>
Where ControlHelper is:
xmlns:ControlHelper="clr-namespace:WhereYouPutYour.ListBoxHelperClass"
And finally, to Disable (but I recommend setting to Auto) the Scrollbar, you can use the ScrollViewer attached property in your ListBox like this:
<ListBox
x:Name="SettingListBox"
Template="{StaticResource myDataTemplate}"
ScrollViewer.VerticalScrollBarVisibility="Disabled" >
<Item>
<Setting Title="Foo" Description="Bar">
<Option>Yes</Option><Option>No</Option>
</Setting>
</Item>
</ListBox >
You can use a ListBox, just change how the data is presented. For example, this code will default to not having a scroll bar. (I want scroll bars, so I have to explicitly wrap the whole thing in a ScrollViewer.)
<ListBox.Template>
<ControlTemplate>
<StackPanel IsItemsHost="True" >
</StackPanel>
</ControlTemplate>
</ListBox.Template>
精彩评论