How to switch contenttemplate dependent which view model object? - c#

How to settings which view showup when specific viewmodel set? i read this explain with data template but i still dont understand. cause it dont show all code that use data template as switch view. https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern
<Window x:Class="Pandora.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Pandora.ViewModels"
Title="Pandora" Height="350" Width="525">
<Window.ContentTemplate>
<DataTemplate DataType="{x:Type vm:IndexViewModel}">
<TextBlock>Index Content</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
<TextBlock>Dashboard Content</TextBlock>
</DataTemplate>
</Window.ContentTemplate>
</Window>
C#
using Pandora.ViewModels;
namespace Pandora.Views;
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new IndexViewModel();
}
}

For Window to show anything it should have some Content (empty Window template contains <Grid></Grid> element). Simply copy Content from DataContext by using binding.
In your case you want to show elements, which don't have visual representation of their own (IndexViewModel, DashboardViewModel), so they require a custom DataTemplate. You already have, just put them in Resources.
<Window x:Class="Pandora.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Pandora.ViewModels"
Title="Pandora"
Content="{Binding}"
Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:IndexViewModel}">
<TextBlock>Index Content</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
<TextBlock>Dashboard Content</TextBlock>
</DataTemplate>
</Window.Resources>
</Window>
or more common method which uses a separate element to insert custom content:
<Window x:Class="Pandora.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Pandora.ViewModels"
Title="Pandora"
Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:IndexViewModel}">
<TextBlock>Index Content</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
<TextBlock>Dashboard Content</TextBlock>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentPresenter Content="{Binding}"/>
<TextBlock Text="Demo watermark" VerticalAlignment="Center" HorizontalAlignment="Center" IsHitTestVisible="False"/>
</Grid>
</Window>

You must assign the IndexViewModel or DashboardViewModel instance to the Window.Content property. The ContentTemplate applies to the data object that is the current value of the Content property:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.Content = new IndexViewModel();
}
}

Related

WPF Passing DataSource into a UserControl

I have a very simple usercontrol:
<UserControl x:Class="PointOfSale.UserControls.HousesGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ItemsControl x:Name="LayoutRoot" ItemsSource ="{Binding PopularHouses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton
Content="{Binding FormattedPanelTimeRemaining}"
Style="{StaticResource MetroToggleButtonStyle}"
Height="45"
Width="80"
VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see the ItemSource property is bound to the PopularHouses property on the ViewModel of the parent. This works great. However, what I want to do is set the ItemSource of the LayoutRoot element to a different property at the point on the parent form where the control is inserted in the XAML.
The end result should then be multiple instances of this user control, bound to several different properties on the parent's datacontext.
Could someone explain how to implement this?
You just have to bind your UserControl's DataContext to the datacontext of the first ContentControl using RelativeSource.
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}"
I have made the following sample:
The mainwindow XAML
<Window x:Class="WpfDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfDataContext"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<local:UserControl1/>
</Grid>
</Window>
We set its datacontext to Self just for the purpose of this sample. In codebehind we define a simple property to show how it works:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string SomeString { get; set; } = "Hello";
}
Then, the usercontrol XAML:
<UserControl x:Class="WpfDataContext.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}">
<Grid>
<TextBlock Text="{Binding SomeString}"/>
</Grid>
</UserControl>
Note how we bind its DataContext property since this is the key.
I use a Textblock for simplicity, but the principle applies for your case also

How to use ContentPresenter with ElementHost

I have
three WPF UserControls with their Viewmodels
one WPF container for displaying one of the WPF Usercontrols
WinForm Usercontrol
I want: display different WPF UserControl, which I set is in WinForms User Control
public partial class WinContainer : UserControl
{
public WinContainer()
{
InitializeComponent();
WPFContainer WPFControl = new WPFContainer();
PartPageViewModel ss = new PartPageViewModel();
WPFControl.DataContext = ss;
ElementHost elHost = new ElementHost();
elHost.Child = WPFControl;
elHost.Dock = DockStyle.Fill;
this.Controls.Add(elHost);
}
}
<UserControl x:Class="MDMSpecification.Views.WPFContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MDMSpecification.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:services="clr-namespace:MDMSpecification.Services"
xmlns:viewModels="clr-namespace:MDMSpecification.ViewModels"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="Assembly" DataType="{x:Type viewModels:AssemblyPageViewModel}">
<local:AssemblyPageView />
</DataTemplate>
<DataTemplate x:Key="Part" DataType="{x:Type viewModels:PartPageViewModel}">
<local:PartPageView />
</DataTemplate>
<DataTemplate x:Key="Drawing" DataType="{x:Type viewModels:DrawingPageViewModel}">
<local:DrawingPageView />
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ContentPresenter Content="{Binding}" />
</StackPanel>
</UserControl>
In result I have this:
What's wrong with my code?
Remove x:Key="XYZ" from DataTemplate.
In order to apply a DataTemplate automatically to DataType you should omit the key.
For example:
<DataTemplate DataType="{x:Type viewModels:AssemblyPageViewModel}">
<local:AssemblyPageView />
</DataTemplate>
This xaml is equivalent to:
<DataTemplate x:Key={x:Type viewModels:AssemblyPageViewModel}
DataType="{x:Type viewModels:AssemblyPageViewModel}">
<local:AssemblyPageView />
</DataTemplate>
DataTemplates with a key should be applied explicitly like this:
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource Assembly}" />

Multiple usercontrol not reflected while setting DataTemplate to ViewModel in MVVM

I am trying to add two usercontrols in one view
usercontrol working fine which get call in constructor i am implementing INotifyPropertyChanged for 'SelectedViewModel'.
property get updated for second usercontrol but not reflected in view .
thanks in advance
<Window x:Class="MovieBookinGWithUserControl.View.StartupWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MovieBookinGWithUserControl.ViewModel"
xmlns:localview ="clr-namespace:MovieBookinGWithUserControl.View"
Title="StartupWindow" Height="AUTo" Width="Auto">
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginWIndowViewModel}">
<localview:LoginWindow></localview:LoginWindow>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MovieInfoViewModel }">
<localview:MovieInfo ></localview:MovieInfo>
</DataTemplate>
</Window.Resources>
<Grid>
<DockPanel LastChildFill="True">
<StackPanel x:Name="navigation" DockPanel.Dock="Left" Width="Auto">
</StackPanel>
<ContentControl x:Name="Pages" DockPanel.Dock="Right" Content="{Binding SelectedViewModel,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DockPanel>
</Grid>
</Window>

How to access this user control in Main Window

I have one user control in my WPF app
<UserControl x:Class="NewWPFApp.ProgressControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Expander Header="{Binding Path=Headerval}">
<StackPanel Margin="10,4,0,0">
<DataGrid
x:Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=records}"/>
</StackPanel>
</Expander>
</Grid>
and in my Mainwindow when I am doing this
<Window xmlns:NewWPFApp="clr-namespace:NewWPFApp" x:Class="NewWPFApp.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">
<Grid>
<ListBox x:Name="peopleListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="AliceBlue">
<NewWPFApp:ProgressControl Height="100" Width="100"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I cant see the output.
If I remove it from the Data template then it works.But not inside the data template.
What am I missing ???
Thanks
ListBox.ItemTemplate specifies how items added to a list box will be displayed. In your case, you uses your own custom control. However, at the same time you added no items to the list box so there is nothing to display. To populate the list box you can use binding e.g.:
<ListBox ItemsSource="{Binding ItemsToBeDisplayed}" x:Name="peopleListBox" >
...
</ListBox>
Where ItemsToBeDisplayed is a property of a view model (if you use MVVM pattern) that returns a collection of objects. If not you can populate ItemsSource without using binding:
var list = new List<People>();
//Add objects to a list
peopleListBox.ItemsSource = list;
I assumed that you have People class that models people you want to display in your application. People class should have Headerval and records properties because you use them in your your ProgressControl control. If you do as described, a new instance of your ProgressControl control will be created for every object in ItemsSource.

Showing a control based on type of databound viewmodel?

I'm learning WPF at the moment. I'm finding xaml quite tough to use. I have MainWindow.xaml defined like this:
<Window x:Class="Compliance.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>
<ResourceDictionary Source="MainWindow.Resources.xaml"></ResourceDictionary>
</Window.Resources>
<Grid>
</Grid>
</Window>
And MainWindow.Resources.xaml like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Compliance.ViewModel"
xmlns:vw="clr-namespace:Compliance.View">
<DataTemplate DataType="{x:Type vm:Entities.AbstractEntityViewModel}">
<vw:AbstractEntityView></vw:AbstractEntityView>
</DataTemplate>
</ResourceDictionary>
AbstractEntityView is like this:
<UserControl x:Class="Compliance.View.AbstractEntityView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Label Content="ID:"></Label>
<TextBlock Text="{Binding Path=EntityId}"></TextBlock>
</StackPanel>
</UserControl>
Then in App.xaml.cs I am overriding OnStartup like this:
MainWindow window = new MainWindow();
//Model class
Individual ind = new Individual(1,"Name");
//subclass of AbstractEntityViewModel
var vm = new Entities.IndividualEntityViewModel(ind);
window.DataContext = vm;
window.Show();
However, nothing appears in the window.
I used the answer from this question to get my control to render. However, this requires you to refer to elements in the view from the code, which I don't want to do.
Is it possible to get a window to pick a View to render based on the ViewModel set as its datacontext? Or do I have the wrong idea about how MVVM is supposed to work?
You have the right idea, but you're not actually telling WPF to display your ViewModel anywhere
I usually host a ViewModel in a ContentControl object if I am binding to a single ViewModel
<Window x:Class="Compliance.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>
<ResourceDictionary Source="MainWindow.Resources.xaml"></ResourceDictionary>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding }" />
</Grid>
</Window>
The ContentControl is usually not needed for lists of Models or ViewModels, since the object is automatically inserted as the Content property of the ContentPresenter of each item. For example, no ContentControl is needed when binding a ListBox to a collection of ViewModels
<ListBox ItemsSource="{Binding MyCollectionOfViewModel}" />

Categories

Resources