load user control dynamically in wpf using mvvm - c#

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>

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>

WPF MVVM Binding dynamic control in code behind and pass in View

I am working on WPF application using MVVM. I have two page. I have multiple UserControls in a page 1, on selection of UserControls from page 1, I want to show that selected userControl in 2nd page. Below are my code.
ViewModel Code
public RelayCommand<string> OnClickSelectWidgetCommand => new RelayCommand<string>((setUserControlName) =>
{
using (new CursorWait())
{
var MyContentControl = setUserControlName;
MessageBox.Show(MyContentControl);
//How to render UserControl to View?
}
}, true);
Here in above code I get the UserControl name in setUserControlName variable. Now how to bind that UserControl to XAML page? Below are my code that I have tried.
View Code
<StackPanel Background="Black" VerticalAlignment="Top">
<Border Name="UserControl1BorderLow" BorderBrush="White" BorderThickness="0" >
<ItemsControl ItemsSource="{Binding LowCollection}" Margin="4,0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" />
<!--what can I do here in above line to make it dynamically render the userControl in place of UserControlColumn1XL-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border></StackPanel>
Above code, In DataTemplate what need to be change to bind UserControls dynamically?
There are two ways to solve this, one involves setting the template based on your data type (DataTemplates) and the second involves setting it based on the data itself (DataTriggers).
In the first case your LowCollection should be an array of objects, or some base class that your view models are all derived from (ViewModel1, ViewModel2 etc). In this case you can get rid of your itemtemplate altogether and just add DataTemplates to specify how each of the items in your ItemsControl should be represented:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<UserControl2 />
</DataTemplate>
... etc...
In the second case you need to set a template based on the value of some property in your view model. In this case you do need to set the ItemTemplate, and you give it a Style which uses data triggers to set an appropriate DataTemplate:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue1">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="YourValue2">
<Setter Property="ContentTemplate" Value="{StaticResource YourDataTemplate2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
</DataTemplate>
</ItemsControl.ItemTemplate>
The relevant parts to note here are that there is a property in your view model called YourProperty which can have two values i.e. YourValue1 or YourValue2; the style above then selects either YourDataTemplate1 or YourDataTemplate2, depending on the value of YourProperty.

Conditional Static Resource For Itemsource

at the moment in order to fix a bug from telerik, my ItemsSource must be pointing to the viewmodel I'm currently working with.
Relationship.xaml
<UserControl.Resources>
<Client:PersonViewModel x:Key="MyViewModel"/>
</UserControl.Resources>
Where it's used.
<Telerik:GridViewComboBoxColumn Header="Relationship"
ItemsSource="{Binding GridRelationshipTypes, Mode=TwoWay, Source={StaticResource MyViewModel}}"
DataMemberBinding="{Binding RelationshipType}"
SelectedValueMemberPath="Id"
DisplayMemberPath="Name"
IsReadOnly="False"/>
I have four other view models this logic needs to be applied to. I don't want to create 5 different UserControls for such a small thing. I'm wondering if I can create a method such that it'll check what the current viewmodel type is and will use the corresponding viewmodel.
PseudoCode - ViewModelTypes is an enum.
public void StaticResourcToUse(ViewModelTypes viewModelType)
{
if (viewModelType == ViewModelTypes.PersonViewModel)
use personviewmodel resources
if (viewModelType == ViewModelTypes.BusinessViewModel)
use businessViewModel resources
}
If I understand correctly what you want is switch your view based on view model.
Use a ContentControl to display the data, and swap out the ContentTemplate in a trigger based on the property that changes.
Here's an example in Rachel Lim's blog that swaps a template based on a bound property:
<DataTemplate x:Key="CarTemplate" TargetType="{x:Type local:YourViewModel}">
<TextBlock Text="I'm a Car" />
</DataTemplate>
<DataTemplate x:Key="TrackTemplate" TargetType="{x:Type local:YourViewModel}">
<TextBlock Text="I'm a Track" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:YourViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource CarTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourType}" Value="Track">
<Setter Property="ContentTemplate" Value="{StaticResource TrackTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>

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>

Changing Conent Control template on Menu Item click?

Can you please tell me how to change content template when, in WPF, when it is clicked on different Menu Item headers. I've defined user control that I can put it as a template.
For example: Menu Items are: Home, Players, Team . when i click on Home I want that specific template in my Content Control to pop up, tehen when I click on Players I want another template (list of players) to pop in Content Control as template.
How to do that with triggers in XAML?
Thank you very much :)
You can use a ContentControl to host whatever your content will be, and set the ContentControl.ContentTemplate based on how you want to draw your content.
As a very basic example,
<ContentControl x:Name="MyContentControl">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="Home">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyHomeUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding }" Value="Players">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyPlayersUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding }" Value="Team">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:MyTeamUsercontrol />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style>
</ContentControl.Style>
</ContentControl>
And on MenuItem.Click
MyContentControl.Content = "Home"; // or "Players" or "Team"
In this example I'm using a string for the ContentControl.Content, however if you were to use a class object such as a HomeViewModel or PlayersViewModel, your XAML could be simplified to use implicit data templates, which are templates that automatically get used whenever WPF tries to draw a specific class
<Window.Resources>
<DataTemplate DataType="{x:Type HomeViewModel}">
<local:MyHomeUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type PlayersViewModel}">
<local:MyPlayersUserControl />
</DataTemplate>
<DataTemplate DataType="{x:Type TeamViewmModel}">
<local:MyTeamUserControl />
</DataTemplate>
</Window.Resources>
<ContentControl x:Name="MyContentControl" />
and
MyContentControl.Content = new HomeViewModel();
How about using a tab control with tab placement at the top? it would be much cleaner that way..
Edit; Example:
<TabControl>
<TabItem>
<TabItem.Header>
<TextBlock Text="Football header!"/>
</TabItem.Header>
<TabItem.Content>
<Button Content="Push for football!"/>
</TabItem.Content>
</TabItem>
</TabControl>
this might help also; http://www.switchonthecode.com/tutorials/the-wpf-tab-control-inside-and-out

Categories

Resources