Propagate DataContext to Dependency Property from ContentControl - c#

I have a styled ContentControl with a DependencyProperty which, among others, has a DataTemplate property (StatusTemplate) that I have to assign to a DataTemplate I got in resources (StatusTemplate1).
The ContentControl is shown and the 'Binding:: ' text from StatusTemplate1 is also displayed, but the binding is empty.
If I'm not mistaken, ContentControl doesn't propagate its DataContext to their Content, so the question is: is there any workaround I could use in order StatusTemplate1 receives the DataContext ? I'd prefer to use XAML only for this, but I've got no problem if code-behind is the way to go.
EDIT: the problem here is how to propagate the DataContext to the dependency property StatusTemplate. I changed the question title as it seems to be misleading.
The DataTemplate:
<DataTemplate x:Key="StatusTemplate1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Binding:: " />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
And the ContentControl:
<ContentControl Style="{Binding Path=Status, Converter={StaticResource StatusToStyleConverter}}"
dp1:AddOn.StatusTemplate="{StaticResource StatusTemplate1}">
</ContentControl>
Thanks

I have a very similar scenario, and if I recall correctly,
Content="{Binding}"
As an attribute of the contentcontrol actually works.

Related

Multiple DataContext bindings in UWP Page and CommandBar

I need to bind some specific elements in my UWP page to a different VIewModel than what the page has.
So in my page I bind to one DataContext:
<Page
DataContext="{Binding RootViewModel, Source={StaticResource Locator}}">
...
and I am now trying to bind the bottomAppBar CommandBar to a different ViewModel:
<Page.BottomAppBar>
<CommandBar DataContext="{Binding OtherViewModel, Source={StaticResource Locator}}">
<CommandBar.Content>
<TextBlock Text="{Binding Status}"></TextBlock>
But it doesn't work, the binding does not occur in the CommandBar.
Why?
By design, the DataContext of the CommandBar doesn't get passed to its Content. Actually, only the DataContext property of a child element will automatically get the same DataContext of its parent, unless you specifically break the flow by assigning another value to it.
In your case, the DataContext of your CommandBar will be OtherViewModel, but the Content property will remain null.
So instead of manually setting the DataContext, you should set its Content instead.
<CommandBar Content="{Binding OtherViewModel, Source={StaticResource Locator}}">
<CommandBar.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}"></TextBlock>
</DataTemplate>
</CommandBar.ContentTemplate>
...
</CommandBar>

Strange artifact when updating Pivot title in Windows Phone 8

I have a XAML page with just a Pivot with binded ItemSource and the following template (changing just the header for simpilcity)
<phone:Pivot
Margin="0,108,0,0"
ItemsSource="{Binding Services}">
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:Pivot.ItemTemplate>
<DataTemplate>
</DataTemplate>
</phone:Pivot.ItemTemplate>
</phone:Pivot>
The ItemSource (Services) is an ObservableCollection of a simple data type with just a Title property implementing INotifyPropertyChanged. When I change the Title of any item, the Pivot header is rendered very strangely:
Here is a complete simplified solution to reproduce the problem: https://dl.dropboxusercontent.com/u/73642/pivotproblem.zip
Is this a Pivot bug?
Indeed a bug... I had the same problem and I end up in forcing to reload whole DataContext of the Pivot.
Since I was using MVVM I just created new instance of the items collection and raised property changed for that.
It seems that TextBlock's Width is not updated after Title changes. You can check it by defining your TextBlock like this:
<TextBlock Text="{Binding Title}" Width="400" />
Hence it's not updated, two Titles overlap each other.

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>

WPF Canvas Binding

I'm rather new to WPF, so maybe this is a simple question. I have a class that derives from Canvas, let's call it MyCanvas. And I have a class, MyClass, that has a property of type MyCanvas. In XAML, I built a TabControl, so each TabItem binds to a MyClass object. Now, in the Content of every tab I want to display MyObject.MyCanvas.
How should I do that?
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<myCanvas:MyCanvas Focusable="true" Margin="10" >
<Binding Path="Canvas"></Binding>
</myCanvas:MyCanvas>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
You should use ContentPresenter
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<ContentPresenter Content="{Binding MyCanvas}" Focusable="true" Margin="10" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
Try using ContentPresenter and binding the contents to the property you want. If the property is a descendent of Canvas, this should result in it simply displaying that content. If the property was of another type, it would attempt to use a DataTemplate to render it.

WPF: Accessing two DataContexts in the same control

I am using an MVVM approach, and I have an object from my ViewModel called DatabasesSubFrame which is DataTemplated to show a ListBox. I want to display a Button below the ListBox, which binds to both the currently SelectedItem, and a property on the DatabasesSubFrame object which is being DataTemplated.
I know how to refer to the currently selected item, by setting the DataContext on a shared ancestor with the ListBox and use {Binding /}. In this example the shared ancestor is a StackPanel. And if the DataContext wasn't explicitly set there I could easily bind to a property on the DatabasesSubFrame object by just doing {Binding SomeProperty}. However, if I do {Binding SomeProperty} within the explicitly set DataContext, it refers to the wrong DataContext.
How do I access the "original" DataContext here? I tried messing with RelativeSources and TemplatedParents but couldn't figure out how to fit them in.
<DataTemplate DataType="{x:Type VM:DatabasesSubFrame}">
<StackPanel DataContext="{Binding Databases}" >
<ListBox Name="DbInfoBox"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding ShortName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Problem: The Command and V:CreateCommandBinding.Command are set incorrectly here. How do I access OpenDbCommand from the top-level DataTemplate's DataContext? -->
<Button Content="Open Database"
CommandParameter="{Binding /}"
Command="{Binding ???, Path=OpenDbCommand.Command}"
V:CreateCommandBinding.Command="{Binding ???, Path=DataContext.OpenDbCommand}"/>
</StackPanel>
</DataTemplate>
I think this question will help you to find the answer to yours. Another trick is to set the Name of the Window to something like "Root". You can then get at the window's original datacontext by using:
{Binding ElementName=Root, Path=DataContext.MyViewModelsProperty}

Categories