Place a ContentControl inside a custom control XAML - wpf

I created a custom control and what to use a ContentControl inside that control for use of the MVVM design pattern however my control does not like this when I run the application. For testing I also tried other standard controls but none of them work inside the custom control... just more custom controls that are dependant on the parent custom control.
Does anybody suggest how to place standard controls such as the ContentControl inside a custom control?
Cheers.
EDIT
XamlParseException -> 'Add value to collection of type
'System.Collections.ObjectModel.ObservableCollection(Ribbon_Framework.RibbonTabItem)'
threw an exception.' Line number '8' and line position '14'.
<Ribbon:Ribbon AutomaticStateManagement="True" x:Name="Ribbon">
<ContentControl x:Name="SearchRibbon" Content="{Binding Path=SearchRibbon}" ContentTemplate="{DynamicResource SearchRibbonTemplate}" />
</Ribbon:Ribbon>
Inside the contentcontrol ->
<DataTemplate x:Key="SearchRibbonTemplate">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ribbon:RibbonTabItem Header="Search">
<Ribbon:RibbonGroupBox Header="{Binding Path=DisplayName}" Width="100">
<Ribbon:Button Width="100" Icon="{Binding Path=TemplateResource}" LargeIcon="{Binding Path=TemplateResource}" Command="{Binding Path=Commands}" />
</Ribbon:RibbonGroupBox>
</Ribbon:RibbonTabItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>

Your Ribbon control expects an object of RibbonTabItem type (since it houses an
ObservableCollection<RibbonTabItem>
so you can only add the RibbonTabItem class to it - you need to make sure your control allows other elements inside it. Some 3rd party controls get round this by providing a content control inside the inner item of their custom control (i.e. let RibbonTabItem have a ContentControl inside it) or allow you to customise the item template
You need to change your implementation of Ribbon or change the functionality of RibbonTabItem for this to work. Look at ItemsControl.Items property and see what type that is. You should try using that type for your ObservableCollection

Related

Expander-like WPF control that only hides empty children

In my WPF view, I need something similar to an Expander or a TreeView, but instead of completely hiding the content, I only want to hide empty parts, i.e. TextBoxes with null or empty text, or empty ItemCollections.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
I would like to avoid doing this in the ViewModel, as that would need a property for each section (and I need lots of them), but it's purely visual and therefore IMHO it only belongs to the View.
So I guess the way to go is to use DependencyProperties or write some CustomControls, but I don't have an idea where to start. The XAML of the end result could look something like this:
<CustomExpander Header="Main" CollapseContentIfEmpty="True">
<CustomExpander Header="Section1" CollapseContentIfEmpty="True">
<StackPanel>
<TextBox Text="{Binding SomeString}" />
<TextBox Text="{Binding SomeEmptyString}" />
</StackPanel>
</CustomExpander>
<CustomExpander Header="Section2" CollapseContentIfEmpty="True">
<ListView ItemsSource="{Binding SomeCollectionView}" />
</CustomExpander>
</CustomExpander>
In this example, if CollapseContentIfEmpty is set to true and the CollectionView shows no elements (e.g. due to filters), only the content of SomeString should be visible, along with all the headers. If SomeString is empty, only "Main" should be visible, as now all child CustomExpanders are empty as well.
Setting CollapseContentIfEmpty to false (e.g. via a Button like in Expander) would show all Children again, regardless if they are empty or not.
I thought about using a style with a DataTrigger or set Visibility with a converter, but how would I link that to the parent's setting (e.g. IsExpanded)?
Use a binding with a {RelativeSource}.
In the following example, the TextBlock is invisible unless you set the Tag property of the parent UserControl to true:
<UserControl xmlns:sys="clr-namespace:System;assembly=mscorlib">
<UserControl.Tag>
<sys:Boolean>false</sys:Boolean>
</UserControl.Tag>
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</UserControl.Resources>
<StackPanel>
<TextBlock Text="text..."
Visibility="{Binding Tag,
RelativeSource={RelativeSource AncestorType=UserControl},
Converter={StaticResource BooleanToVisibilityConverter}}" />
</StackPanel>
</UserControl>
You can of course replace the UserControl with a custom control with a custom bool property.
An Expander collapses its entire Content which is different from hiding specific controls in that content.

Showing UserControl once at the time with DataTemplateSelector

I have a couple specific user controls to Show some Content, e.g. simple like Image, WebControl but also two complex specific custom controls drawing on a canvas.
Now I thought using the DataTemplateSelector to handle the different UserControls. I actully used this http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector as a reference.
I changed the code so the form loads the UserControls dynamically (according to the file extension) in the following collection:
ObservableCollection<string> _pathCollection = new ObservableCollection<string>();
The only difference to the reference is now I want to navigate back and forward to the next control by showing one control only at the time. Which control should I use instead of ListView?
<Grid>
<ListView ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=This, Path=PathCollection}"
ItemTemplateSelector="{StaticResource imgStringTemplateSelector}">
</ListView>
</Grid>
How do I need to bind it to the template (equal to ItemTemplateSelector above)? WPF is still very new to me and I am learning.
Use a ContentControl. Bind your current item to the Content-property and the DataTemplateSelector to the ContentTemplateSelector-property.
<ContentControl Content="{Binding Path=CurrentItem, Mode=OneWay}", ContentTemplateSelector="{StaticResource imgStringTemplateSelector}" />
Your CurrentItem should be a DependencyProperty or a INotifyPropertyChanged-property of your DataContext. When you change your CurrentItem, the ContentControl will update the template automatically with help of your TemplateSelector.

How do I bind a generic window to an arbitrary view model at runtime, using DataTemplates?

I have a large number of ViewModel classes. For each of these classes, there is a corresponding .xaml file which is a 'UserControl'. In my App.xaml, I have them registered as DataTemplates, like so:
<DataTemplate DataType="{x:Type viewModel:MainMenuViewModel}">
<view:MainMenuView/>
</DataTemplate>
With the idea being that WPF will be able automatically swap in the necessary user controls at runtime. For example, this works:
<Grid>
<StackPanel>
<TextBlock Text="SuperApp" />
<ItemsControl>
<ViewModels:MainMenuViewModel/>
</ItemsControl>
</StackPanel>
</Grid>
In that the entry "MainMenuViewModel" is automatically replaced by the MainMenuView, bound to the MainMenuViewModel. Great. My current goal is now this: I want to have a button, on, say, a view embedded in the MainMenuView, which opens a popup window, which will have a new ViewModel inside. The idea is to set it up so that I have a single 'generic' popup form, in which I embed an arbitrary ViewModel, and let WPF handle actually rendering it with DataTemplates, similar to the above. So I have a command bound to a button, like so:
<Button Command="{Binding Path=LaunchInStandaloneForm}" Content="Rip Out"/>
Which successfully creates a new window, sets the dataContext equal to the appropriate ViewModel, and shows the window.
The question is: How do I set up the XAML of this popup window so that it will render the appropriate DataTemplate for the ViewModel which is the DataContext? I've tried:
<Grid>
<ItemsControl ItemsSource="{Binding Path=.}">
</ItemsControl>
</Grid>
, but it comes up blank. Any pointers?
To set the ItemsSource to the DataContext, use ItemsSource={Binding}. That assumes that the DataContext is an enumerable collection of your View Model objects.
Updating with correct answer:
Use a ContentControl :)
Hope that helps.
The accepted answer here shows how to change templates at runtime. You should be able to dig out the answer from that. Any questions just shout.
How to modify silverlight combobox data display
Hope that helps

Selecting the usercontrol to the relating datatemplate in mvvm

I have lets say a WeeklyViewUserControl.xaml and a DailyViewUserControl.xaml.
Normally I used stuff like this to switch content:
<DataTemplate DataType="{x:Type ViewModel:LessonPlannerViewModel}">
<View:LessonPlannerDailyUC/>
</DataTemplate>
This worked so far. But now I have still the WeeklyViewUC which uses 90 % of the LessonPlannerViewModel code so I want to make this additionally:
<DataTemplate DataType="{x:Type ViewModel:LessonPlannerViewModel}">
<View:LessonPlannerWeeklyUC/>
</DataTemplate>
but this can not work, because from where does the ContentControl
know that VM (LessonPlannerViewModel) should display a DailyViewUC or a WeeklyViewUC ?
<ContentControl Content="{Binding VM}" />
This is my further scenario:
The DailyViewUC has a Button "Weekly View" which is executed via Command="{...}" to the Command in the LessonPlannerViewModel.
The WeeklyViewUC has a Button "Daily View"...
Depending on what button is pressed the datatemplate should somehow choose the appropriate UserControl to display!
How can I do that?
You can create a DataTemplateSelector and assign it to the ContentTemplateSelector property of the ContentControl.

Is it Possible to apply a DataTemplate to a Page?

I'm trying to follow the MVVM pattern laid out here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090097 I have this in my MainWindowResources.xaml file:
<DataTemplate DataType="{x:Type vm:VendorsViewModel}">
<vw:Vendors/> <--- I get a "Can't put a page in a style" error in blend with this
</DataTemplate>
and I've got this in my MainWindow.xaml file
<Window.Resources>
<ResourceDictionary Source="MainWindowResources.xaml"/>
</Window.Resources>
The mainWindow.xaml file contains a menu on the left and page holder on the right. Can I apply a dataTemplate to a <Page>? Or does it have to be a <UserControl>? As it stands, nothing is being data bound, here's what I have on the page that I want to have the viewmodel applied to:
<Custom:DataGrid Margin="0,30,0,0" d:LayoutOverrides="Width" ItemsSource="{Binding Path=AllVendors, Mode=Default}" >
<Custom:DataGrid.Columns>
<Custom:DataGridTextColumn Header="Company Name" Binding="{Binding Path=Name}" />
</Custom:DataGrid.Columns>
</Custom:DataGrid>
DataTemplates are applied to Content, which in most cases is either the Content property of a ContentControl or the Items/ItemsSource property of an ItemsControl. Page is not derived from ContentControl (UserControl is) so a DataTemplate can't be applied to its Content.
From what you're doing here it doesn't sound like that's what you're trying to do though. It looks like you're trying to use a Page in a DataTemplate which is what the error is telling you. Page is treated like Window in that it is a root container that is intended to have visual Content defined in a xaml file. UserControl has a similar purpose but can be inserted anywhere into a layout. If you change vw:Vendors to be a UserControl that should get rid of this specific error but you should also consider whether you're gaining anything from having the UserControl instead of just putting its content directly into the DataTemplate - this can help discourage code-behind and force you to use your ViewModel correctly.

Resources