Data Binding Scopes - In Need of clarification - c#

I've been doing some work in WPF, and I for the most part have my bindings working, but I need a scope clarification here. I've run into some seemingly simple operations that require silly binding workarounds and I believe a lot of it has to do with scope.
Example #1 - Out of visual tree, binding to parent.
<ComboBox x:Name="Combo1" ItemsSource="{Binding SomeListOfStrings}">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding ElementName=Combo1, Path=SelectedItem}" />
<MenuItem Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=SelectedItem}" />
</ContextMenu>
</ComboBox.ContextMenu>
</ComboBox>
In this example, I'm trying to bind a child-element's property to a parent's property. Since this item isn't in the visual tree under the element, but instead as just a property, I cannot locate the parent using FindAncestor. In my experience I've had no luck binding with ElementName in this case either (Tried both Name="" and x:Name="").
What is the scope here? How does the MenuItem relate to the ComboBox? Because I know it inherits the DataContext of it's parent here, why is it unreachable with FindAncestor / ElementName?
Example #2 - Resources + StaticResource/DynamicResource
<UserControl x:Name="MainControl" ... />
<UserControl.Resources>
<Style TargetType="ComboBox">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding ViewModelsMenuItems}" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBox" x:Key="Example2_Style2">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu ItemsSource="{Binding ElementName=MainControl, Path=DataContext.ViewModelMenuItems}" />
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<StackPanel>
<ComboBox />
<ComboBox />
<ComboBox Style="{StaticResource Example2_Style2" />
</StackPanel>
</UserControl>
In this example, I'm trying to set the context menu for all ComboBox's in my user control (or specific ones if I used a named style). Since the ContextMenu is defined outside of scope, and "set" into scope, I've experienced issues before with the DataContext being inherited, Or being able to use ElementName (Because the item is out of scope?).
Bonus Question
Since I've had terrible luck alltogether with ElementName can someone please tell me which to use, because I see both ALL OVER the internet/books. Name="Whatever" or x:Name="Whatever"
Update (As per request)
The type of binding failures I am getting are:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ComboBox', AncestorLevel='1''. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Header' (type 'object')

There is something called inheritance context on which there is not all too much information to be found except this blog post (archive).
In that article the ContextMenu is given as an example where no linking occurs.
I for one would solve this issue using the PlacementTarget property:
<ContextMenu>
<MenuItem Header="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />
</ContextMenu>
Original article has dissapeared; here are the main points from an archive:
Nick on Silverlight and WPF
What’s an inheritance context?
Nick Kramer [MSFT] August 17, 2006
But before I tell you about inheritance contexts, I have to explain
the problem it solves. Once upon a time, property inheritance looked
only at the logical tree and the visual tree -- so if an element
didn't have a logical or visual parent, its properties didn't inherit
values from the parent it didn't have. Which makes sense if you think
about the world in terms of code. But if you look at the world
through xaml glasses, there's a lot of places where one element looks
like it has a parent when it really doesn't. Consider the following:
<Button>
<Button.Background>
<SolidColorBrush>green</SolidColorBrush>
</Button.Background>
</Button>
Quick, what's the parent of SolidColorBrush? If you said Button, you
would be wrong -- SolidColorBrush is not part of the visual tree (it's
not a Visual). Nor is SolidColorBrush part of the logical tree,
because if you call Button.Content the answer is not the
SolidColorBrush. So SolidColorBrush in this example has no parent, so
it didn't inherit property values from anyone.
At first this may seem academic -- who cares if SolidColorBrush
inherits? Actually, there's a couple reasons it matters, the
DataContext property and the Loaded event. DataContext is an
inherited property that use the default data source for your {Binding}
statements. When you write:
<SolidColorBrush Color="{Binding}"/>
Since you didn't specify a data source (and who does), it uses the
DataContext property. And if that inherits the way you expect,
everything is happy. But it's easy to write:
<Button DataContext="whatever">
<Button.Background>
<SolidColorBrush Color="{Binding}"/>
</Button.Background>
</Button>
And be confused that your SolidColorBrush didn't inherit the
DataContext. Similarly, the Loaded event was originally tied to the
logical tree, so if you put your MediaElement inside a VisualBrush,
there would be a gap in the visual tree and your media would never get
a Loaded event and never start play the video.
And that's why we invented inheritance context. You can think of it
as logical tree 2.0 -- inheritance context is an extra pointer, which
the property engine uses when there's no logical parent or visual
parent to get values from. Inheritance context don't solve every
problem in this space, but they solve a lot of them, and in the future
we'll add more inheritance context pointers and solve more problems.
There's a number of places we establish inheritance context pointers,
I won't try to list all of them but here are some of the more
interesting ones:
Freezable inside a FrameworkElement -- our SolidColorBrush/Button
sample above FrameworkElement inside a VisualBrush Triggers and
setters
Resource dictionaries present another interesting case. Suppose you
use DynamicResource inside a resource dictionary:
Does that dynamic resource get evaluated where the SolidColorBrush was
defined? Or where the brush gets used? If the latter, what happens
if you use the SolidColorBrush in two different places where the
DynamicResource will give two different answers? It may sound
contrived:
<Window.Resources>
`<Color x:Key="color">red</Color>`
`<SolidColorBrush x:Key="brush" Color="{DynamicResource color}" /> `
</Window.Resources>
<Button>
<Button.Background>
<StaticResource ResourceKey="brush"/>
</Button.Background>
</Button>
<Button>
<Button.Resources>
<Color x:Key="color">blue</Color>
</Button.Resources>
<Button.Background>
<StaticResource ResourceKey="brush"/>
</Button.Background>
</Button>
But it actually happens in real code. We chose the first solution,
the inheritance context for SolidColorBrush points to the resource
dictionary, and not its point of use.
Inheritance context has been wonderfully useful, but we haven't put
inheritance context links everywhere that's theoretically possible,
mostly because of time in the day (adding inheritance context links
are very difficult to implement performantly and without causing
undesired behavior changes). Probably the simplest example of where
we don't have inheritance context links is across random property
elements:
<Button>
<Button.ContextMenu>
<ContextMenu/>
</Button.ContextMenu>
</Button>
ContextMenu is neither a visual nor logical child of Button, nor is it
one of the inheritance context cases listed above (ContextMenu is not
a Freezable). But, with inheritance context concept at our disposal,
we hope to solve that in future versions of WPF.

Related

Can I always replace DynamicResource with a Binding to a Source=StaticResource

What is the difference between A and B both will work.
Can I always replace inplace DynamicResource with a Binding to a StaticResource?
<Button Content="{DynamicResource content}">
<Button.Resources>
<sys:String x:Key="content">A</sys:String>
</Button.Resources>
</Button>
<Button Content="{Binding Source={StaticResource content}}">
<Button.Resources>
<sys:String x:Key="content">B</sys:String>
</Button.Resources>
</Button>
As this doesn't work (because it is defined after it is used):
<Button Content="{StaticResource content}">
<Button.Resources>
<sys:String x:Key="content">C</sys:String>
</Button.Resources>
</Button>
The primary difference between StaticResource and DynamicResource is that a StaticResource only provides a value once, when the Xaml (or Baml) is parsed and applied. Despite what another answer says, a StaticResource is not resolved at compile time.
A DynamicResource provides an expression value, where the expression is a 'live' resource reference that can respond to changes in the future.
For a StaticResource to be applied, the requested resource must be in scope when the markup extension is asked to provide a value. If it's not in scope, the resource lookup will fail. This is not true for a DynamicResource: if the requested resource is not in scope, no value will be applied. If the resource comes into scope later on, it will get picked up. Similarly, if the referenced resource gets removed or replaced, the target property will be updated accordingly.
Because DynamicResource provides an expression for a value, it can only be applied to a dependency property. A StaticResource can be applied to any property that is capable of receiving a markup extension.
With that in mind, let's revisit your question:
Can I always replace [an] in-place DynamicResource with a Binding to a StaticResource?
You can replace a DynamicResource with a StaticResource if and only if:
The resource will be in scope when the Xaml is loaded.
The resource will not be replaced at runtime.
If the above conditions are known to be true, then it's better to use a StaticResource, because static resources are more lightweight than dynamic resources. Using a dynamic resource necessarily requires some additional overhead because change listeners need to be hooked up.
However, it makes little sense to replace {DynamicResource content} with {Binding Source={StaticResource content}}. If all you want to do is insert a resource reference, there is no point in wrapping it a Binding. Just use {StaticResource content}.
There are times when you might want to use a StaticResource for a binding's Source, but it only makes sense if you're drilling down to a value inside the resource, or if you need to apply a converter.
As this doesn't work (because it is defined after it is used):
<Button Content="{StaticResource content}">
<Button.Resources>
<sys:String x:Key="content">C</sys:String>
</Button.Resources>
</Button>
Right. To use a StaticResource directly, you would need to move the resource to an outer scope or set Content after the Resources (using the <Button.Content> element syntax).
Now, about this guy:
<Button Content="{Binding Source={StaticResource content}}">
<Button.Resources>
<sys:String x:Key="content">B</sys:String>
</Button.Resources>
</Button>
Are you sure this works? For me, it works in the designer, but if I actually run the application, it fails just like it does when using StaticResource directly.
DynamicResource is resolved during runtime
while StaticResource is resolved during compile time and placed in the BAML.
If they work then your ResourceDictionaries are resolved correctly.
They both work that's right but they "work" in 2 different ways.
Can I always replace DynamicResource with a Binding to a StaticResource?
You can. Primarily one would use DynamicResource when loading time of the Window or UserControl takes a long time.
EDIT
After your edit I can tell you that the resource that you are trying to use is defined after your attempt to use it. If you move that resource to the top of the Visual Tree i.e. Window or UserControl then it will be available to you. This is because the compiler is trying to resolve the name content which at this point doesn't exists yet. If you would use a DynamicResource which would be resolved during runtime rather than compile time, then you would see the difference.
EDIT 2
As EdPlunket pointed out the StaticResource if changed will not be reflected on the screen, however if you change the DynamicResource then it will be updated.
EDIT 3
I should have probably made this more clear that DynamicResource StaticResource are Markup Extensions that resolve Resources used within the app. Also worth mention that x:Static and x:Type are Markup Extensions` as well.

Context menu binding never gets executed

I have ListView control in my view with it's own viewmodel A. I have made a seperate UserControl to use as ListViewItem, because it's styling takes a lot of space. In this ListViewItem I have a button, which is binded to viewmodel A and it works fine.
As the context menu has it's own visual tree and cannot bind via ancestor, I have used binding proxy, to solve this issue. I have tweaked it a little so it worked for my particular case, because if it just used {Binding} it would bind to item's model, not listview's viewmodel.
<helpers:BindingProxy x:Key="proxy" Data="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
To check if the binding is correct I've used a converter as a way just to have a breakpoint to check source. Everything was good and I was getting my viewmodel right there.
Now, when I try to bind to this in my context menu
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Open"
Command="{Binding DataContext.OpenChatCommand, Source={StaticResource proxy}, Converter={StaticResource DataBindingDebugConverter}}"
CommandParameter="{Binding}"/>
</ContextMenu>
</UserControl.ContextMenu>
The command never gets called. I added converter to see if something is wrong, but it turns out, I never get to my converter, which in turn means this code never gets executed.
Anyone with any ideas why this is happening and how to solve this is welcome.
I think it's the compiler malfunctioning though
I just did a brief readup on that "binding proxy" you mentioned, but as far as I know, DataGridTextColumn is in the same Visual Tree as its DataGrid, just that its DataContext is bound to its data.
For ContextMenu, it's totally different. This one really has a separate tree from its parent. There is no point in using a proxy object in resources, because it is from a different visual tree. When you use StaticResource, WPF will search upwards through its visual tree, level by level, inside those elements' Resource property (which is a ResourceDictionary).
One way is to make that proxy into a singleton, and use Source={x:Static helpers:BindingProxy.Instance}. Of course using this means that your proxy can only be used by a single View, or else something unexpected would happen.
The other way is to make use of PlacementTarget property of the ContextMenu.
<ContextMenu DataContext="{Binding Path=PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
This is the preferred way, but you need to make sure the parent's DataContext is really the VM that you need.
Edit
There is no super elegant way to do it the MVVM way. The best way is probably through the use of Tag property.
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag,
RelativeSource={RelativeSource Self}}">
ListView Control:
<MyControl:MyListViewItem .... Tag="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type MyControl:MyListViewView}}}"}" ...>

Binding works fine, but intellisense says: Cannot resolve property XXX in data context of type 'object'

I have a binding to a parent-element. How can I provide the data type for the DataContext in the binding, so intellisense can resolve the bound Properties?
The binding works fine at runtime. So, I have the following XAML structure:
<TabControl Name="TabDynamic"
ItemsSource="{Binding TabItems, Mode=OneWay}" ...>
<TabControl.Resources>
<DataTemplate x:Key="TabHeader" DataType="TabItem">
<DockPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}" />
<Button Command="{Binding ElementName=TabDynamic, Path=DataContext.DeleteTabCommand}"
CommandParameter="{Binding ElementName=TabDynamic, Path=DataContext.TabItems/}">
<Image Source="{DynamicResource DeleteImg}" Height="11" Width="11"></Image>
</Button>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="TabContent" DataType="viewModels:ConnectionInfoVM">
<views:BufferViewerControl/>
</DataTemplate>
</TabControl.Resources>
</TabControl>
The data type of the DataContext is "viewModels:ConnectionInfoVM".
Intellisense will now underline both Properties on the DataContext (so DeleteTabCommand and TabItems are underlined).
I already tried to use the design-time data-context definition "d:DataContext" within the Button element like so:
d:DataContext="{d:DesignInstance viewModels:ConnectionInfosVM}"
But this does not change the intellisense warnings.
I also tried to define the DataType on the DataTemplate to be "viewModels:ConnectionInfosVM", as I do for the content-template, but that too does not change the intellisense warnings (and I guess would be wrong, as the data type of the element really is a TabItem).
Another try was to define the DataContext by adding the following to the Button element definition:
<Button.DataContext>
<viewModels:ConnectionInfosVM/>
</Button.DataContext>
But this too, does not get rid of the warnings.
I found a solution at least for the above described problem. I was setting the DataContext of the window in question in the code-behind. The TabControl in my example just inherits that DataContext, which is a ConnectionInfosVM.
In order to fix those warnings (and of course for the gained flexibility in providing a DataContext by a Locator), I defined the DataContext in XAML like so:
<UserControl ...
DataContext="{Binding Source={StaticResource mainViewModelLocator}, Path=ConnectionInfosVM}">
It seems that intellisense is now able to resolve those references. But on the other side, I also changed the TextBlock contained within the DataTemplate to the following:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=DataContext.Address}" />
Now the DataContext of such a TabItem is actually a different type, as it is the type of the backing object for the TabItem. So in this case, the Property Address cannot be found by intellisense.
So, the question kind of remains, is there a possibility to define the type of the DataContext within a binding defined in a DataTemplate?
I needed to add the mc:Ignorable="d" attribute to the Window tag. Essentially I learned something new. The d: namespace prefix that Expression Blend/Visual Studio designer acknowledges is actually ignored/"commented out" by the real compiler/xaml parser!
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
The following was taken from
Nathan, Adam (2010-06-04). WPF 4 Unleashed (Kindle Locations 1799-1811). Sams. Kindle Edition.
Markup Compatibility
The markup compatibility XML namespace (http://schemas.openxmlformats.org/markup-compatibility/2006, typically used with an mc prefix) contains an Ignorable attribute that instructs XAML processors to ignore all elements/attributes in specified namespaces if they can’t be resolved to their .NET types/members. (The namespace also has a ProcessContent attribute that overrides Ignorable for specific types inside the ignored namespaces.)
Expression Blend takes advantage of this feature to do things like add design-time properties to XAML content that can be ignored at runtime.
mc:Ignorable can be given a space-delimited list of namespaces, and mc:ProcessContent can be given a space-delimited list of elements. When XamlXmlReader encounters ignorable content that can’t be resolved, it doesn’t report any nodes for it. If the ignorable content can be resolved, it will be reported normally. So consumers don’t need to do anything special to handle markup compatibility correctly.

WPF Template Binding with more than 1 content

So, I have this Window. On it, I'm creating a list of TextBlocks and TextBoxes in pairs. When you click on either, they will put a number in the corresponding TextBox, and set some values in the background. This all works well now.
I have the following XAML to create a custom Checkbox (as it has the behavior I'd like to use for this). My problem is that I want to bind different content into both the TextBlock and TextBox. For the TextBlock, I bound to the Content property, but I can't find a suitable option to satisfy the second binding. I considered placing it in the tag, but this didn't feel right, and in any case, I'm already binding an index value I require into there.
<Style x:Key="CustomCHK" TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<DockPanel LastChildFill="True">
<TextBox DockPanel.Dock="Right" Width="50" Height="30" />
<TextBlock Text="{TemplateBinding Content}" TextWrapping="Wrap" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Feels like there should a simple solution to this, but I'm just trying to decide what's best. Do I create a custom checkbox class and just add a couple properties?
As always, I appreciate any direction you can offer me.
Unfortunately, there is no straightforward way to do this. I can see just two (somewhat flawed) workarounds:
Subclass CheckBox to suit your needs, and add the additional content properties that you need. The advantage is that this will fully enable your IDE's programming help for setting the content properties, and those properties will be typesafe. The downside is that your will need to add C# code for the sole purpose of declaring the additional content properties (i.e. without adding any "behavioral logic"), which somehow seems to conflict with a "clean" XAML-only for presentation approach.
You could try passing an array to the Content property, and then place several ContentPresenter instances in your control template, each of which bind to another item in the array. Binding property paths should support indexed access, though your code may become a bit verbose, as arrays in XAML have to be written explicitly by using the x:Array markup extension.

Is possible to make a WPF RelativeSource binding with AncestorType pointing to a base type?

I want to bind a property to the parent container view having a ViewModel in its DataContext.
This code works perfectly well when the parent is a direct instance of ConcreteClassView:
Property="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:ConcreteClassView}}, Path=DataContext.Name}"
However, the parent is not found when trying to locate it via a base class or a interface.
Sample:
PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:BaseClassView}}, Path=DataContext.Name}"
PropertyB="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ty:INamedElementView}}, Path=DataContext.Name}"
Giving that:
class ConcreteClassView : BaseClassView, INamedElementView { }
Ok, Let's assume that FindAncestor, AncestorType needs the concrete type to work.
But there is any workaround to locate ancestors just based on base classes or implementing a given Interface?
Thxs.
FindAncestor, AncestorType do work with base classes, so your assumption is wrong.
Here is proof: This works
<HeaderedContentControl Tag="ABC">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</HeaderedContentControl>
It works also with interface (Button implements ICommandSource):
<Button Tag="ABC">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ICommandSource}}" />
</Button>
(Tested in .NET 4.5)
So why your code does not work?
There may be another element derived from ty:BaseClassView in your visual tree between the binding target and the element you are looking for.
This doesn't work:
<HeaderedContentControl Tag="ABC">
<Label>
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
</Label>
</HeaderedContentControl>
Label is also inherited from ContentControl, so Binding Source is Label in this case
Visual Tree may be disconnected. For example Popup control is part of Logical Tree, but it has its own visual tree, so you can't use RelativeSource FindAncestor inside popup to look for parents outside popup. Please note, that elements are removed from visual tree also when you set Visibility="Collapsed"
How to debug?
You can use converter to debug your binding. Just specify RelativeSource and some fake converter and leave the path empty. You can then place breakpoint to your converter, where value is be your binding source.
Use loaded event of your element with binding to write all visual parents to Debug window
EDIT: Now in Visual Studio 2015, you can use Live Visual Tree explorer, to inspect the visual tree at runtime, (similarly like Browsers' developers tools can inspect dom elements). Using this tool you should be able to find bug in your application within few seconds.
https://msdn.microsoft.com/en-us/library/mt270227.aspx

Categories

Resources