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}" />
Related
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();
}
}
I'm trying to use compiled bindings in UWP with a simple use case.
In order to make my XAML more readable easy to manage, I've extracted the XAML of a DataTemplate to a UserControl. So I transformed this
MainPage.xaml
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind ViewModel.Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ProjectItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
<TextBlock Text="{x:Bind Description, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Page>
Into this
MainPage.xaml
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{x:Bind ViewModel.Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ProjectItem">
<local:MyUserControl1 />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Page>
MyUserControl1.xaml
<UserControl
x:Class="App1.MyUserControl1"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Description}" />
</StackPanel>
</UserControl>
The problem is that it doesn't even compile because x:Bind doesn't know the context.
How does x:Bind cover this use case?
I'd suggest create Dependency Property for ProjectItem on your MyUserControl1.xaml.cs
public static readonly DependencyProperty ProjectItemProperty =
DependencyProperty.Register(
nameof(ProjectItem),
typeof(ProjectItem),
typeof(MyUserControl1),
null);
public ProjectItem ProjectItem
{
get => (ProjectItem)GetValue(ProjectItemProperty);
set => SetValue(ProjectItemProperty, value);
}
Then on your XAML, bind the properties of your ProjectItem Dependency Property:
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind ProjectItem.Name, Mode=OneWay}" />
<TextBlock Text="{x:Bind ProjectItem.Description, Mode=OneWay}" />
</StackPanel>
Then on your MainPage.xaml, pass on the 'ProjectItem' collection item.
<DataTemplate x:DataType="local:ProjectItem">
<local:MyUserControl1 ProjectItem="{x:Bind}"/>
</DataTemplate>
If you use this approach, you could (or rather need to) add a property to MyUserControl1.xaml.cs that casts the current DataContext to a ProjectItem and returns it:
public ProjectItem Item => DataContext as ProjectItem;
public MyUserControl1()
{
InitializeComponent();
DataContextChanged += (s, e) => Bindings.Update();
}
You then bind to this property in the XAML markup:
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Bind Item.Name}" />
<TextBlock Text="{x:Bind Item.Description}" />
</StackPanel>
The other option would to use non-compiled {Bindings} or get rid of MyUserControl1 and revert to the inline DataTemplate.
This could be faster as the DataContext is casted only once when the Control is Loaded.
public ProjectItem ProjectItem { get; private set; }
public MyUserControl1()
{
InitializeComponent();
Loaded += (s, e) =>
{
ProjectItem = (ProjectItem)DataContext;
Bindings.Update();
};
}
Add this if DataContext change in MainPage:
DataContextChanged += (s, e) =>
{
ProjectItem = (ProjectItem)DataContext;
Bindings.Update();
};
Based on the answer given by #mm8.
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
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>
I'm using WPF, MVVM & PRISM.
I got a datatemplate in my View linked to a ViewModel UC2002_RFPBeheren_ViewModel cause the page were this code is included is linked to another ViewModel and I want the Button to have UC2002_RFPBeheren_ViewModel as ViewModel.
The datacontext of this page is UC2002_RFPBeheren_ProjectInfo_ViewModel but I want the SaveButton to use the ViewModel UC2002_RFPBeheren_ViewModel
Here is my code:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl ContentTemplate="{StaticResource SaveButton}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
Although the SaveButton displays but don't reacts on my command.
Do I forget something or is there another way to solve this?
Thanks in advance ;) !
=================================================================================
EDIT:
So I made some changes but it still doesn't work.
Code example:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
I set this property in the ViewModel of the page
public UC2002_RFPBeheren_ViewModel MySaveVM { get; set; }
My stackpanel looks now like this:
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}" ContentTemplate="{StaticResource SaveButton}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
what happens if you set your UV2002_RFPBeheren_ViewModel instance as the content for the ContentPresenter?
<ContentControl Content="{Binding MyUV2002_RFPBeheren_ViewModel}"/>
the DataTemplate just say how your Viewmodel should be displayed, but you have to set the DataContext or Binding to the instance of your viewmodel.
EDIT:
example
public class VMFoo
{
public UV2002_RFPBeheren_ViewModel MySaveVM {get; set;}
}
xaml
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/RFPModuleResources.xaml" />
<ResourceDictionary>
<DataTemplate x:Key="SaveButton" DataType="{x:Type vm:UC2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<x:local VMFoo/>
</UserControl.DataContext>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
EDIT: Small working sample
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication1="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type WpfApplication1:UV2002_RFPBeheren_ViewModel}">
<Button Command="{Binding SaveRFPCommand}">Save</Button>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.DataContext>
<WpfApplication1:VMFoo/>
</Grid.DataContext>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<ContentControl Content="{Binding MySaveVM}"/>
<Button Command="{Binding CloseTabCommand}">Close</Button>
</StackPanel>
</Grid>
</Window>
Viewmodels
public class VMFoo
{
public VMFoo()
{
this.MySaveVM = new UV2002_RFPBeheren_ViewModel();
}
public UV2002_RFPBeheren_ViewModel MySaveVM { get; set; }
}
public class UV2002_RFPBeheren_ViewModel
{
private DelegateCommand _save;
public ICommand SaveRFPCommand
{
get{if(this._save==null)
{
this._save = new DelegateCommand(()=>MessageBox.Show("success"),()=>true);
}
return this._save;
}
}
}
This is because of the way that ContentControl's work. It is assumed that the things in the ContentTemplate are related to the Content and so the DataContext is set to the Content and therefore that is the DataContext that the button within the template has access to. You haven't specified a Content so the value is null and so the DataContext is explicitly set to null. You can see this in a basic example. One thing you can do is to bind the Content of the ContentControl to the DataContext - see the last contentcontrol in the example.
<StackPanel DataContext="Foo">
<StackPanel.Resources>
<DataTemplate x:Key="withBtn">
<Button Content="{Binding}" />
</DataTemplate>
</StackPanel.Resources>
<Button Content="{Binding}" />
<ContentControl ContentTemplate="{StaticResource withBtn}" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource withBtn}" />
</StackPanel>
If you are using MVVM you must expose some instance of UC2002_RFPBeheren_ViewModel within your UC2002_RFPBeheren_ProjectInfo_ViewModel to bind against. Either as a property, or an item of a collection that is a property.
Everything in the view must be ultimately accessible from the ViewModel that is your data context (UC2002_RFPBeheren_ProjectInfo_ViewModel)