ContentControl.Content setter always overwrites the content template content - c#

I am fighting with this issue for at least a couple of hours already. I tried all possible solution I found on the net. My latest is below and you will see that I am trying to add a simple MenuBar to the main Window control and present the content beneath. My application is using MVVM and the view is assigned like this:
myMainWindow.Content = view; // this is what I cannot change
The soultion should be trivial but none works. I tried with ContentTemplate, ContentPresenter, Style setter, all the variations with binding but nothing works as expected that is whenever myMainWindow.Content menu bar disappears.
None of the samples available on the internet does not actaully show an application with MenuBar and content at the same time.
Is it so hard to add a menu bar in WPF application?
I would be more than happy for any new suggestions.
Thanks in advance.
<window:BaseWindow x:Class="OneTwoThree.Manager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:window="clr-namespace:Xsell.Client.Common;assembly=Xsell.Client.Common"
Title="{Binding Title}"
Width="1020"
Height="750"
Closed="AppClosed"
Icon="app.ico"
Loaded="AppLoaded">
<Window.Resources>
<Style TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<DockPanel>
<Menu IsMainMenu="True" DockPanel.Dock="Top">
<MenuItem Header="Admin">
<MenuItem Header="Manage Labels"></MenuItem>
</MenuItem>
</Menu>
<ContentPresenter DockPanel.Dock="Bottom"/>
</DockPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
</window:BaseWindow>

Your application isn't really using MVVM. At best it might be sort of halfway using MVVM.
Meanwhile, all the XAML inside your <Window> tag in your main window XAML file is the window's Content. And you are explicitly replacing that with some random object, so of course it's not there any more.
You aren't giving much detail, but you need to assign your main viewmodel to the Window's DataContext and not assign anything to the window's Content.
You didn't say what view is in this line of code, so I have no idea what you were trying to do there:
myMainWindow.Content = view; // this is what I cannot change
Is view a viewmodel, or a view? Whatever it is, don't do that.
You could assign your main viewmodel to the Window's datacontext in XAML, too:
<Window ... >
<Window.DataContext>
<local:MyViewModel />
</Window.DataContext>
<!-- ... -->
</Window>
Then bind your viewmodel to the Content property of a ContentPresenter. There's no need to apply the window's content via a template here so I eliminated that part. The only template you need in this snipppet is the DataTemplate which displays your viewmodel in the ContentPresenter.
The layout here is copied verbatim from MainWindow.xaml in a project I recently wrote. I replaced my main menu with yours and omitted my Window.Resources. That's the only change.
<Window ...>
<DockPanel>
<Menu IsMainMenu="True" DockPanel.Dock="Top">
<MenuItem Header="Admin">
<MenuItem Header="Manage Labels"></MenuItem>
</MenuItem>
</Menu>
<Grid>
<ContentPresenter
Content="{Binding}"
/>
</Grid>
</DockPanel>
</Window>
A simple {Binding} with no Source or Path etc. properties will bind to the DataContext. The ContentPresenter will inherit its DataContext from the Window that contains it.
Then, if you've got a data template defined with a DataType the same as your main viewmodel, that data template will be instantiated in that ContentPresenter.
"Bind" doesn't mean "assign in code-behind". It means to use the System.Windows.Data.Binding class. The easiest way to do that is in the XAML as I've shown above.

Related

Navigation with ContentPresenter MVVM Xamarin Forms

So I'm trying to make a simple navigation using MVVM in Xamarin forms, and people suggested that I used the control template with a content presenter.
So far so good.
I made the control template, but I'm not sure how to bind content presenter to my buttons so it changes when I click them.
App.Xaml
<Application.Resources>
<!-- Application resource dictionary -->
<ResourceDictionary>
<ControlTemplate x:Key="ThemeMaster">
<StackLayout>
<Label Text="App name" BackgroundColor="Blue"></Label>
<ContentPresenter x:Name="ContentPresenter"
Content="{Binding changeContentCommand}">
</ContentPresenter>
<Button Text="Click me" Command="{Binding changeContentButtonCommand}"></Button>
</StackLayout>
</ControlTemplate>
When I open the program contentpresenter starts showing the mainPage as it should, but what should I write in MainViewModel.cs too change contentpresenter too lets say LeaderBoardPage?
I think you should create different ControlTemplate for your different content.

Visual brush as an icon in the ContextMenu wpf

I want to use Visual Brush as an icon in the context menu (of treeview) in my wpf usercontrol.
I have a resource dictionary (separate icon xaml file), few lines from the file are given below:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<VisualBrush x:Key="Trashcan"
Stretch="Uniform">
I have merged the dictionaries in the usercontrol, and also checked that it has been added and can be accessed in the usercontrol xaml code.
The code in the usercontrol xaml (view) is given below, where the icons have to be used
<ContextMenu x:Key=xxxxxxxx>
<MenuItem Header="Delete" Command="{Binding xxxxxx, Source=xxxxxxx}" CommandParameter="{Binding}" IsEnabled="xxxxxxxxxx}" Icon="{StaticResource Trashcan}"/>
</ContextMenu>
Now the issue is I am unable to see the icons in the context menu, picture is attached below:
So far, I have tried the approach given in this link Using MahApps Icons with ContextMenu but didnt quite work for me.
Is there some way other than the one in the above mentioned link that could be used to show visual brush as an icon in the context menu.
NOTE: I cannot use the menuitem.icon -> image, as I have restrictions coming from other components of the application.
NOTE: I dont know whether it is important to state here that a Form is hosting my WPF usercontrol.
What you need is to set an Image to the MenuItem.Icon.
<MenuItem.Icon>
<Image Style="{StaticResource Trashcan}"/>
</MenuItem.Icon>
Define a style for that image in the resource dictionary:
<Style x:Key="Trashcan" TargetType="Image">
<Setter Property="Source" Value="/ProjectName;component/Images/Trashcan.png"/>
<Setter Property="Width" Value="24"/>
</Style>
added:
If you can't use MenuItem.Icon here's a trick you can do:
<MenuItem.Header>
<Grid>
<TextBlock Text="........"/>
<Image HorizontalAlignment="Left" Width="24" Margin="-24,0,0,0"
Style="{StaticResource Trashcan}"/>
</Grid>
</MenuItem.Header>

Navigate properly between tab in a WPF MVVM application

I'm doing a WPF application with the M-V-VM patern (i'm using galasoft if it's relevant), but I have issues when I navigate through a tabcontrol.
I'm adding tabs on the run. All the binding seems to goes well : inside the tab or in the header of the tab.
I've bind my tabcontrol to a observable list. Through an interface I'm adding several types of viewmodel to this list and the binding seems correct.
My XAML code looks like this :
<Grid>
<Grid.Resources>
<DataTemplate x:Key="itemTemplate">
<TechnicalControls:ItemTab />
</DataTemplate>
</Grid.Resources>
<TabControl Grid.Row="1" x:Name="MainTab" Grid.Column="1"
ItemsSource="{Binding TabViewModels}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}"
ItemContainerStyleSelector="{StaticResource LastItemStyleSelector}"
ItemTemplate="{StaticResource itemTemplate}"
>
<TabControl.Resources>
<DataTemplate DataType="{x:Type VM:JobViewModel}" x:Shared="False" >
<FunctionnalControls:Job />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ExcelJobViewModel}" x:Shared="False">
<FunctionnalControls:ExcelJob />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:MonitoringViewModel}" x:Shared="False">
<FunctionnalControls:Monitoring />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ErrorViewModel}" x:Shared="False">
<FunctionnalControls:Error />
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
For example if I go from a ExcelJob to another ExcelJob usercontrol, the new usercontrol is not load properly but it changes then it works, for exemple, I can go to a ExcelJob to another ExcelJob if only I go through the monitoring.
I've already look to this this but it didn't work for me.
I've also looked at this : it says that we should not used inputs because you can focus them. I've tried to set the IsEnabled property on the users controls to false. I did it when tabs were changing. It didn't work...
The only solution that I can see is to go through another a new usercontrol with no other purpose to be used every time a tab is changed but this is ugly, and I'm pretty sure, Microsoft thought about this and came up with a better solution.
If necessary I can put the code of the view model.
EDIT : Just to clarify, when I click on other tab with the same control, instead of showing me the new usercontrol, it shows me the previous one. In order to see the new one, I have to change to another tab with another usercontrol then come back on the one I want to see.
I've look through debug and the when I click on the other tab It doesn't call the viewmodel
<UserControl x:Class="App.ExcelJob"
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"
DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}">
<Grid >
<Label>Futur Excel Job</Label>
<TextBox Width="200" Height="60" Text="{Binding Header}"/>
</Grid>
</UserControl>
So Main returns the Mainviewmodel and Main.ExcelJobVM returns the good viewmodel of the usercontrol. the returned isntance is based on selected Index.
The only thing I need is to force the redrawing of the usercontrol or recall the method to update the datacontext, by loading the good viewmodel. I tried, I've failed so far. I'm not sure of what I'm doing because I want to use the event SelectionChanged of the tabcontrol but it would be in the code behind, and I don't know if it would still respect the MVVM pattern.
The problem is that you have the DataContext hardcoded in your UserControl, and your UserControl is a Template.
When you switch to a tab that uses the same Template, WPF doesn't bother to redraw the template and only changes the DataContext behind the template. But in your case, you have the DataContext hardcoded in the UserControl, so it's not using the existing data context from the TabItem.
The best solution would be to remove the DataContext binding from your UserControl, and let it be inherited from the TabItem when the selected item changes.
For example:
WPF says
User has selected ExcelJobA for display. Because of the DataTemplate, let me draw it using an ExcelJob UserControl
<TabItem>
<ContentPresenter DataContext="ExcelJobA">
<local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
</ContentPresenter>
</TabItem>
So an ExcelJob UserControl gets created, and by default the DataContext of ExcelJobA would be inherited by the UserControl.
When the user changes the selected tab to ExcelJobB, WPF goes
Hey, the user has changed to ExcelJobB. Because of the DataTemplate, let me draw it using an ExcelJob UserControl, BUT WAIT! I'm already displaying an ExcelJob UserControl, so let me just change the DataContext behind that to ExcelJobB
<TabItem>
<ContentPresenter DataContext="ExcelJobB">
<local:ExcelJob DataContext="{Binding Main.ExcelJobVM, Source={StaticResource Locator }}" />
</ContentPresenter>
</TabItem>
So the actual displayed ExcelJob UserControl does not get recreated or redrawn, but only the DataContext behind it changes.
HOWEVER, because you have hard-coded the DataContext inside your UserControl, the actual data context obtained from the selected item doesn't ever get used, because a DataContext specified from inside a <Tag> always takes precedence over a DataContext that would be inherited from further up the visual tree.
You need to remove the DataContext binding from inside your UserControl, and let it get passed in normally from your TabControl's SelectedItem, and it will work fine.
<TabItem>
<ContentPresenter DataContext="ExcelJobA">
<local:ExcelJob /> <!-- DataContext inherited from the ContentPresenter -->
</ContentPresenter>
</TabItem>

How do I "pass through" the inherited properties of Control and ContentControl to an element in a UserControl?

WPF question. I'm not sure what to google for.
I have a usercontrol:
<UserControl x:Class="MyProj.MyControl"
x:Name="Self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button />
</UserControl>
I use it like this:
<local:MyControl Background="Green" />
But the background doesn't seem to change. It's because the background of the button hasn't changed. I want the background of the button to use the background of the usercontrol.
I suppose I could do this:
<UserControl x:Class="MyProj.MyControl"
x:Name="Self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Background="{Binding Path=Background, ElementName=Self" />
</UserControl>
But I pretty much want all of the inherited properties from Control and ContentControl to apply to the button (ToolTip, BorderThickness, BorderBrush, etc).
So what can I do instead of this:
<UserControl x:Class="MyProj.MyControl"
x:Name="Self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Background="{Binding Path=Background, ElementName=Self"
ToolTip="{Binding Path=ToolTip, ElementName=Self"
BorderThickness="{Binding Path=BorderThickness, ElementName=Self"
BorderBrush="{Binding Path=BorderBrush, ElementName=Self"
...
...
...
... />
</UserControl>
?
(Note: This is a small (the smallest I could manage, in fact) example of a larger UserControl which hosts many controls.)
Ugh. Well, short answer: You can't, at least not easily. XAML doesn't work quite like HTML/CSS does. The Button (and for that matter, pretty much any Control) does not inherit properties from their containers by default.
You could craft your own Button, etc controls that do inherit...alternatively, you might be able to declare a Style that applies to everything and attempts to grab any containing elements properties (i.e., via RelativeSource FindAncestor)...or you could do what you're doing here: set every property manually.
Example of the Style approach:
<Style TargetType="{x:Type Control}">
<Setter
Property="Background"
Value="{Binding (Control.Background), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Control}}}"/>
</Style>

WPF Item Templates - TabControl

I am writing an application in which I utilize a tab control which will start with one tab open but allows the user to open multiple other tabs.
Each tab that is openned should have a treeview inside which I fill using databinding when the user loads a file.
I am new to WPF but I feel as if there is a way in which I can create a template containing each of the elements the TabItems should contain. How can I do this using templates? Right now my WPF for the tab items is the following
<TabItem Header="Survey 1">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</TabItem>
I think you want something like this:
<Window x:Class="TestWpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="TabItemTemplate">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding ListThatPowersTheTabs}"
ItemTemplate="{StaticResource TabItemTemplate}">
</TabControl>
</Grid>
You basically create re-usable templates as static resources which you refer to by their key name.
Usually in this sort of situation I bind my TabControl.ItemsSource to a ObservableCollect<ViewModelBase> OpenTabs, so my ViewModel is in charge of adding/removing new tabs as needed.
Then if you want something in every Tab, then overwrite the TabControl.ItemTemplate and use the following line to specify where to display the currently selected TabItem
<ContentControl Content="{Binding }" />
If you don't need to setup something in every single tab, you don't need to overwrite the TabControl.ItemTemplate - it will default to displaying the currently selected ViewModelBase in the Tab.
And I use DataTemplates to specify which View to use
<DataTemplate TargetType="{x:Type local:TabAViewModel}">
<local:TabAView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabBViewModel}">
<local:TabBView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabCViewModel}">
<local:TabCView />
</DataTemplate>

Categories