How to access the nested resource in WPF? - c#

I have the following resource:
<Window.Resources>
<Style x:Key="TopKey" TargetType="local:CustomType">
<Style.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</Style.Resources>
</Style>
</Window.Resources>
Then I have the following declaration:
<local:CustomType ItemTemplate="{StaticResource TopKey.NestedKey}"/>
Of course the above line doesn't compile and I don't know how to resolve this...

Putting a Resource in a ResourceDictionary of a FrameworkElement means that you don't want the Resource to be accessible outside this FrameworkElement (though you could get around it in code behind).
In your case NestedKey is in the wrong ResourceDictionary. Try something like this:
<Window.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
<Style x:Key="TopKey" TargetType="local:CustomType">
<!-- here I can use {StaticResource NestedKey} -->
</Style>
</Window.Resources>
<!-- in the same window I can use: -->
<local:CustomType ItemTemplate="{StaticResource NestedKey}"/>
You could also define a new Style that is based on the TopKey Resource, thus gaining access to it's ResourceDictionary (but that is a workaround to something you can do better)
<local:CustomType>
<local:CustomType.Style>
<Style BasedOn={StaticResource TopKey} TargetType="local:CustomType">
<!-- here I can use {StaticResource NestedKey} -->
</Style>
</local:CustomType.Style>
</local:CustomType>

simply do this
<Window.Resources>
<DataTemplate x:Key="NestedKey">
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
<Style x:Key="TopKey" TargetType="local:CustomType">
<Setter Property="ItemTemplate" Value="{StaticResource NestedKey}" />
</Style>
</Window.Resources>
<local:CustomType Style="{StaticResource TopKey}" />
hope that helps

Related

Dynamic Path in Treeview TextBlock

I am trying to change the Path in WPF Treeview TextBlock dynamically via user selection i.e. dropdown. Upon user interaction the path should take predefined values i.e. Name, Type, Order.
<TreeView x:Name="Main" ItemsSource="{Binding Items, NotifyOnSourceUpdated=True}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Name}" /> <--- Dynamically change this
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
In C# there is an BindingExpression helper class however I am not clear how to use it in ViewModel scenario
If you want to change the actual binding path, you need to do this programmatically. You cannot dynamically change the binding path in XAML.
A better option would be to change the value of the source property based on the selection in the ComboBox, or use a Style with triggers. Something like this:
<HierarchicalDataTemplate DataType="{x:Type models:Root}"
ItemsSource="{Binding Path=Children}">
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedValue}" Value="Name">
<Setter Property="Content">
<Setter.Value>
<TextBlock Text="{Binding Name}" />
</Setter.Value>
</Setter>
</DataTrigger>
...
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</HierarchicalDataTemplate>

TabControl.Resource DataTemplate Use Icon Instead of Text

I have a project that adds tabs to the view using a TabControl with DataTemplates like so:
<TabControl Name="dcTabControl"
ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTabViewModel}"
Height="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.MainContentHeight}">
<TabControl.Resources>
<!-- Removed numerous other tabs to save space -->
<!-- System Setup tab -->
<DataTemplate DataType="{x:Type vm:SystemSetupViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:SystemSetupUserControl />
</ScrollViewer>
</DataTemplate>
<!-- About tab -->
<DataTemplate DataType="{x:Type vm:AboutViewModel}">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:AboutUserControl />
</ScrollViewer>
</DataTemplate>
</TabControl.Resources>
</TabControl>
Each ViewModel has a Header property that is used to populate the text of the tab (e.g. "About").
I now have a requirement to change the "About" text to an icon. I have tried this but it doesn't change anything.
<!-- About tab -->
<DataTemplate DataType="{x:Type vm:AboutViewModel}">
<TabItem>
<TabItem.Header>
<Image Name="AboutTabImage" Height="auto" Width="auto" Source="Images/About.png" />
</TabItem.Header>
<TabItem.Content>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<v:AboutUserControl />
</ScrollViewer>
</TabItem.Content>
</TabItem>
</DataTemplate>
How can I get an icon in place of the text?
UPDATE Adding code to show how Header property is bound to Tab.
The Header is bound to the Tab using this Style
<!-- Standard Tab Style -->
<Style x:Key="TabStyle" TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Padding" Value="10,5,10,5" />
</Style>
I am now thinking I have to create a new style to use an icon instead of text, but not sure how I would apply that style to the data template.
You may add a DataTrigger to the TabItem Style that changes the Header to an Image if the Header property contains the string "About":
<Style TargetType="TabItem">
...
<Setter Property="Header" Value="{Binding Header}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Header}" Value="About">
<Setter Property="Header">
<Setter.Value>
<Image Source="Images/About.png"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>

UWP ListView DataTemplate Bindings

My ViewModel
BookingTakerViewModel
has a property
public List<string> Vias {get;set;}
I am wanting to bind a ListView with a DataTemplate but I can not find out how to do it. This is my code and it is throwing this error when it runs:
"Unable to cast object of type 'System.String' to type 'UI_Test_1.ViewModels.BookingTakerViewModel'."
I thought that the x:DataType should reference the class and in this case it is my Viewmodel.I presume that the x:Bind Vias is wrong beacuase this is a List so lost as to what to do.
<ListView
Name="viasList"
Width="300"
BorderBrush="Black"
BorderThickness="5"
ItemsSource="{x:Bind viewModel.Vias, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="MinHeight" Value="1" />
<Setter Property="MaxHeight" Value="15" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:Name="dt" x:DataType="viewmodels:BookingTakerViewModel">
<Grid>
<TextBlock FontSize="14" Text="{x:Bind Vias, Mode=OneWay}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The Vias are intantiated with data so it's not that which is causing a NullReference Exception. I believe the XAML is incorrect.
Probably you are looking for something like this?
<ListView.ItemTemplate>
<DataTemplate x:Name="dt" x:DataType="x:String">
<Grid>
<TextBlock FontSize="14" Text="{x:Bind}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Inside the data template, the context of {x:Bind} is the templated object, which is a string item from the collection you set in ItemsSource.

load user control dynamically in wpf using mvvm

Hi have 2 usercontrol(WPF).
I have to load this control according to condition.
I have ReadingBookDoubleView.xaml nad ReadingBookDoubleViewpdf.xaml
this is my code.
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModels:ReadingBookDoubleVM}">
<view:ReadingBookDoubleViewPdf/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:ReadingBookDoubleVM}">
<view:ReadingBookDoubleView/>
</DataTemplate>
</UserControl.Resources>
I have book kind in Viewmodel class which is bind to this view where I am loading the user control.
i have to load one control at a time.If book Kind is Pdf then I have load ReadingBookDoubleViewpdf control other wise I have to load ReadingBookDoubleView.
how can I load the control according to condition.
You could use a single DataTemplate with a Trigger:
<UserControl.Resources>
<DataTemplate DataType="{x:Type viewModels:ReadingBookDoubleVM}">
<ContentControl x:Name="Presenter" Content="{Binding}">
<ContentControl.ContentTemplate>
<DataTemplate>
<view:ReadingBookDoubleView />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Kind}" Value="Pdf">
<Setter TargetName="Presenter"
Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<view:ReadingBookDoubleViewPdf />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>

How can I get the current datacontext from my control

I have a listbox that contains a control chooser I'm trying to develop. Basically, the datasource is XML and I want to read the current context to decide which element control to display.
For this purpose I want to grab the XmlDataProvider with the current item's context, and evaluate that XML. Within XAML I would write {Binding Path=#label} to retrieve a label attribute from the curretn XML element. From code behind, I cant even figure where to get this XML, as it's passed to the class by the list control to this control, but not as an accessible property so far as I can find.
Anyway getting #label isn't sufficient; I want the XmlElement object in class ControlChooser, instantiated below.
<ListBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding XPath=*[not(self::units)]}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<W3V:ControlChooser/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<!-- Force the items to fill all available space. -->
<Style TargetType="ListBoxItem">
<Setter
Property="VerticalContentAlignment"
Value="Stretch"
/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Or, if you can suggest another way to get the job (switching what control is displayed) done....
You can use multiple DataTemplates to style the different element types. If you are familiar with XSLT, DataTemplates are the functional equivalent of xsl:template.
Example to style:
<Window x:Class="ZoomingScrollViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<XmlDataProvider x:Key="testData" XPath="/Contacts/*">
<x:XData>
<Contacts xmlns="">
<Person Name="John" />
<Person Name="Robby" />
<Business>
<ContactName>Jemma</ContactName>
<BusinessName>Ars</BusinessName>
</Business>
<Business>
<BusinessName>The other one</BusinessName>
</Business>
</Contacts>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource testData}}">
<ListBox.Resources>
<DataTemplate DataType="Person">
<TextBlock Text="{Binding XPath=#PersonName}" />
</DataTemplate>
<DataTemplate DataType="Business">
<StackPanel>
<TextBlock Text="{Binding XPath=ContactName}" />
<TextBlock Text="{Binding XPath=BusinessName}" />
</StackPanel>
</DataTemplate>
</ListBox.Resources>
</ListBox>
</Grid>
</Window>
If you wanted to select the DataTemplate based off of an attribute, rather than an element, you can use a DataTemplateSelector to run arbitrary code as explained in this question.
I found "Different views / data template based on member variable" as the closest to an answer. With the idea there, I could make a standalone control that made sense to me. This is the key bit:
<DataTrigger Binding="{Binding XPath=#kind}" Value="Number">
<Setter Property="ContentTemplate" Value="{StaticResource SmallInt}" />
</DataTrigger>
Here's the whole control:
<ContentControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:W3V="clr-namespace:W3.Views"
x:Class="W3.Views.ControlChooser">
<ContentControl.Resources>
<DataTemplate x:Key="StringChoice" >
<W3V:ComboView />
</DataTemplate>
<DataTemplate x:Key="SmallInt" >
<W3V:SpinView />
</DataTemplate>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource StringChoice}" />
<Style.Triggers>
<DataTrigger Binding="{Binding XPath=#kind}" Value="Number">
<Setter Property="ContentTemplate" Value="{StaticResource SmallInt}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>

Categories

Resources