change the template of an item after clicking on it - silverlight

i have a class named :
public class CountryTemplateSelector : ContentControl
{
public DataTemplate TrueTemplate
{
get;
set;
}
public DataTemplate FalseTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Shopping_Ingredients itemAux = item as Shopping_Ingredients;
if (itemAux != null)
{
if (itemAux.IsMarked == true)
return TrueTemplate;
else
return FalseTemplate;
}
return base.SelectTemplate(item, container);
}
public virtual DataTemplate SelectTemplate(object item, DependencyObject container)
{
return null;
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
and a DataTemplate declared in App.xaml :
<DataTemplate x:Key="SelectorForCheckbox">
<local:CountryTemplateSelector Content="{Binding}">
<local:CountryTemplateSelector.TrueTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="82">
<CheckBox Name="cb1" FontSize="15" IsChecked="{Binding Path=IsMarked, Mode=TwoWay}" Margin="0,4" RenderTransformOrigin="0.485,0.365" VerticalContentAlignment="Bottom" />
<TextBlock Text="{Binding AmountToString}" Margin="15,0,5,0" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="24"/>
</StackPanel>
</DataTemplate>
</local:CountryTemplateSelector.TrueTemplate>
<local:CountryTemplateSelector.FalseTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="82">
<CheckBox Name="cb1" FontSize="15" IsChecked="{Binding Path=IsMarked, Mode=TwoWay}" Margin="0,4" RenderTransformOrigin="0.485,0.365" VerticalContentAlignment="Bottom" />
<TextBlock Text="TEST" Margin="15,0,5,0" VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="24"/>
</StackPanel>
</DataTemplate>
</local:CountryTemplateSelector.FalseTemplate>
</local:CountryTemplateSelector>
</DataTemplate>
and a LongListSelector :
<toolkit:LongListSelector x:Name="recipe1" Background="Transparent"
ItemTemplate="{StaticResource SelectorForCheckbox}"
ListHeaderTemplate="{StaticResource citiesListHeader}"
ListFooterTemplate="{StaticResource citiesListFooter}"
GroupHeaderTemplate="{StaticResource groupHeaderTemplate}"
GroupItemTemplate="{StaticResource groupItemTemplate}" >
<toolkit:LongListSelector.GroupItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel/>
</ItemsPanelTemplate>
</toolkit:LongListSelector.GroupItemsPanel>
</toolkit:LongListSelector>
The LongListSelector launches ok, and the templates are shown ok. The problem is that i would like to be able to change the template of the specific item clicked. How can i do so?

We should be able to set the templates in code. See this StackOverflow question. Make sure that all the templates needed are resources in the App.xaml or in your specific UserControl. My example is from a button click, but the same approach should apply elsewhere in code. I'm not 100% certain it should be cast to ControlTemplate. It will probably very per template.
public void CLick_Method(object sender, RoutedEventArgs e)
{
this.recipe1.ItemTemplate = (ControlTemplate)Resources["SelectorForX"];
this.recipe1.ListHeaderTemplate= (ControlTemplate)Resources["NewListHeaderTemplate"];
// etc.
}

Related

UWP XAML How do I wrap text in a bound ListView

How do I wrap or otherwise display long strings in my listview control. I have been unsuccessful in wrapping, or otherwise displaying long text in my bound ListView control. My xaml page is basically a BOUND FlipView with an ItemTemplate that contains two bound textBlocks and a bound ListView. I can get the TextBlocks to wrap but not the listviewitems. It would seem like such a simple thing yet it eludes me.
Here is a portion of my xaml:
<Page.Resources>
<DataTemplate x:DataType="data:MydataObject" x:Key="MydataObjectTemplate">
<StackPanel HorizontalAlignment="Stretch" Height="596" Width="982">
<TextBlock Name="txtDataObjectId" Text="{Binding dataObject.Id}" Visibility="Collapsed" TextWrapping="WrapWholeWords"/>
<TextBlock FontSize="24" Text="{x:Bind dataObject}" HorizontalAlignment="Center" TextWrapping="WrapWholeWords"/>
<ListView ItemsSource ="{x:Bind theObjectDetails, Mode=OneWay }"
HorizontalAlignment="Stretch"
BorderBrush="Black"
BorderThickness="1"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel HorizontalAlignment="Stretch">
<ComboBox x:Name="cboCategory" Header="Category" SelectionChanged="cboCategory_SelectionChanged" />
<FlipView x:Name="FlipView1"
ItemsSource="{x:Bind MydataObjects, Mode=OneWay }"
ItemTemplate="{StaticResource MydataObjectTemplate}"
BorderBrush="Black"
BorderThickness="1"/>
</StackPanel>
</Grid>
//c#
public class mydataObject
{
public int Id { get; set; }
public dataObject theObject { get; set; }
public List<dataObjectDetails> theObjectDetails { get; set; }
public override string ToString()
{
return this.theObject.Subject;
}
}
public class dataObjectDetails
{
public int Id { get; set; }
public int dodId{ get; set; }
public string bodyText { get; set; }
public override string ToString()
{
return bodyText ;
}
}
Give the ListView an ItemTemplate, which puts the content in a TextBlock that wraps the text:
<ListView
ItemsSource="{x:Bind theObjectDetails, Mode=OneWay}"
HorizontalAlignment="Stretch"
>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding bodyText}"
TextWrapping="WrapWholeWords"
/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

How to put an Expander inside Datatemplate?

Strange one.
I have a contentcontrol on a WPF form, this loads a datatemplate within it.
This shows up fine (handwritten summary code so ignore errors/lack of attributes):
<DataTemplate>
<Label Content="Found datatemplate" />
</DataTemplate>
This however renders blank
<DataTemplate>
<Expander Header="Why dont I show">
<Label Content="Found datatemplate" />
</Expander>
</DataTemplate>
I have set the expander to visibile, isexpanded to true etc and no matter what it doesn't render at all.
Confused- is this just not possible?
I've recently done something similar to what you're describing and it worked for me. I have an ItemsControl that binds to a collection of view models, each of which contains a UserControl representing custom content. I implemented the ItemsControl.ItemTemplate to display the custom control inside an Expander like this:
<ItemsControl Margin="0,20,0,0" ItemsSource="{Binding ControlItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="0,0,0,0"
BorderBrush="#E7E7E7"
BorderThickness="0,1,0,0"
Padding="20,0">
<Expander Foreground="#E7E7E7"
IsExpanded="{Binding Path=IsExpanded,
Mode=TwoWay}">
<Expander.Header>
<Grid>
<TextBlock HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="24"
Text="{Binding Title}" />
</Grid>
</Expander.Header>
<DockPanel>
<ScrollViewer MinHeight="250">
<ContentControl Content="{Binding Control}" />
</ScrollViewer>
</DockPanel>
</Expander>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This is what my view model looks like:
public class SidePanelControlItem : ModelBase
{
private bool _isExpanded;
public SidePanelControlItem(UserControl control)
{
if (control == null) { throw new ArgumentNullException("control");}
Control = control;
}
public string Title { get; set; }
public UserControl Control { get; private set; }
public bool IsExpanded
{
get { return _isExpanded; }
set
{
_isExpanded = value;
OnPropertyChanged("IsExpanded");
}
}
}

Using pushpins in a DataTemplateSelector

I have built a DataTemplateSelector which I use for showing a different pushpin on the map. I now have the following DataTemplate wich works in the way I want, except that it are not pushpins but textblocks which are shown on the map.
<DataTemplate x:Key="pushpinSelector">
<my:Pushpin Location="{Binding Location}" Tap="Pushpin_Tap">
<my:Pushpin.Template>
<ControlTemplate>
<local:PushpinTemplateSelector Content="{Binding}">
<local:PushpinTemplateSelector.ClusterTemplate>
<DataTemplate>
<TextBlock Text="Cluster" Width="100" Foreground="YellowGreen"></TextBlock>
</DataTemplate>
</local:PushpinTemplateSelector.ClusterTemplate>
<local:PushpinTemplateSelector.PushpinTemplate>
<DataTemplate>
<TextBlock Text="Pushpin" Width="100" Foreground="Blue"></TextBlock>
</DataTemplate>
</local:PushpinTemplateSelector.PushpinTemplate>
</local:PushpinTemplateSelector>
</ControlTemplate>
</my:Pushpin.Template>
</my:Pushpin>
</DataTemplate>
I would expect that it would work in the following format too:
<DataTemplate x:Key="pushpinSelector">
<local:PushpinTemplateSelector Content="{Binding}">
<local:PushpinTemplateSelector.ClusterTemplate>
<DataTemplate>
<my:Pushpin Location="{Binding Location}" Content="{Binding Count}" Foreground="YellowGreen"></my:Pushpin>
</DataTemplate>
</local:PushpinTemplateSelector.ClusterTemplate>
<local:PushpinTemplateSelector.PushpinTemplate>
<DataTemplate>
<my:Pushpin Location="{Binding Location}" Foreground="Blue"></my:Pushpin>
</DataTemplate>
</local:PushpinTemplateSelector.PushpinTemplate>
</local:PushpinTemplateSelector>
</DataTemplate>
But with this template it's only showing 1 black pushpin on the map. Am I doing something wrong with the Bindings? I'am not seeing why this is not working in the expected way.
As requested by #localjoost the code for the datatemplateselector:
The abstract class:
public abstract class DataTemplateSelector : ContentControl
{
public virtual DataTemplate SelectTemplate(
object item, DependencyObject container)
{
return null;
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
ContentTemplate = SelectTemplate(newContent, this);
}
}
And the implementation of the abstract class:
public class PushpinTemplateSelector : DataTemplateSelector
{
public DataTemplate ClusterTemplate
{
get;
set;
}
public DataTemplate PushpinTemplate
{
get;
set;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PushpinModel model = item as PushpinModel;
if (model.CurrentObject == null)
{
return ClusterTemplate;
}
else
{
return PushpinTemplate;
}
}
}
And how I use the Datatemplate (which is defined in my application resources section) in my map control:
<my:Map Height="624" HorizontalAlignment="Left" Name="map1" VerticalAlignment="Top" Width="468" CredentialsProvider="XXXXX"
ZoomLevel="13">
<my:MapItemsControl Name="pushPinModelsLayer" ItemsSource="{Binding PushpinModels}" ItemTemplate="{StaticResource pushpinSelector}" />
<my:Pushpin Name="myLocation" Template="{StaticResource MyLocationTemplate}"></my:Pushpin>
</my:Map>
As far is I can see there's not rocket science inside the pushpinselectortemplate.
I usually do this using multiple layers. This is a piece of code that comes from an actual working program. Maybe adding a Layer helps you out?
<Microsoft_Phone_Controls_Maps:Map x:Name="map"
CredentialsProvider="blah">
<Microsoft_Phone_Controls_Maps:MapLayer x:Name="MapLayer_GasStations">
<Microsoft_Phone_Controls_Maps:MapItemsControl
ItemsSource="{Binding GasStations}"
ItemTemplate="{StaticResource GasStationViewModelTemplate}" />
</Microsoft_Phone_Controls_Maps:MapLayer>
<Microsoft_Phone_Controls_Maps:MapLayer x:Name="MapLayer_RoadBlocks">
<Microsoft_Phone_Controls_Maps:MapItemsControl
ItemsSource="{Binding RoadBlocks}"
ItemTemplate="{StaticResource RoadBlockViewModelTemplate}" />
</Microsoft_Phone_Controls_Maps:MapLayer>
</Microsoft_Phone_Controls_Maps:Map>
Haven't found a different approach than the first one described in the question. For now I will work with it in that way.

How can I track the selected TabPage's DataContext from my ViewModel?

I have TabItem class:
public class TabItem
{
public string Header { get; set; }
public IView Content { get; set; }
}
and in my model:
public ObservableCollection<TabItem> Tabs
{
get { return _tabs; }
set
{
if(_tabs!=value)
{
_tabs = value;
RaisePropertyChanged("Tabs");
}
}
}
public TabItem CurrentTabItem
{
get { return _currentTabItem; }
set
{
if (_currentTabItem != value)
{
}
_currentTabItem = value;
RaisePropertyChanged("CurrentTabItem");
}
}
In View i'm binding to ModelView:
<TabControl x:Name="shellTabControl" ItemsSource="{Binding Tabs}"
IsSynchronizedWithCurrentItem="True" SelectionChanged="ShellTabControlSelectionChanged">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Content}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
From view i want to change ViewModel's CurrentTabItem property:
private void ShellTabControlSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(e.Source is TabItem)
{
var tabItem = e.Source as TabItem;
ViewModel.CurrentTabItem = tabItem; //don't work
}
}
What is the best approach to convert TabControl's TabItem to my TabItem?
Maybe it is better to use SelectedItem="{Binding CurrentTabItem, Mode=TwoWay, UpdateSourceTrigget=PropertyChanged}"?
<TabControl x:Name="shellTabControl" ItemsSource="{Binding Tabs}"
IsSynchronizedWithCurrentItem="True"
SelectionChanged="ShellTabControlSelectionChanged"
SelectedItem={Binding Path=CurrentTabItem,Mode=Twoway}>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Content}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will get you the selected TabItem.......
Also change the Name of your Custom "TabItem" its confusing ;)

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