I am wrapping Windows Form legacy control inside WPF control , I am doing this for a few Windows Form controls. ie:
// WinFormHost control, only AWrapper is shown here, but there
//are BWrapper, CWrapper etc
public class AWrapper : WindowsFormsHost
{
Child=new ALegacyWinFormControl();
}
//WPF control, similarly there are BControl, CControl etc
<UserControl x:Class="AControl">
<local:AWrapper />
</UserControl>
At my ResourceDictionary I am defining a key for each WPF controls
// each WPF control has a key
<ResourceDictionary>
<DataTemplate x:Key="AControlKey">
<AControl />
</DataTemplate>
<DataTemplate x:Key="BControlKey">
<BControl />
</DataTemplate>
</ResourceDictionary>
There is one layer of duplication that I want to eliminate-- I will have to define each WPF control for each WindowsFormsHost wrapper, which is redundant. Is there anyway that I can do away with the UserControl object, and connect the DataTemplate straight with the WindowsFormsHost control? ie: I am looking at something like this:
// This wouldn't work
<ResourceDictionary>
<DataTemplate x:Key="AControlKey">
<AWrapper/>
</DataTemplate>
<DataTemplate x:Key="BControlKey">
<BWrapper/>
</DataTemplate>
</ResourceDictionary>
But the above doesn't seem to work. Anyway to make the above work? Is it possible at all?
Edit:
This should work provided that AWrapper is a WindowsFormsHost:
<DataTemplate x:Key="AControlKey">
<local:AWrapper/>
</DataTemplate>
Sample usage:
<ContentControl Content="." ContentTemplate="{StaticResource AControlKey}" />
No UserControl is involved.
local maps to the namespace in which the AWrapper class is defined:
xmlns:local="clr-namespace:WpfApplication1"
Related
I have problems with matching Ressource Dictionaries for a specific type in my project.
At start of my application my programm takes all .dll files in the folder and bind the .dlls to my project. After that I will search in all Assemblies for .xaml Files also the Ressource Dictionaries in my different dlls.
At the end I got a list with my Ressource Dictionaries.
Okay here is my problem now...How can I select the right dictionary for my type in the list View dynamicly. The second thing is how can I match the Dictionary Templates only to 1 control in my Window?
When the type of the items in my list view are changing check again for the right dictionary maybe i need another one
Here is an example of 1 dictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PresentObjectDataTemplate">
<Style TargetType="ListView">
<!-- My Style for my ListView Control-->
</Style>
<!-- My Data Templates only for 1 Control-->
<DataTemplate DataType="{x:Type local:Entity1}">
<StackPanel>
<TextBox Text="{Binding A1}"/>
<TextBox Text="{Binding A2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Entity2}">
<StackPanel>
<TextBox Text="{Binding A1}"/>
<TextBox Text="{Binding A2}"/>
</StackPanel>
</DataTemplate>
My MainWindow has 3 Controls
A treeView to select my List
A listview to show the objects in the list
A contentcontrol to show the properties of a selected object
When I merge the dictionary to the window ressources my list view and my content control will use the datatemplates. But i want that the templates only used in the contentcontrol
Here a picture from my idea for better understanding
myPicture
I'm writing a small application whilst learning MVVM in WPF.
As long as I keep on using one Window, everything is pretty easy.
Now I want to open a new Window with a specific ViewModel.
I have a main ViewModel, which contains a Command that should open a new Window / ViewModel, along with a Parameter.
To do this in an MVVM way, I've created a NavigationService, which I'd like to call like this:
public MainWindowViewModel()
{
DetailsCommand = new DelegateCommand(Details);
}
public void Details()
{
SessionsViewModel sessions = new SessionsViewModel();
_NavigationService.CreateWindow(sessions);
}
I've noticed that it's possible to "bind" Views and ViewModels in XAML, like this:
<Application x:Class="TimeTracker.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TimeTracker"
xmlns:vm="clr-namespace:TimeTracker.ViewModels"
xmlns:vw="clr-namespace:TimeTracker.Views"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:MainWindowViewModel}">
<vw:MainWindow />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SessionsViewModel}">
<vw:Sessions />
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
<Window x:Class="TimeTracker.Views.Sessions"
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"
xmlns:local="clr-namespace:TimeTracker.Views"
xmlns:vm="clr-namespace:TimeTracker.ViewModels"
mc:Ignorable="d"
Title="Sessions" Height="300" Width="300">
<Window.DataContext>
<vm:SessionsViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text="Hallo" />
</Grid>
</Window>
The problem I'm having is that I don't know how I can use this ResourceDictionary in my NavigationService, so that I can create a new Window by only using its ViewModel.
class NavigationService
{
public void CreateWindow(IViewModel viewModel)
{
//How do I create a new Window using the ResourceDictionary?
}
}
Make sure you have a ContentControl or ContentPresenter in your new window so that the ViewModel can be presented. Next, make sure that resource dictionary is in scope. Putting it in Application.Resources will make it global and guarantee that WPF can find the DataTemplate.
Also, don't use a Window class as your view in the DataTemplate. Use your sub-window panel (e.g., Grid, StackPanel, etc).
I do this:
<blah:MyChildWindow>
<ContentControl Content={Binding DataContext}/>
</blah:MyChildWindow>
And in Application.Resources:
<DataTemplate DataType={x:Type blah:MyViewModel}>
<blah:MyChildWindow/>
</DataTemplate>
BTW - using DataTemplates the way you are trying to do is an excellent pattern.
I need to load a collection of items as documents in AvalonDock 2.0. These objects inherit from an abstract class, for which I want to render a frame inside the document depending on which subclass are.
This is my XAML:
<ad:DockingManager Background="Gray" DocumentsSource="{Binding Path=OpenProjects}"
ActiveContent="{Binding Path=CurrentProject, Mode=TwoWay}">
<ad:DockingManager.DocumentHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=OpenProjects/Name}" />
</DataTemplate>
</ad:DockingManager.DocumentHeaderTemplate>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Grid.Resources>
</Grid>
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
So far I've achieved to show as many documents as items are in the OpenProjects collection, but I can't seem to show anything inside each document.
Plus, I don't know if I'm using ActiveContent properly: I want to assign to CurrentProject the ViewModel assigned on the current active document.
Thank you for your time.
The reason why you are not able to see any content is of the way you defined your LayoutItem templates. This can't work.
Also consider to use a custom control instead of the Frame. The Frame is very heavy. Unless you don't need to display HTML, avoid this control. Content navigation is very easy to implement, in case you want to show navigable content. Just wrap your document content into a UserControl.
You are using the ActiveContent property properly.
To fix your problem you have three recommended solutions, where the first doesn't exactly meet your requirements. Since you defined the DockingManager.LayoutItemTemplate wrong, I will show it anyway.
Solution 1: Local LayoutItemTemplate
In case you only need a single template for all LayoutDocument and LayoutAnchorable containers, you can use the DockingManager.LayoutItemTemplate property. This property accepts a single DataTemplate. Nested DataTemplate definitions, like in your code, are generally not supported by WPF.
<ad:DockingManager>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane />
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
Solution 2: Implicit DataTemplate
In more advanced scenarios you display different views based on different models. If the displayed content depends on the data type of the model alone (like in your case), the recommended approach is to provide implicit DataTemplate definitions.
WPF will automatically apply an implicit DataTemplate to every data type that matches the DataTemplate.TargetType of this template.
The DataTemplate is implicit, if it has no explicit x:Key value assigned. To ensure that the DataTemplate can actually be applied automatically, the DataTemplate must also be defined in the same resource scope as the target type. E.g., defining the DataTemplate in Application.Resources of App.xaml, would make the template to be applied automatically in the application scope.
<ad:DockingManager>
<ad:DockingManager.Resources>
<DataTemplate DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</ad:DockingManager.Resources>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
Solution 3: DataTemplateSelector
The previous solution, which uses implicit DataTemplate definitions, can be replaced with a DataTemplateSelector. DataTemplateSelector is another WPF concept to apply a DataTemplate selectively.
A DataTemplateSelector is the recommended choice, if selecting a DataTemplate may depend on more complex constraints, than the model's data type alone. It allows to e.g. evaluate the data item and chose the appropriate template based on certain criteria.
To define a template selector, you have to extend DataTemplateSelector, which is expected to return a DataTemplate.
The easiest way is to define the templates in App.xaml resource dictionary using an x:Key and then select from them based on the condition.
DockingManger accepts a template selector by assigning the DockingManager.LayoutItemTemplateSelector property:
App.xaml
Define the explicit DataTemplate using x:Key:
<Application.Resources>
<DataTemplate x:Key="SubclassAViewModelTemplate" DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassBViewModelTemplate" DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassCViewModelTemplate" DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Application.Resources>
DocumentManagerTemplateSelector.cs
The following code uses the Switch Expression, which is available since C# 8.0. It can be replaced with a switch statement or cascaded if-statements.
class DocumentManagerTemplateSelector : DataTemplateSelector
{
#region Overrides of DataTemplateSelector
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return item switch
{
SubclassAViewModel _ => Application.Current.Resources["SubclassAViewModelTemplate"] as DataTemplate,
SubclassBViewModel _ => Application.Current.Resources["SubclassBViewModelTemplate"] as DataTemplate,
SubclassCViewModel _ => Application.Current.Resources["SubclassCViewModelTemplate"] as DataTemplate,
_ => base.SelectTemplate(item, container)
};
}
#endregion
}
MainWindow.xaml
<ad:DockingManager>
<xcad:DockingManager.LayoutItemTemplateSelector>
<local:DocumentManagerTemplateSelector />
</xcad:DockingManager.LayoutItemTemplateSelector>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
I have the following structure of objects in my WPF application (simplified):
Main window
<Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedResources>
<ResourceDictionary Source="Styles.xaml" />
</ResourceDictionary.MergedResources>
</ResourceDictionary>
</Window.Resources>
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<local:MyEditor />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
...
Then, the editor:
<UserControl>
<!-- No resources here - getting styles from parent -->
...
</UserControl>
Resources in the main window are being managed dynamically (in certain circumstances I clear them and load another set to match theme of application), so I cannot embed the styles in UserControl (otherwise I would have to load styles in that UserControl dynamically as well). Of course apart from fact, that I actually don't even have to embed these styles in user control, because it will get them from parent.
But.
If I don't specify link to resources in XAML, the designer looks very sad (despite complicated structure of the UserControl):
Is there a way to provide information about related resources for the designer only?
I have created a tab control and Created the tabItems dynamically, but i dont know how to add controls into the tabItems using MVVM. Could any one help me
There are a few ways to programmatically add Tab Items in WPF and I am going to show you a simple example on how I deal with this in my application.
First I host a collection of the ViewModels for the TabItems (or Workspaces as I refer to them) in my MainWindowViewModel.cs:
private ObservableCollection<WorkspaceViewModel> _workspaces;
public ObservableCollection<WorkspaceViewModel> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<WorkspaceViewModel>();
}
return _workspaces;
}
}
Next I add a reference to the various controls in my MainWindow.xaml. This is important as we want to make sure that whenever the collection contains a ViewModel that it displays the appropriate View for that Model.
<Window.Resources>
<DataTemplate DataType="{x:Type vm:MyUserControlViewModel}">
<vw:MyUserControlView/>
</DataTemplate>
</Window.Resources>
If you have multiple types of UserControls you simply add them all here like this:
<Window.Resources>
<DataTemplate DataType="{x:Type vm:FirstUserControlViewModel}">
<vw:FirstUserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondUserControlViewModel}">
<vw:SecondUserControlView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ThirdUserControlViewModel}">
<vw:ThirdUserControlView/>
</DataTemplate>
</Window.Resources>
Next we add the TabControl and bind it to our Workspace Collection.
<TabControl ItemsSource="{Binding Workspaces}"/>
Then I simply add my ViewModels to the Collection to have them show up in the TabControl.
Workspaces.Add(new FirstUserControlViewModel());
Workspaces.Add(new SecondUserControlViewModel());
Workspaces.Add(new ThirdUserControlViewModel());
My WorkspaceViewModel that I base the TabItem collection of is very simple and looks something like this:
public abstract class WorkspaceViewModel : BaseViewModel
{
public String HeaderText { get; set; }
public override string ToString()
{
return HeaderText;
}
}
Adding a TabItem:
To create a TabItem you simply create a UserControl and ViewModel like you normally would using WPF and the MVVM pattern.
namespace MyApplication.ViewModel
{
public class FirstUserControlViewModel : WorkspaceViewModel
{
public FirstUserControlViewModel ()
{
base.HeaderText = "My First Tab";
}
}
}
Next you need to bind a View to your new ViewModel.
<DataTemplate DataType="{x:Type vm:FirstUserControlViewModel }">
<vw:FirstUserControlView/>
</DataTemplate>
Then you create an instance of the ViewModel and add it to the collection in your MainWindowViewModel.
FirstUserControlViewModel firstvm = new FirstUserControlViewModel();
Workspaces.Add(firstvm);
And now the TabItem should show up in your TabControl.
Loading TabItems dynamically using Extensions:
In some cases you might even need to load TabItems from plugins dynamically without the host application first knowing about the TabItem. In these cases you need to have the plugin register the View and ViewModel with the application domain.
This is very easy to do, and actually something I do for one of my MEF based projects. I have an post here, with some additional details as well.
All you need to do is add a Resource Dictionary to your plugin/extension and make sure that the host application loads it once the plugin has been imported.
To show you a fast example I would have a View.xaml in my extensions:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:MyExtension.Test">
<DataTemplate DataType="{x:Type vw:TestViewModel}">
<vw:TestView/>
</DataTemplate>
</ResourceDictionary>
I then expose the ResourceDictinary using MEF to the Host like this:
private ResourceDictionary _viewDictionary = new ResourceDictionary();
public ResourceDictionary Dict
{
get
{
return _viewDictionary;
}
}
_viewDictionary.Source =
new Uri("/MyExtension.Test;component/View.xaml",
UriKind.RelativeOrAbsolute);
Last you use Application.Current.Resources.MergedDictionaries.Add to load the View.xaml into the host.
You Dont have to add controls you just have to specify the UserControl.
TabControl has two properties ItemTemplate && Content Template
ItemTemplate is for how the Tab will look where as
ContentTemplate is how the Tab Content will Look... so...
Xaml for the above
<TabControl Grid.Row="1"
ItemsSource="{Binding Path=TabList}"
SelectedItem="{Binding Path=SelectedTab,
Mode=TwoWay}"
<!--This is How tab will look--> >
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20"
Height="20"
Margin="0,0,2,0"
Source="Images\TreeView\yourFavImg.png" />
<TextBlock Margin="0,4,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=TabText}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--This will be the content for the tab control-->
<TabControl.ContentTemplate>
<DataTemplate>
<!--This User Control will contain the controls you like-->
<ViewLayer:YourFavUserControl />
</DataTemplate>
</TabControl.ContentTemplate>
you dont have to add controls if you use mvvm. you just have to create datatemplates for your viewmodel objects you wanna display.
all you need is a contentcontrol/presenter which is bind to your viewmodel and the datatemplate will show what you want.