Element addressing within a DataTemplate from a ContentControl - wpf

How do I access an element contained in a DataTemplate that is displayed through a ContentControl. I have a ContentControl which hosts a PresentationModel along the lines of:
<ContentControl x:Name="ContentContainer"
Content="{Binding}"
ContentTemplate="{Binding ContentControlTemplate, ElementName=this}"
Where "this" is the view (UserControl).
There's a DataGridControl I want to EndEdit on, so I tried this:
ContentPresenter presenter = VisualTreeHelper.GetChild(this. ContentContainer, 0) as ContentPresenter;
DataGridControl dg = this. ContentContainer.ContentTemplate.FindName("datagrid", presenter) as DataGridControl;
dg.EndEdit();
Problem is that the ContentControl has no children, maybe because of the way the content is bound?
I appreciate any help.

Well, you are casting the result of GetChild to a ContentPresenter. Depending on its Template, this may not be the case. I think its default template includes a Border, so your cast will return null. If you don't need it to do anything other than display the content, why not use ContentPresenter directly?

Related

wpf pass datatemplate to new window

i need to send a DataTemplate to a new window for printing purposes.
1) I have create a general Window lets name it PrintPreview that holds the followings:
FlowDocument > BlockUiContainer > ContentControl (Responsible to display the DataTemplate that i will send to it)
The problem is that the bindings inside the datatemplate are not working. (not for all cases )
For example:
i have this datatemplate somewhere in my application
<DataTemplate x:Key="MyPrintPreview">
<DockPanel>
<TextBlock Text="{Binding SomeProperty1,RelativeSource={RelativeSource AncestorType=UserControl}}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource SomeViewModel},Path=SomeProperty2}"></TextBlock>
</DockPanel>
</DataTemplate>
The above DataTemplate is working very well and show both properties in my current View (UserControl)
but when i send this DataTemplate to the New Window PrintPreview i have the following issues
The 1st TextBlock (SomeProperty1) fails to display the content
The 2nd TextBlock (SomeProperty2) show just fine!
i don't know how to make this work. or if am doing it the wrong way?
You should set or bind the Content property of the ContentControl to an object that contains the properties that the elements in the ContentTemplate try to bind to.
So set the ContentTemplate property of the ContentControl to your DataTemplate and set the Content property to the actual object to bind to. That's how a ContentControl is supposed to be used.
Also note that for your first binding to work, the ContentControl must be a child of a UserControl because you are binding to SomeProperty1 of the parent UserControl. If there is no parent UserControl, the binding will always fail.

XAML property path for templates

I have the following hierarchy in my XAML:
Grid
Ruler
Ruler
ScrollViewer
ItemsControl
ItemsControl.ItemsPanel
ItemsPanelTemplate
DrawingCanvas
Here Ruler and DrawingCanvas are my UserControls. DrawingCanvas exposes a property named MousePosition that I want both Rulers to bind to. After reading about PropertyPath Syntax, I tried the following:
Chip="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Mode=OneWay, Path=Children[2].(ScrollViewer.Content).(ItemsControl.ItemsPanel).(local:DesignerCanvas.MousePosition).X}"
but this doesn't seem to work. What am I doing wrong?
Generally in these circumstances you would simply name the element you wish to bind to. It is much less brittle than finding your way through the tree:
Chip="{Binding MousePosition.X, ElementName=myCanvas}"
However, because your canvas is part of the ItemsControl template, the ElementName will not be visible to elements outside the ItemsControl.
In order to bind the two together, you need some intermediate property that both can see. One way would be to declare a DependencyProperty in your code-behind for the view. Both your Canvas and Rulers could then bind to that property using ElementName on the view root.
Another alternative is to hijack the Tag property that every element has, so in your case you could use ItemsControl.Tag as the intermediary:
Chip="{Binding Tag.X, ElementName=myItemsControl}"
<DrawingCanvas MousePosition={Binding Tag,
ElementName=myItemsControl,
mode=OneWayToSource} />
You are selecting element 3 in the Children collection which will map to the fourth element inside of the Grid. You are looking for Children[2].

Binding inside ContentControl not working

I'm building a graphical designer, based upon an article by Sukram in CodeProject. I'm now trying to extend it so that each item on the canvas binds to a different ViewModel object - i.e. I'm setting the DataContext for each item.
Every item on the designer is actually a ContentControl, into which is placed a different template (based upon which toolbox item was dragged onto the canvas). So I have a template containing a TextBox, and I have a ViewModel object containing a Name property, and I bind the Text property of the TextBox to the Name property of the ViewModel, and ... nothing. I've checked the visual tree with Snoop, and it confirms that the DataContext of the TextBox is the ViewModel object. Yet the TextBox remains empty. And if I modify the (empty) Text in the TextBox, the Name property in the ViewModel does not change. So it looks like the binding is not being applied (or has been removed somehow).
I've found a few posts which talk about the ContentControl messing around with the DataContext and Content properties, but I'm not sure how applicable they all are. The code sets the ContentControl.Content as follows:
newItem = new ContentControl();
ControlTemplate template = toolbox.GetTemplate();
UIElement element = template.LoadContent() as UIElement;
ViewModelItem viewModel = new ViewModelItem() { Name = "Bob" };
newItem.Content = element;
newItem.DataContext = viewModel;
and the XAML for the template is:
<ControlTemplate>
<Border BorderBrush="Black" BorderThickness="1" Width="100">
<TextBox Text={Binding Name}/>
</Border>
</ControlTemplate>
Snoop shows that the TextBox has a DataContext, and if I Delve that DataContext I can see that it has a Name property whose value is "Bob". So why does the TextBox remain empty? Snoop allows me to change that Name property, but the TextBox remains empty.
What am I doing wrong?
A few more details. I've set the VS2010 Debug DataBinding option for the OutputWindow to Verbose, which seems to show that the binding is all being attempted before I set the DataContext. Is it possible that the change to the DataContext is not being recognised?
I've just found this post DataTemplate.LoadContent does not preserve bindings - apparently DataTemplate.LoadContent does not preserve bindings. So it looks like I have to write my own version of LoadContent().
I've realised that the template has come through a XamlWriter, which apparently strips all bindings. This wouldn't be helping.
I've not been able to fix the DataTemplate.LoadContent(), but I realised that I didn't actually need a DataTemplate, since the XamlWriter / XamlReader was already instantiating the UI element that I was after. I found a fix to make the XamlWriter write all the bindings here, and after that it all works.
Thanks for your help.
Maybe you need to tell the binding in the ControlTemplate to look at the TemplatedParent, as is mentioned in this thread?
<TextBox Text="{Binding Path=Name, RelativeSource={RelativeSource TemplatedParent}}"/>
Either that, or try to use a DataTemplate instead.
I can't test this at the moment, so I might just be guessing here.
I would use a DataTemplate, as bde suggests.
You are trying to put some UI on your own data (ViewModel), and this is what Data-Templates are meant for (ControlTemplate is usually what you use if you want to change how e.g. a Button looks).
Change your code to use ContentControl.ContentTemplate with a DataTemplate:
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Width="100">
<TextBox Text={Binding Name}/>
</Border>
</DataTemplate>
Code-behind:
newItem = new ContentControl();
//NOTE: .GetTemplate() needs to return a DataTemplate, and not a ControlTemplate:
newItem.ContentTemplate = toolbox.GetTemplate();
ViewModelItem viewModel = new ViewModelItem() { Name = "Bob" };
newItem.Content = viewModel;
newItem.DataContext = viewModel;

Set WPF TabControl to show the same Content for every tab

I'd like to use a TabControl where each tab shows the same view just with different parameters. Therefore I do not want to create a new content control for each tab but reuse the same control for all tabs (binding some properties of it to the SelectedItem property of the TabControl)
I tried to my contained control as resource and set the Content property of the tab items to it, but this resulted in an exception, because the same element cannot appear as content in to different parents.
Any ideas?
<TabControl>
<TabControl.ContentTemplate>
<DataTemplate>
your view
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I found a solution in this question
TabControl's TabItems sharing same content... Do Not Want
(even if the poster wanted specifically the opposite behaviour :) ...)

Content generated from ContentTemplate does not have DataContext of Silverlight ContentControl set

In my Silverlight 4 application, I have a ContentControl with its ContentTemplate property bound to a property in the data context. That works fine. However, the content of the template once rendered has its DataContext set to null. I would like the content to inherit the same DataContext as set for the ContentControl. Is there a way to get this to happen?
The ContentControl's template has the ContentControl's Content property as a DataContext. So try
<ContentControl Content="{Binding}" />
if this is merely the current DataContext.
I found an alternate way to accomplish what was required. In my case, the template (not the content template) of the ContentControl was unimportant, so I made my DataTemplate objects into ControlTemplate objects instead and bound the Template property of the ContentControl instead of ContentTemplate. The data context was preserved if I did it this way.

Resources