If I have the following XAML:
<toolkit:DataForm Height="100" x:Name="form">
<toolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel Name="stack"></StackPanel>
</DataTemplate>
</toolkit:DataForm.EditTemplate>
</toolkit:DataForm>
I can get a reference to "form" by this.FindName("form") from the View.
How can I get a reference to "stack"? FindName returns null.
The problem here is that the xaml content of a DataTemplate belongs to a different NameScope than the outer Xaml. Calling FindName on an element searches only the NameScope in which the element was originally generated. Hence calling FindName on the UserControl will not find elements generated by a DataTemplate. The reason for this is that DataTemplate (and other templates) are designed to be reused multiple times, the use of NameScope disambiguates the names.
The solution is to invoke the help of the VisualTreeHelper, have a look at the code in this blog for my take on VisualTreeHelper. With the VisualTreeEnumeration class from that blog added to your project you can do this:-
var element = this.Descendents().OfType<FrameworkElement>().FirstOrDefault(fe => fe.Name == "stack");
Of course if you know that "stack" is a StackPanel then you can get more specific with .OfType<T>.
Related
I need to wrap a datatemplate in a datatemplate that gets built at run time. The wrapped datatemplate is WPF element where as the wrapping template needs to be created in code.
Something like:
public DataTemplate GetTemplate(DataTemplate template)
{
string xaml = string.Format(#"
<DataTemplate>
<ContentControl Content=""{{Binding}}"">
<ContentControl.ContentTemplate>
{0}
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>", template);
return CreateTemplate(xaml);
}
Obviously my datatemplate is more complicated then the one I'm using above.
I dont know of anyway to take an existing xaml element and convert it to a string. It seems like I might be able to use FrameworkElementFactory but I see it is depricated, which leads me to think I'm missing something obvious.
EDITED ---
What I'm doing is creating a control that users will supply a datatemplate but I need to make changes to the the template. Maybe this example will make more sense...
public DataTemplate GetTemplate2()
{
// this template would be supplied by the user
// I'm creating it here as an example
string t = string.Format(#"
<DataTemplate>
<TextBlock Text=""{{Binding Value}}""/>
</DataTemplate>");
T = CreateTemplate(t);
string xaml = string.Format(#"
<DataTemplate>
<ContentControl Content=""{{Binding}}"">
<ContentControl.ContentTemplate>
{0}
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>", t);
return CreateTemplate(xaml);
}
This all works because I'm using the string template (e.g. t). However I need to figure out some way to do it with the actual DataTemplate (e.g. T). Unfortunately XamlWriter can't deal with the Binding.
You can create a DataTemplate selector. There you can add your logic to build your DataTemplate at runtime. Also you can create a dependencyProperty in your DataTemplate selector. Then bind it in your xaml to a DataTemplate stored in some backing model, and there do what ever ...
This link might be a good place to start
You can use XamlWriter (the analog to XamlReader) but it has limitations on what can be properly serialized. Things like event handlers and x:Names cause issues.
**UPDATE
Seeing the additional detail I think you should try reversing your approach. Rather than combining the templates using strings and then trying to turn that into the object you want, you can avoid all the weird parsing restrictions by just creating the user's template as a DataTemplate object and then building your own DataTemplate object around it. Your example code is also using 2 Value Paths, which is going to give you .Value.Value on the inner template Text so check to make sure on your real one that you're ending up with the Paths you want. Here's the basics of your example using the objects instead, with the paths updated to expect a String and display its length:
DataTemplate T = XamlReader.Parse(string.Format(#"
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
<TextBlock Text=""{{Binding}}""/>
</DataTemplate>")) as DataTemplate;
FrameworkElementFactory controlFactory = new FrameworkElementFactory(typeof(ContentControl));
controlFactory.SetBinding(ContentControl.ContentProperty, new Binding("Length"));
controlFactory.SetValue(ContentControl.ContentTemplateProperty, T);
DataTemplate mainTemplate = new DataTemplate { VisualTree = controlFactory };
I have a an object created in Xaml:
<Grid>
<MyObject/>
</Grid>
I need someway to bind the object myObject back to a property in my view model. I dont know whether this is possible, everything ive seen so far binds properties together, but any help would be greatly appreciated.
I am assuming what you want is your ViewModel to hold the actual visual control MyObject in it and your Grid to display it via MVVM.
This is possible through ContentControl in WPF.
Assuming your ViewModel has a property MyObjectView which holds MyObject...
<Grid>
<ContentControl Content="{Binding MyObjectView}" />
</Grid>
Having said that you must take caution that same MyObjectView is not bound to any other content control as that will result in an error
"Specified element is already the logical child of another element.
Disconnect it first"
And if that requirement is possible then you must excercise ContentTemplate option.
Let me know if this helps.
It is possible. It kinda breaks mvvm though.
You can attach an InvokeCommandAction to this object, and bind the CommandParameter to it via ElementBinding. Then in the callback of the command which you defined in the viewmodel, you will have a reference to this object from the CommandParameter.
In the above image, child is a ContentPresenter. Its Content is a ViewModel. However, its ContentTemplate is null.
In my XAML, I have a TabControl with the following structure:
<local:SuperTabControlEx DataContext="{Binding WorkSpaceListViewModel}"
x:Name="superTabControl1" CloseButtonVisibility="Visible" TabStyle="OneNote2007" ClipToBounds="False" ContentInnerBorderBrush="Red" FontSize="24" >
<local:SuperTabControlEx.ItemsSource>
<Binding Path="WorkSpaceViewModels" />
</local:SuperTabControlEx.ItemsSource>
<TabControl.Template>
<ControlTemplate
TargetType="TabControl">
<DockPanel>
<TabPanel
DockPanel.Dock="Top"
IsItemsHost="True" />
<Grid
DockPanel.Dock="Bottom"
x:Name="PART_ItemsHolder" />
</DockPanel>
<!-- no content presenter -->
</ControlTemplate>
</TabControl.Template>
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:WorkSpaceViewModel}">
....
WorkSpaceViewModels is an ObservableCollection of WorkSpaceViewModel. This code uses the code and technique from Keeping the WPF Tab Control from destroying its children.
The correct DataTemplate - shown above in the TabControl.Resource - appears to be rendering my ViewModel for two Tabs.
However, my basic question is, how is my view getting hooked up to my WorkSpaceViewModel, yet, the ContentTemplate on the ContentPresenter is null? My requirement is to access a visual component from the ViewModel because a setting for the view is becoming unbound from its property in the ViewModel upon certain user actions, and I need to rebind it.
The DataTemplate is "implicitly" defined. The ContentPresenter will first use it's ContentTemplate/Selector, if any is defined. If not, then it will search for a DataTemplate resource without an explicit x:Key and whose DataType matches the type of it's Content.
This is discussed here and here.
The View Model shouldn't really know about it's associated View. It sounds like there is something wrong with your Bindings, as in general you should not have to "rebind" them. Either way, an attached behavior would be a good way to accomplish that.
I think the full answer to this question entails DrWPF's full series ItemsControl: A to Z. However, I believe the gist lies in where the visual elements get stored when a DataTemplate is "inflated" to display the data item it has been linked to by the framework.
In the section Introduction to Control Templates of "ItemsControl: 'L' is for Lookless", DrWPF explains that "We’ve already learned that a DataTemplate is used to declare the visual representation of a data item that appears within an application’s logical tree. In ‘P’ is for Panel, we learned that an ItemsPanelTemplate is used to declare the items host used within an ItemsControl."
For my issue, I still have not successfully navigated the visual tree in order to get a reference to my splitter item. This is my best attempt so far:
// w1 is a Window
SuperTabControlEx stc = w1.FindName("superTabControl1") as SuperTabControlEx;
//SuperTabItem sti = (SuperTabItem)(stc.ItemContainerGenerator.ContainerFromItem(stc.Items.CurrentItem));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(stc);
//ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(sti);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
The above code is an attempt to implement the techniques shown on the msdn web site. However, when I apply it to my code, everything looks good, except myDataTemplate comes back null. As you can see, I attempted the same technique on SuperTabControlEx and SuperTabItem, derived from TabControl and TabItem, respectively. As described in my original post, and evident in the XAML snippet, the SuperTabControlEx also implements code from Keeping the WPF Tab Control from destroying its children.
At this point, perhaps more than anything else, I think this is an exercise in navigating the Visual Tree. I am going to modify the title of the question to reflect my new conceptions of the issue.
We see some properties in TextBlock or Grid like this:
<TextBlock x:Name="TextBlock1" ...
Why do we include this (x)? why don't we just say:
<TextBlock Name="TextBlock1" ...
I mean, we're already within the definition scope of this TextBlock, right?
There must be a reason for that.
Thanks in advance.
As an extension to Gabe's answer, x:Name is an attached property. Attached properties are different from standard properties, as they aren't defined (usually) on the control that uses them. For example, the TextBlock control does not have an x:Name property - instead, this property is defined elsewhere (in the XAML namespace), and is being "attached" to the TextBlock control to implement it's behaviour. It's saying "I want to use the Name attached property that can be found in the XAML namespace). Of course, to complicate things, the TextBlock control has a Name property (it didn't used to in Silverlight 2, thus you needed to use the x:Name attached property instead). They do the same thing though.
Another (easier to understand) example of an attached property is Grid.Row. You can use this property on the TextBlock control to specify what row the control should appear in a Grid, even though it's not defined on that control (the Grid control defines it). The TextBlock is simply attaching that property to itself, which associates itself with that behaviour. It's a confusing concept initially, but very powerful and useful. More info on attached properties can be found here: http://msdn.microsoft.com/en-us/library/cc265152(VS.95).aspx.
Hope this helps...
Chris
That is a namespace prefix.
Example 1:
You should see something like this on the xaml page:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Which declares the x prefix referring to the xaml namespace.
Example 2:
You could load your own user controls by registering the namespace and giving it a prefix.
xmlns:mycontrols="clr-namespace:MyControls.Namespace;assembly=MyAssembly"
Then here we are using the prefix to utilize one of the controls from this namespace.
<mycontrols:MyControl />
I speak about josh smith article.
can anyone show me please how the CustomerView.xaml specifically this:j
<TextBox
x:Name="firstNameTxt"
Grid.Row="2" Grid.Column="2"
Text="{Binding Path=FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{x:Null}"
/>
Why is there a Binding to FirstName which is public property in the CustomerViewModel.
There is a datacontext set for the MainViewModel, but not for the CustomerViewModel, so why does the binding work ???
Check out the ResourceDictionary in MainWindowResources.xaml. Josh uses the following code to describe what View should be used if an instance of CustomerViewModel is shown in the main window:
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
We've described that when our DataType is of Type CustomerViewModel, we'll create a new instance of the CustomerView. WPF takes care of the DataContext and creation when it sees the CustomerViewModel type.
From the rest of the article:
Applying a View to a ViewModel
MainWindowViewModel indirectly adds
and removes Workspace ViewModel
objects to and from the main window's
Tab Control. By relying on data
binding, the Content property of a
TabItem receives a
ViewModelBase-derived object to
display. ViewModelBase is not a UI
element, so it has no inherent support
for rendering itself. By default, in
WPF a non-visual object is rendered by
displaying the results of a call to
its ToString method in a TextBlock.
That clearly is not what you need,
unless your users have a burning
desire to see the type name of our
ViewModel classes! You can easily tell
WPF how to render a ViewModel object
by using typed DataTemplates. A typed
DataTemplate does not have an x:Key
value assigned to it, but it does have
its DataType property set to an
instance of the Type class. If WPF
tries to render one of your ViewModel
objects, it will check to see if the
resource system has a typed
DataTemplate in scope whose DataType
is the same as (or a base class of)
the type of your ViewModel object. If
it finds one, it uses that template to
render the ViewModel object referenced
by the tab item's Content property.
The MainWindowResources.xaml file has
a Resource Dictionary. That dictionary
is added to the main window's resource
hierarchy, which means that the
resources it contains are in the
window's resource scope. When a tab
item's content is set to a ViewModel
object, a typed DataTemplate from this
dictionary supplies a view (that is, a
user control) to render it, as shown
in Figure 10.
The DataContext for the MainViewModel in App.xaml.cs serves as a starting point for our application.