tricky command source issue - wpf

I'm trying to use the RadDiagram commands from a separate window. My bindings don't work because "Cannot find source for binding with reference 'ElementName=Diagram'". My (simplified) XAML looks like this:
<UserControl x:Class="Client.Wpf.EditableLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:s="clr-namespace:Client.Wpf" xmlns:Converters="clr-namespace:Client.Wpf.Converters"
mc:Ignorable="d" DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="800" Background="Transparent" Name="Root">
<UserControl.Resources>
<Converters:MultiBoolToVisibilityConverter x:Key="multiBoolToVis"/>
<Converters:ObjectToBooleanConverter x:Key="objToBool"/>
<telerik:InvertedBooleanConverter x:Key="invBool"/>
</UserControl.Resources>
<telerik:RadDocking HasDocumentHost="False" BorderThickness="0" Name="DockingStation">
<telerik:RadSplitContainer>
<telerik:RadPaneGroup>
<telerik:RadPane CanDockInDocumentHost="False" Title="Controls" >
<telerik:RadPane.TitleTemplate>
<DataTemplate>
<Grid Margin="0 5">
<ContentPresenter Content="{Binding}" VerticalAlignment="Center"/>
<telerik:RadButton Command="telerik:DiagramCommands.Undo" CommandTarget="{Binding Path=Diagram, Source={RelativeSource FindAncestor, AncestorType={x:Type s:EditableLayoutControl}}}" />
</Grid>
</DataTemplate>
</telerik:RadPane.TitleTemplate>
<telerik:RadTabControl ...></telerik:RadTabControl>
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
<telerik:RadSplitContainer>
<telerik:RadPaneGroup>
<telerik:RadPane PaneHeaderVisibility="Collapsed" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Grid>
<telerik:RadDiagram Name="Diagram">...
</telerik:RadDiagram>
</Grid>
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking>
</UserControl>
The problematic line of code is this beaut from inside the DataTemplate:
CommandTarget="{Binding Path=Diagram, Source={RelativeSource FindAncestor, AncestorType={x:Type s:EditableLayoutControl}}}"
That line of code doesn't work because Diagram is not a property, and bindings require properties. Using ElementName instead of Path there also doesn't work as ElementName and Source are exclusive. I also tried this:
CommandTarget="{Binding ElementName=Diagram}"
That line of code doesn't work. It gives this error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=Diagram'. BindingExpression:(no path); DataItem=null; target element is 'RadButton' (Name=''); target property is 'CommandTarget' (type 'IInputElement')
I also tried CommandTarget="{x:Reference Diagram}", but that feature is apparently not yet implemented in .NET 4.0. How is it done?

In this particular situation I had to set the Loaded event on the undo button. Then in my event handler (code-behind) I did this: ((RadButton)e.Source).CommandTarget = Diagram;.

Related

How to assign DataContext from parent View's ViewModel (Property)

EDIT:
Here is a small sample Solution (VS 2013) showing my exact problem: nakkvarr.net/TestApp.zip
I have two views:
MainView
EmployeeView
I want my EmployeeView to reference the MainViewModel property "employees".
I tried this:
<DockPanel DataContext="{Binding DataContext.MainViewModel,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type EmployeeLisViewModel}}}">
But all I get is the Error: EmployeeListViewModel ist not supported in WPF ?!
(EmployeeListViewModel is also the name of the property of my MainViewModel)
The thing is:
My MainView has some menu items. One is for sorting the employee list, which is inside my UserControl. My UserControl ViewModel contains the Commands for sorting.
EDIT:
I changed it to vm:EmployeeListViewModel ... now the error is gone...but now I don't seem to get access to the ViewModel :/
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}"
Does not trigger, no emplyees get loaded. When I use the ViewModel directly, it works just fine.
MainWindow.xaml
<Window x:Class="de.XXX.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:de.XXX.Views"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
Icon="/Images/App.ico"
Style="{StaticResource MainWindowStyle}"
Title="MainWindow">
<DockPanel DataContext="{Binding MainViewModel, Source={StaticResource Locator}}">
<Menu DockPanel.Dock="Top">
...
</Menu>
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
</DockPanel>
</Window>
EmployeeListView.xaml //not model, copy & paste error x_X
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
If you modify your SubControl to below, this should work.
<Grid DataContext="{Binding Path=DataContext.SubVM, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
I have a hint on what you are trying to achieve and I guess you have missed some details on Binding especially on other underlying properties needed to be set. This part is working based on the given code. But then, there are numerous ways in making it better. Good luck.
I think there might be a combination of misunderstandings here! Your UserControl has
x:Class="de.XXX.Views.EmployeeListView"
defined as the class name in xaml. However, you're also saying that the file is named EmployeeListViewModel.xaml? A .xaml file should never be named a viewmodel. I believe you should have MainWindow.xaml, EmployeeListView.xaml, MainWindowViewModel.cs, and EmployeeListViewModel.cs, is this the case?
Also, in your MainWindow.xaml, you're already setting the datacontext of your EmployeeListView usercontrol:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding EmployeeListViewModel}" />
This implies to me that your class structure is that MainViewModel contains an instance of the EmployeeListViewModel... is this true?
If all the above is true, then this part of your UserControl xaml does not make sense:
<DockPanel DataContext="{Binding MainViewModel,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type vm:EmployeeListViewModel}}}">
Whether EmployeeListViewModel is actually your view and not your viewmodel, the property "MainViewModel" does not exist on EmployeeListViewModel, so the binding will never work.
If you're trying to have a reference to your Window's DataContext, MainViewModel, within your UserControl, a possible way to do this is:
MainWindow.xaml:
<controls:EmployeeListView DockPanel.Dock="Bottom" DataContext="{Binding}" />
This should set the DataContext of your EmployeeListView to MainViewModel. Then, in your EmployeeListView, you can do this:
<UserControl x:Class="de.XXX.Views.EmployeeListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:de.XXX.ViewModel"
xmlns:ia="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="350">
<DockPanel
<ListBox DockPanel.Dock="Bottom" x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}" ScrollViewer.CanContentScroll="False" />
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding EmployeeListViewModel.LoadEmployeesCommand}" />
</ia:EventTrigger>
</ia:Interaction.Triggers>
</DockPanel>
</UserControl>
There's a lot of assumptions about your views and viewmodels I made for this answer. I hope this at least helps. If it doesn't make any sense, please post your viewmodels as well :)

How to make binding from user control to property of parent control?

I've seen similar questions, but I still not able to do my need. I need to output checkbox's name through a label inside a user control:
Window1.xaml:
<Window x:Class="WpfBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfBinding" Title="Window1" Height="300" Width="300">
<Grid>
<CheckBox Name="checkBox1">
<local:UserControl1></local:UserControl1>
</CheckBox>
</Grid>
</Window>
UserControl1.xaml:
<UserControl x:Class="WpfBinding.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas>
<Label Content="{Binding ElementName=checkBox1, Path=Name}"></Label>
</Canvas>
</UserControl>
How to do it correctly? What lack of knowledge I have? Thanks for help.
Above solution will work, but a more direct solution for this specific problem would be to use RelativeSource binding in your user control as below:
<Canvas>
<Label Content="{Binding RelativeSource={RelativeSource AncestorType=CheckBox, AncestorLevel=1}, Path=Name}"></Label>
</Canvas>
Hope this is what you need !!!
ElementName binding works within in same XAML scope. This will work -
<Grid>
<CheckBox Name="checkBox1"/>
<Label Content="{Binding ElementName=checkBox1, Path=Name}"/>
</Grid>
But if you want to do it in different UserControl, you have to tweak a bit your code and use Tag to hold name -
<Grid>
<CheckBox Name="checkBox1">
<local:UserControl1 Tag="{Binding ElementName=checkBox1, Path=Name}"/>
</CheckBox>
</Grid>
UserControl.xaml
<Canvas>
<Label Content="{Binding Path=Tag, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=UserControl}}"/>
</Canvas>
On a sidenote, in your UserControl, you know you need to bind with ElementName = checkBox1 and that's the name only you are binding to. Its something equivalent to -
<Label Content="checkBox1"/>

Binding one dependency property to another

I have a custom Tab Control that I have created, but I am having an issue. I have an Editable TextBox as part of the custom TabControl View.
<Controls:EditableTextControl x:Name="PageTypeName"
Style="{StaticResource ResourceKey={x:Type Controls:EditableTextControl}}" Grid.Row="0" TabIndex="0"
Uid="0"
AutomationProperties.AutomationId="PageTypeNameTextBox"
AutomationProperties.Name="PageTypeName"
Visibility="{Binding ElementName=PageTabControl,Path=ShowPageType}">
<Controls:EditableTextControl.ContextMenu>
<ContextMenu x:Name="TabContextMenu">
<MenuItem Header="Rename Page Type" Command="{Binding Path=PlacementTarget.EnterEditMode, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
AutomationProperties.AutomationId="RenamePageTypeMenuItem"
AutomationProperties.Name="RenamePageType"/>
<MenuItem Header="Delete Page Type" Command="{Binding Path=PageTypeDeletedCommand}"
AutomationProperties.AutomationId="DeletePageTypeMenuItem"
AutomationProperties.Name="DeletePageType"/>
</ContextMenu>
</Controls:EditableTextControl.ContextMenu>
<Controls:EditableTextControl.Content>
<!--<Binding Path="CurrentPageTypeViewModel.Name" Mode="TwoWay"/>-->
<Binding ElementName="PageTabControl" Path="CurrentPageTypeName" Mode ="TwoWay"/>
</Controls:EditableTextControl.Content>
</Controls:EditableTextControl>
In the Content section I am binding to a Dependency Prop called CurrentPageTypeName. This Depedency prop is part of this custom Tab Control.
public static DependencyProperty CurrentPageTypeNameProperty = DependencyProperty.Register("CurrentPageTypeName", typeof(object), typeof(TabControlView));
public object CurrentPageTypeName
{
get { return GetValue(CurrentPageTypeNameProperty) as object; }
set { SetValue(CurrentPageTypeNameProperty, value); }
}
In another view, where I am using the custom TabControl I then bind my property, with the actual name value, to CurrentPageTypeName property as seen below:
<Views:TabControlView Grid.Row="0" Name="RunPageTabControl"
TabItemsSource="{Binding RunPageTypeViewModels}"
SelectedTab="{Binding Converter={StaticResource debugConverter}}"
CurrentPageTypeName="{Binding Path=RunPageName, Mode=TwoWay}"
TabContentTemplateSelector="{StaticResource tabItemTemplateSelector}"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.SelectedTabIndex}"
ShowPageType="Hidden" >
<!--<Views:TabControlView.TabContentTemplate>
<DataTemplate DataType="{x:Type ViewModels:RunPageTypeViewModel}">
<RunViews:RunPageTypeView/>
</DataTemplate>
</Views:TabControlView.TabContentTemplate>-->
</Views:TabControlView>
My problem is that nothing seems to be happening. It is grabbing its Content from the Itemsource, and not from my chained Dependency props. Is what I am trying even possible? If so, what have I done wrong.
Thanks for looking.
Unless I'm missing something this is definitely possible. Here is a simplified working example.
User control with a dependency property named TestValue, containing a TextBox bound to this property:
<UserControl x:Class="TestApp.TestControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
x:Name="TestControlName">
<Grid>
<TextBox Text="{Binding ElementName=TestControlName, Path=TestValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
A different view using this user control, binding the above mentioned dependency property to something:
<Window x:Class="TestApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestApp="clr-namespace:TestApp" Title="MainWindow"
Height="350" Width="525">
<StackPanel>
<TestApp:TestControl TestValue="{Binding ElementName=SourceTextBox, Path=Text, Mode=TwoWay}" />
<TextBox Name="SourceTextBox" />
</StackPanel>
</Window>
It sounds that the issue is somewhere in the part of the code you have not posted (e.g. wrong name used in Content binding).
I think you already solved this yourself for the "SelectedIndex" property. Just do the same thing for the "CurrentPageType" property i.e. use RelativeSource

Caliburn Micro: ElementName for ComboBox in DataGrid not working

my problem is that the ItemsSource of a ComboBox in an DataGrid is not binding.
My UserControl:
<UserControl x:Class="MyClass"
x:Name="Root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
mc:Ignorable="d"
d:DesignHeight="360" d:DesignWidth="757">
The DataGrid:
<DataGrid x:Name="Article"
AutoGenerateColumns="False"
SelectionUnit="FullRow"
SelectionMode="Single"
CanUserAddRows="False"
CanUserDeleteRows="True"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4"
Margin="5">
<DataGrid.Columns>
<DataGridTemplateColumn Width="*"
Header="Article">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding FilteredArticle, ElementName=Root}" IsEditable="True"
cal:Message.Attach="[Event KeyUp] = [Action FindArticlel($source)]" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
If I don't say ElementName=Root, he tries to bind FilteredArticle in the Article class.
If I say ElementName=Root, he says the following at runtime:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=Root'. BindingExpression:Path=FilteredArticle; DataItem=null; target element is 'ComboBox' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
FilteredArticle is a BindableCollection in my ViewModel. All other bindings are working.
What is wrong here?
Using the latest stable version of Caliburn.Micro.
Binding to the view via ElementName is usually a bad practice. Mainly because someone that creates an instance of the view can give it a different x:Name and that will break your internal binding.
Plus, the FilteredArticle property is not the property of the view but of the view model which is the data context for the view.
Use relative source for binding in those scenarios
ItemsSource="{Binding DataContext.FilteredArticle,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UserControl}}}"
You can use a more specific notation (although, in 99% of cases it's not necessary)
ItemsSource="{Binding DataContext.FilteredArticle,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MyClass}}}"
Where local is the xmlns for the namespace of MyClass

Binding to ancestors from within a ResourceDictionary

How can I bind to a UserControl's property from within its ResourceDictionary? I want an object I declare in my resources to have the same DataContext as the UserControl it is contained in:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Some.Namespace"
DataContext="{Binding Path=ViewModel, RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<local:SomeClass
x:Key="SomeClass"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
</UserControl.Resources>
</UserControl>
At runtime I get the error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='UserControl', AncestorLevel='1''. BindingExpression:Path=DataContext; DataItem=null; target element is 'SomeClass' (Name=''); target property is 'DataContext' (type 'Object')
My workaround was to set the DataContext of the resource in the code-behind.
.xaml
<local:SomeType x:Key="SomeKey" SomeProperty="{Binding ... }" />
.xaml.cs
public SomeControl()
{
InitializeComponent();
((SomeType)this.Resources["SomeKey"]).DataContext = this;
}
When using FindAncestor, the target element needs to be a descendent (either logical or visual) of the source. Your object does not appear in either the visual or logical tree, it's simply in the resources. So you can't use RelativeSource with FindAncestor for your object.
You can use ElementName in your Binding though. Something like this should work:
<UserControl x:Name="userControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Some.Namespace"
DataContext="{Binding Path=ViewModel, RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<local:SomeClass
x:Key="SomeClass"
DataContext="{Binding Path=DataContext, ElementName=userControl}" />
</UserControl.Resources>
</UserControl>
Set x:Shared="False", this will clone resource on each use and make it a child of your element, enabling usage of bindings.
<local:SomeClass
x:Key="SomeClass"
x:Shared="False"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
I think what you're looking for is just {Binding} which binds to the inherited DataContext. Here's an example, though a bit strange shows how you can grab a color through binding to the DataContext:
<Window x:Class="AncestorBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Blue" />
</Window.Resources>
<StackPanel>
<Button DataContext="{Binding Source={StaticResource MyBrush}}" Content="My Button">
<Button.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding}" />
</Style>
</Button.Resources>
</Button>
</StackPanel>
</Window>
What I would do is to create an attached behavior (ContextualizeResourceBehavior) on the user control, and specify the resource key on that attached behavior. The behavior would lookup the resource (not sure you would be able to do it on attach, if not you would need to hook up Loaded event) and transfer the data context.
when you add your resource to the visual tree it should inherit the data context. but... have a look at element spy it might just do what you need.

Resources