Bind TextBlock in DataTemplate (resource) to Content of checked radio button - silverlight

I have a TextBlock in resource data template:
<DataTemplate x:Key="MyDataTemplate" ItemsSource="{Binding MySource}">
<TextBlock x:Name="MyText" Text="{Binding ???}" />
</DataTemplate>
that I want to bind with Content of checked radio button:
<RadioButton GroupName="MyGroup" Content="Code" />
<RadioButton GroupName="MyGroup" Content="Description" />
if Code radio button is selected, then I want the text become Text={Binding Code}.
Please help, thank you.

Assuming this is backed by a viewmodel, you would set up your viewmodel thusly:
bool isCodeChecked;
public bool IsCodeChecked
{
get { return isCodeChecked; }
set
{
if(value == isCodeChecked) return;
isCodeChecked = value;
// raise notification that ***MyText*** property has changed (INotifyPropertyChanged interface)
}
public string MyText
{
get { return IsCodeChecked ? "Code" : "Description"; }
}
Then set up your XAML:
<RadioButton GroupName="MyGroup" Content="Code" IsChecked="{Binding IsCodeChecked, Mode=OneWayToSource}" />
<DataTemplate x:Key="MyDataTemplate" ItemsSource="{Binding MySource}">
<TextBlock x:Name="MyText" Text="{Binding MyText}" />
</DataTemplate>
The binding on the CheckBox will cause the boolean property on the viewmodel to be updated, which in turn will notify the textbox to update its bound value.

I created 2 DataTamplate in resources, and I switch my TreeView's ItemTemplate from code behind
if (ViewByCodeRadioButton.IsChecked == true)
MyTreeView.ItemTemplate = Resources["MyDataTemplateCode"] as DataTemplate;
else
MyTreeView.ItemTemplate = Resources["MyDataTemplateDesc"] as DataTemplate;

Related

Binding SelectedIndex property of a list to a property in different class in same/another project in Windows Phone 8?

There is something that I can't understand when I bind SelectedIndex of a list to a property. It works only if the property is part of the Codebehind of this XAML page
for example : If there we have test.xaml , test.xaml.cs and AppSettings.cs , the binding works correctly only if the SelectedIndex property is bound to a property in test.xaml.cs
After some trials , I found that if I set ItemSource and write all binding stuff in Code-behind , it works even if the property is in binder.cs !
I think is related to the order of ItemSource and SelectedItem.
The Code of the Property
private Currency sTileCurrency;
public Currency STileCurrency
{
get
{
return GetValueorDefault<Currency>("STileCurrency", null);
}
set
{
sTileCurrency = value;
if (AddOrUpdateValue("STileCurrency", value))
{
settings.Save();
}
}
}
(This won't work !)
<ListBox Name="sCurrencyLB" Margin="10,0,0,0" Width="Auto" Height="180" ItemsSource="{Binding SCurrencyList}" SelectedItem="{Binding STileCurrency, Source={StaticResource appSettings}, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Name="scountryNametb" Width="50" Text="{Binding code}" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Image Source="{Binding imgUrl}" Height="50" Width="50" HorizontalAlignment="Left" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But if we removed SelectedItem and ItemSource from XAML and set it in Codebehind
(This will work !)
sCurrencyLB.ItemsSource = Binder.Instance.SCurrencyList;
Binding binding = new Binding();
binding.Mode = BindingMode.TwoWay;
binding.Source = settings;
binding.Path = new PropertyPath("STileCurrency");
sCurrencyLB.SetBinding(ListBox.SelectedItemProperty, binding);
When I try to set ItemSource in XAML and Binding in Codebehind , it doesn't work too

Binding to a viewmodel property in a DataTemplate

I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a DataTemplate.
I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the Visibility property of a CheckBox in a DataTemplate to a Property in my viewmodel. (Using this scenario purely for learning/demo.)
I have a simple DataModel named Item, but is of little relevance in this example.
class Item : INotifyPropertyChanged
{
// Fields...
private bool _IsRequired;
private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _Items;
private bool _IsCheckBoxChecked;
private bool _IsCheckBoxVisible;
public ObservableCollection<Item> Items
{
get { return _Items; }
set { _Items = value; }
}
public bool IsCheckBoxChecked
{
get { return _IsCheckBoxChecked; }
set
{
if (_IsCheckBoxChecked == value)
return;
_IsCheckBoxChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked"));
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
}
}
public bool IsCheckBoxVisible
{
get { return !_IsCheckBoxChecked; }
set
{
if (_IsCheckBoxVisible == value)
return;
_IsCheckBoxVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
(Constructors and INotifyPropertyChanged implementation omitted for brevity.)
Controls laid out in MainPage.xaml as follows.
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:ItemViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" />
<ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemName}"/>
<CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" >
<CheckBox.DataContext>
<local:ItemViewModel/>
</CheckBox.DataContext>
</CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="IsCheckBoxVisible:"/>
<TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" />
</StackPanel >
<Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/>
</StackPanel>
</Grid>
The 'Hide CheckBoxes' checkbox is bound to IsCheckBoxChecked and is used to update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate to prove, (to myself,) the everything works.)
I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the DataTemplate function as expected but, alas, the Checkbox inside the data template remains unchanged.
I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item. Thus, the DataContext of the CheckBox in the DataTemplate is an Item, not your ItemViewModel. You can see this by your <TextBlock Text="{Binding ItemName}"/>, which binds to a property on the Item class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.

how to access the label inside datatemplate

hello everybody i have a listbox within which is a datatemplate.Inside it is checkbox,textbox,label...Wat i want is to get the value of the label wen the checkbox is unchecked? or any alternative as to how to access the label value but only wen the checkbox is unselected............PLease help me out.
the code is as
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="sp" Orientation="Horizontal" Margin="3,3,3,3" >
<CheckBox Name="chkSubject" IsChecked="{Binding RelativeSource{RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" VerticalAlignment="Center" Margin="0,0,4,0" Unchecked="chkSubject_Unchecked">
<TextBlock FontSize="11" Text="{Binding subject_name}" />
</CheckBox>
<Label Name="lbl_idOfSub" Content="{Binding subject_id}" Visibility="Visible">
</Label>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Since you're using binding on label, I'd go for accessing subject_id from the object the datatemplate is describing. Like this:
var subjectId = dataBoundItem.subject_id;
That's the correct way to go with MVVM and bindings.
UPDATE:
Here's the basic MVVM approach to solving this problem. First of all, I've cleaned up a bit your listbox declaration and added a trigger that sets IsSelected binding:
<ListBox ItemsSource="{Binding}">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Name="sp" Orientation="Horizontal" Margin="3,3,3,3" >
<CheckBox Name="chkSubject" IsChecked="{Binding IsSelected}" VerticalAlignment="Center" Margin="0,0,4,0" Unchecked="chkSubject_Unchecked_1">
<TextBlock FontSize="11" Text="{Binding SubjectName}" />
</CheckBox>
<Label Name="lbl_idOfSub" Content="{Binding SubjectId}" Visibility="Visible"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here, whenever value IsSelected on individual ListBoxItem changes, the "IsSelected" binding of the viewModel is changed. Here's the model:
public class SelectableItem : INotifyPropertyChanged
{
private string _subjectId;
private bool _isSelected;
private string _subjectName;
public string SubjectId
{
get { return _subjectId; }
set { _subjectId = value; OnPropertyChanged("SubjectId"); }
}
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; OnPropertyChanged("IsSelected"); }
}
public string SubjectName
{
get { return _subjectName; }
set { _subjectName = value; OnPropertyChanged("SubjectName"); }
}
// .. INotifyPropertyChangedImplementation
Your IsSelected will be set to true whenever relevant item is selected and to false whenever it is unselected. You may put your code in to the "set" item of the "IsSelected" property and check (value == false) and execute necessary piece of code as you see fit. This would be MVVM approach to the matter.
Using the event, you can do as follows:
private void chkSubject_Unchecked_1(object sender, RoutedEventArgs e)
{
FrameworkElement control = sender as FrameworkElement;
if (control == null)
return;
SelectableItem item = control.DataContext as SelectableItem;
if (item == null)
return;
string yourValue = item.SubjectId;
}
I strongly recommend you read about MVVM and bindings.
What about using the Checked and UnChecked events of your CheckBox, so that you can retrieve the value of subject_id which is binded to your Label.

How can I set my data binding in a WPF ItemsControl child to use a property on the parent data context?

I have a WPF ListView with a collection of RadioButtons. I want to set the GroupName of the child controls to be bound to a property on the parent data context. At the moment I am doing this by duplicating the property in each of the children's data context but that can't be right.
My XAML:
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
GroupName is a property on the parent View Model. I currently pass this onto the child View Model (where it is also a property) in the child constructor:
var item = new FilterOptionsRadioListItemViewModel(option, this.GroupName);
What is the correct way of doing this?
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You certainly can do that, but if you don't want to have to copy the GroupName to every child Option instance, you could instead do something like the following
// I'm imagining your viewmodels look something like this
public class ParentViewModel
{
private ObservableCollection<Option> m_OptionItems;
public ObservableCollection<Option> OptionItems
{
get { return m_OptionItems; }
set { m_OptionItems = value; }
}
private string m_ParentGroupName;
public string ParentGroupName
{
get { return m_ParentGroupName; }
set
{
m_ParentGroupName = value;
}
}
}
public class Option
{
private string m_Value;
public string Value
{
get { return m_Value; }
set
{
m_Value = value;
}
}
}
Then you can use a relative binding so you can look up the control tree to find the right datacontext, so you don't have to copy the ParentGroupName to each child Option instance.
If you name your window, you can traverse the root DataContext.
<Window x:Class="MyNamespace.MainWindow"
Name="Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="725">
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding ElementName=Window, Path=DataContext.ParentGroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

WPF ComboBox - Showing something different when selecting a value

What I need to accomplish is a ComboBox that shows People. When you expand the drop-down it shows FirstName and LastName, but when you select a person, the value shown at the combobox should be just the person's first name.
I have the following ItemTemplate:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
What else should I do to display only the first name when one item is selected?
Thanks!
EDIT
Changed the question slightly: What if I have the person's picture and instead of showing just the first name when a person is selected, I want to show only the picture. In other words, how can I have two separate templates - one for the drop-down and one for the selected item?
Here's the solution:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource ComplexTemplate}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource SimpleTemplate}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Basically, you create one more layer of DataTemplate here. ComboBox'es ItemTemplate always stays the same. But the content inside that template adjusts to the condition you are interested in.
The trick to discriminate dropped-down combobox items against selected-area combobox item is that selected-area is not really enclosed in ComboBoxItem object, it's part of ComboBox control itself. So FindAncestor for ComboBoxItem returns null, which we use in the trigger above.
I got it. I just needed to add the following to my ComboBox:
IsEditable="True" IsReadOnly="True" TextSearch.TextPath="FirstName"
Put a Trigger on the DataTemplate. The trigger should check the IsSelected property (the DataTemplate will need a TargetType set for this to work). If it is selected, you can set the Visibility of your TextBlocks to Collapsed, and set the Visibility of the Image to Visible. Then do the opposite for the case that it is not selected.
Another option is to use ItemTemplateSelector instead of ItemTemplate. I've been using it the following way.
ComboBoxItemTemplateSelector derives from DataTemplateSelector and has two attached properties, SelectedTemplate and DropDownTemplate. Then we set the DataTemplates from Xaml like this
<ComboBox ItemsSource="{Binding Persons}"
ItemTemplateSelector="{StaticResource ComboBoxItemTemplateSelector}">
<ts:ComboBoxItemTemplateSelector.SelectedTemplate>
<DataTemplate>
<TextBlock Text="{Binding FirstName}" />
</DataTemplate>
</ts:ComboBoxItemTemplateSelector.SelectedTemplate>
<ts:ComboBoxItemTemplateSelector.DropDownTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding LastName}" />
</StackPanel>
</DataTemplate>
</ts:ComboBoxItemTemplateSelector.DropDownTemplate>
</ComboBox>
In SelectTemplate we check if the current container is wrapped in a ComboBoxItem and if it is, we return the DropDownTemplate. Otherwise we return SelectedTemplate.
public class ComboBoxItemTemplateChooser : DataTemplateSelector
{
#region SelectedTemplate..
#region DropDownTemplate..
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
ComboBox parentComboBox = null;
ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
if (comboBoxItem == null)
{
parentComboBox = container.GetVisualParent<ComboBox>();
return ComboBoxItemTemplateChooser.GetSelectedTemplate(parentComboBox);
}
parentComboBox = ComboBox.ItemsControlFromItemContainer(comboBoxItem) as ComboBox;
return ComboBoxItemTemplateChooser.GetDropDownTemplate(parentComboBox);
}
}
A small demo project that uses this can be downloaded here: ComboBoxItemTemplateDemo.zip
I also made a short blog-post about this here: Different ComboBox ItemTemplate for dropdown. It also shows the other obvious way of doing the same thing but with properties instead of attached properties in ComboBoxItemTemplateSelector.
Oh, and GetVisualParent. Everyone seems to have their own implementations of this but anyway, here's the one I'm using
public static class DependencyObjectExtensions
{
public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
{
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
I used next approach
<UserControl.Resources>
<DataTemplate x:Key="SelectedItemTemplate" DataType="{x:Type statusBar:OffsetItem}">
<TextBlock Text="{Binding Path=ShortName}" />
</DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<ComboBox DisplayMemberPath="FullName"
ItemsSource="{Binding Path=Offsets}"
behaviors:SelectedItemTemplateBehavior.SelectedItemDataTemplate="{StaticResource SelectedItemTemplate}"
SelectedItem="{Binding Path=Selected}" />
<TextBlock Text="User Time" />
<TextBlock Text="" />
</StackPanel>
And the behavior
public static class SelectedItemTemplateBehavior
{
public static readonly DependencyProperty SelectedItemDataTemplateProperty =
DependencyProperty.RegisterAttached("SelectedItemDataTemplate", typeof(DataTemplate), typeof(SelectedItemTemplateBehavior), new PropertyMetadata(default(DataTemplate), PropertyChangedCallback));
public static void SetSelectedItemDataTemplate(this UIElement element, DataTemplate value)
{
element.SetValue(SelectedItemDataTemplateProperty, value);
}
public static DataTemplate GetSelectedItemDataTemplate(this ComboBox element)
{
return (DataTemplate)element.GetValue(SelectedItemDataTemplateProperty);
}
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uiElement = d as ComboBox;
if (e.Property == SelectedItemDataTemplateProperty && uiElement != null)
{
uiElement.Loaded -= UiElementLoaded;
UpdateSelectionTemplate(uiElement);
uiElement.Loaded += UiElementLoaded;
}
}
static void UiElementLoaded(object sender, RoutedEventArgs e)
{
UpdateSelectionTemplate((ComboBox)sender);
}
private static void UpdateSelectionTemplate(ComboBox uiElement)
{
var contentPresenter = GetChildOfType<ContentPresenter>(uiElement);
if (contentPresenter == null)
return;
var template = uiElement.GetSelectedItemDataTemplate();
contentPresenter.ContentTemplate = template;
}
public static T GetChildOfType<T>(DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
}
worked like a charm. Don't like pretty much Loaded event here but you can fix it if you want

Resources