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}}}"}" ...>
Related
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
I have a MainWindowView(Window) with a Canvas in which I add my Views(UserControls).
The Canvas in the MainWindow is a Custom Canvas derived from Canvas so that Views inside this can be moved here and there, and can bringtofront or sendback.
I add Views to MainWindowView's Canvas by Binding a Command to a Button. So when I click a Button, a View gets added in the Canvas.
But, my problem is, I want to add another View to the same Canvas of MainViewModel from the ViewModel of my Views which are already in the Canvas of MainViewModel.
Since the ObservableCollection, which I used to bind Canvas, is in MainViewModel, I can add View from the MainViewModel only.
When I try to use the MainViewModel from other ViewModel, I have to create a new object of it, which makes the old View in the Canvas being replaced by the new one.
Is there a solution for this. If not what's the use of using MVVM framework.
Please help...
Use Calibrum Micro, which will help you in this
Am I getting this right : Your controls' DataContext is a Different one than that of the Window and you need to access it from there?
Basically that could have been avoided by design (use Dependency Injection to get the MainViewModel instance into the Command), but in fact there is a WPF/MVVM friendly way of solving this:
Use Commands to Add Controls to the MainViewModels ObservableCollection
<Button Command="{Binding Path=CreateViewCommand}" CommandParameter="{Binding}" />
From your Control (what you called View), you must use Ancestor Binding:
<Button Command="{Binding Path=DataContext.CreateViewCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
In your Command, you can cast the parameter to its original Type (MainViewModel) and work with it as you wish.
I can't figure out where I'm going wrong here. Hopefully one of you can help.
I have a Window that contains a TabControl. The TabControl has a ContextMenu with a checkable item for "always-on-top" behavior. I want to bind this checkable item to the containing Window's boolean Topmost property.
No matter what I do, the binding fails and I get a "Cannot find source for binding" error in my debug output.
(These excerpts are greatly simplified from my actual code. I hope I didn't accidentally cut out anything relevant.)
First I tried this:
<Window x:Name="myWindow" (blah blah other properties)>
<TabControl x:Name="tabControl">
<TabControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Always on Top" IsCheckable="True"
IsChecked="{Binding ElementName=myWindow,
Path=Topmost,
Mode=TwoWay}"/>
</ContextMenu>
...
That didn't work. Is it because the MenuItem is inside of the Window "myWindow"? Do I need to use a RelativeSource Ancestor binding?
So I tried this:
<Window x:Name="myWindow" (blah blah other properties)>
<TabControl x:Name="tabControl">
<TabControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Always on Top" IsCheckable="True"
IsChecked="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType={x:Type Window}
},
Path=Topmost,
Mode=TwoWay}"/>
</ContextMenu>
...
That didn't work either.
So now I'm stuck. How do I make this binding work?
Note: My code-behind is not doing anything with these elements. Do I need to set Window.DataContext or something? That could possibly break other parts of this window.
Yes, whatever object your "TopMost" property is on will have to be set as the DataContext of your window. If its set as the DataContext of your Window then your control should be able to pickup the property from the ElementName binding you tried in your first example.
Your view will look at its DataContext for a property named "TopMost"
As far as I remember Menu is drawn in a popup which is not actually a part of the visual tree of the Window. So it's better to try to use MVVM here and set IsChecked through the view model.
I have a dependency property on a user control called "RefreshCommand" (ICommand type) that I want to bind to a Command in my main window.
If I write this in code, it works...
MainToolbar.RefreshCommand = (ICommand)this.CommandBindings[0].Command;
.. but how can I express that in XAML?
I would additionally like to refer to the command by its name as opposed to its index.
Thanks,
you can bind in XAML to other XAML Element through e.g.
The Element name:
RefreshCommand="{Binding ElementName=window, Path=CommandBindings[0].Command}"
On Properties of "yourself"
RefreshCommand="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=CommandBindings[0].Command}"
Upwards along the tree with AncestorType
RefreshCommand="{Binding Path=CommandBindings[0].Command, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
If the toolbar is a child from window, I suppose the third should work well.
I would recommend doing this then.
3 Classes
MainWindow.xaml (Window)
MainViewModel.cs (Class)
UserControl.xaml (UserControl)
Set the data context of the MainWindow to the ViewModel. The UserControl will inheret this data context unless you change it explicitly. Set up the controls on the user control / main window to bind to relay commands on the Main View Model.
This will give you the context you need without hard coding indexes (xxx.[0]) and still maintain a degree of separation.
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.