wpf pass datatemplate to new window - wpf

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.

Related

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.

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;

What is DataContext for?

As a continuation of the question Linking DataContext with another property in WPF.
At the very end of the research I was very surprised to find out that when one writes something like that:
<Label Content="{Binding Path=Name}" />
The DataContext against which the Content property is binded is of the Label control itself! The fact that it still works is due to the default inheritance of the DataContext value from the nearest parent.
But if you have this label wrapped in a custom control, and you don't want to bind your data to the DataContext property of that control, you would more likely love to have:
<Controls:SearchSettings Settings="{Binding Path=Settings}" />
And here you are. Now you need to set Settings as the DataContext for the SearchSettings control, for Label inside to bind against, but you can't, because that will trigger re-binding of Settings property.
I can't see the point in mixing binding properties using different sources: DataContext, by ElementName, etc.
So why would I ever use DataContext?
When you write
<Label name="myLabel" Content="{Binding Path=Name}" />
you are binding to myLabel.DataContext.Name, and not myLabel.Name.
The XAML in WPF is just a pretty user interface to display and interact with the actual data, otherwise known as the DataContext. The purpose of other binding sources (RelativeSource, ElementName, etc) is to point to another property that doesn't exist in the current control's DataContext
So suppose you have a Window. Without setting the DataContext, the window still displays but there is no data behind it.
Now suppose to set myWindow.DataContext = new ClassA();. Now the data that the window is displaying is ClassA. If ClassA has a property called Name, I could write a label and bind it to Name (such as your example), and whatever value is stored in ClassA.Name would get displayed.
Now, suppose ClassA has a property of ClassB and both classes have a property called Name. Here is a block of XAML which illustrates the purpose of the DataContext, and an example of how a control would refer to a property not in it's own DataContext
<Window x:Name="myWindow"> <!-- DataContext is set to ClassA -->
<StackPanel> <!-- DataContext is set to ClassA -->
<!-- DataContext is set to ClassA, so will display ClassA.Name -->
<Label Content="{Binding Name}" />
<!-- DataContext is still ClassA, however we are setting it to ClassA.ClassB -->
<StackPanel DataContext="{Binding ClassB}">
<!-- DataContext is set to ClassB, so will display ClassB.Name -->
<Label Content="{Binding Name}" />
<!-- DataContext is still ClassB, but we are binding to the Window's DataContext.Name which is ClassA.Name -->
<Label Content="{Binding ElementName=myWindow, Path=DataContext.Name}" />
</StackPanel>
</StackPanel>
</Window>
As you can see, the DataContext is based on whatever data is behind the UI object.
Update: I see this question so often from new WPF users that I expanded this answer into a post on my blog: What is this “DataContext” you speak of?
From CodeProject by kishore Gaddam:
DataContext is one of the most fundamental concepts in Data Binding. The Binding object needs to get its data from somewhere, and there are a few ways to specify the source of the data like using Source property directly in the Binding, inheriting a DataContext from the nearest element when traversing up in the tree, setting the ElementName and RelativeSource properties in the Binding object.
Detailed example on CodeProject: http://www.codeproject.com/Articles/321899/DataContext-in-WPF
In that particular case, you could do:
<Controls:SearchSettings DataContext="{Binding Path=Settings}" Settings="{Binding}" />
Assuming you want everything that may be content of the SearchSettings to use Settings as it's data context. Basically, the DataContext affects the element itself an any descendants that don't explicitly override it.
In most cases you do want to bind to the DataContext, in some templates on ItemsControls it is the only way to bind to the currently templated item for example. Further bindings to the DataContext are nice to write and read as they are concise.
In your example you can still set the DataContext, you only need to modify the binding on the Settings respectively:
<Controls:SearchSettings DataContext="{Binding Settings}" Settings="{Binding}"/>

Custom control's content can't bind to parent of the control

I have following XAML (simplified, no ending tags):
<Window Name="myWindow" DataContext="{Binding ElementName=myWindow}" >
<DockPanel>
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top">
<tb:ToolBar.Items>
<tb:ToolBarControl Priority="-3">
<tb:ToolBarControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Maps:</TextBlock>
<ComboBox ItemsSource="{Binding Generator.Maps, ElementName=myWindow}">
But the ComboBox's binding will fail with
Cannot find source for binding with
reference 'ElementName=myWindow'
Some facts about the custom controls:
tb:ToolBar is UserControl which contains actual ToolBar with ItemsSource bound to the Items property of the tb:ToolBar (of type inheriting IList).
The ToolBar's ToolBarItem DataTemplate is chosen from several DataTemplates (according to the type of the item).
The DataTemplate belonging to the tb:ToolBarControl is very simple - it just contains ContentPresenter bound to property Content of the tb:ToolBarControl.
tb:ToolBarControl is not for technical reasons UserControl, it is just DependencyObject with property Content of type object.
Why can't the ComboBox reference the Window?
Thanks for any help!
I had a similar problem here:
Bindings on child dependency object of usercontrol not working
DependencyObject doesn't have a DataContext and I think that's why the binding doesn't work. Instead of inheriting from 'DependencyObject' try inheriting from FrameworkElement.

Passing DataContext to User Control in WPF

I have a usercontrol that acts as a container for a ContentControl.
The user control container has a listview control that I want to use to update controls in a dynamically added user control assigned to the ContentControl.
IOW, as I scroll through the listview control, the textbox's in the UC assigned to the ContentControl should update.
I've done this when everything is in one page no problem, but am having a hard time passing the ListView as a datacontext to the dynamically added UC.
How can this be done?
In XAML
<ListView x:name="lstIncidents">
</Listview>
<ContentControl x:Name="PlaceHolder"></ContentControl>
In Codebehind...
PlaceHolder.Content = new LocationView();
When adding the "LocationView" to the PlaceHolder.Content, I need to pass "lstIncidents" as the datacontext so the textboxs in "LocationView" refresh as the ListView is navigated.
Controls inherit their DataContext from their parent, so try setting the DataContext on the ContentControl:
<ContentControl
x:Name="PlaceHolder"
DataContext="{Binding SelectedItem,ElementName=lstIncidents}" />

Resources