I'm using WPF to make a custom control, I need to retrieve a property that is defined in the user control code behind, so I used the RelativeSource, but I get this error
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=LeftColumnHeader; DataItem=null; target element is 'ExtDataGridComboBoxColumn' (HashCode=47761); target property is 'Header' (type 'Object')
My XAML code (the nested tree) is:
<UserControl x:Class="Administration.Views.UserRoleView"
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:WPFCtrlDg="clr-namespace:WPFControls.DataGrids;assembly=WPFControls"
xmlns:WPFCtrl="clr-namespace:WPFControls;assembly=WPFControls"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<CollectionViewSource x:Key="AllItemsView" Source="{Binding Path='AllitemsList'}" />
</UserControl.Resources>
<Grid>
<GroupBox Grid.Row="0" Grid.Column="0" Header="Assigned Elements">
<WPFCtrlDg:SelfBindingDataGrid x:Name="_sbgAssigned" ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Path=Assigned}"
SelectedItem="{Binding Path=CurrentAssignedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<WPFCtrlDg:SelfBindingDataGrid.Columns>
<WPFCtrlDg:ExtDataGridComboBoxColumn Header="{Binding Path=LeftColumnHeader,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}"
Width="*"/>
Inside the codebehid of the usercontrol I defined my property
private string _leftColumnHeader = "TEST";
public string LeftColumnHeader
{
get { return _leftColumnHeader; }
set { _leftColumnHeader = value; }
}
}
Any idea of how to retrieve my property in order to use it as head function of my satagrid column?
thank you
Andrea
Use either
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserRoleView}},
Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Or add this to your UserControl:
<UserControl
Name="myControl"
...
Then instead of using RelativeSource use binding like this:
Header={Binding Path=LeftColumnHeader, ElementName=myControl}
But actually I'm not quite sure that you still will be able to bind it that way you do it, as columns headers have some weird rules when it comes to binding. Check it:
stackoverflow.com
stackoverflow.com
You need to use the name/type of your class, not the UserControl class that you are extending. Try this:
<WPFCtrlDg:ExtDataGridComboBoxColumn Header="{Binding Path=LeftColumnHeader,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type
UserRowView}}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
Related
I have a WPF Window and UserControl which I use inside the Window
Window:
<Window x:Class="AdjacentControlVisualTree.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AdjacentControlVisualTree"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<StackPanel Orientation="Vertical">
<local:AdjacentControl x:Name="AdjacentControl"/>
<Button Content="Foo"
CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl.BarDataGrid}"
Command="{Binding FooCommand}"/>
</StackPanel>
</Window>
UserControl:
<UserControl x:Class="AdjacentControlVisualTree.AdjacentControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AdjacentControlVisualTree">
<UserControl.DataContext>
<local:AdjacentViewModel/>
</UserControl.DataContext>
<Grid>
<DataGrid x:Name="BarDataGrid"
ItemsSource="{Binding Collection}"
IsReadOnly="True"
AutoGenerateColumns="False"
SelectionUnit="FullRow"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Header="String" Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
Now, the thing is, that I would like to pass SelectedItem property of DataGrid inside the AdjacentControl to the FooCommand command but the binding always passes null:
CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl.BarDataGrid}"
Is there a way how to make the binding work without having to change the structure of the controls?
The probably most simply way would be to expose BarDataGrid as a property.
Change the XAML name (e.g. to x:Name="barDataGrid") and add this property to the UserControl's code behind:
public DataGrid BarDataGrid { get { return barDataGrid; } }
Then bind to the DataGrid's SelectedItem property like this:
CommandParameter="{Binding BarDataGrid.SelectedItem, ElementName=AdjacentControl}"
A cleaner solution would be not to expose the DataGrid child, but only its SelectedItem, by means of a dedicated dependency property.
public static readonly DependencyProperty SelectedItemProperty =
System.Windows.Controls.Primitives.Selector.SelectedItemProperty.AddOwner(
typeof(AdjacentControl));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
You would bind the DataGrid's SelectedItem to this property by a RelativeSource Binding
<DataGrid SelectedItem="{Binding SelectedItem,
RelativeSource={RelativeSource AncestorType=UserControl}}"
and bind the CommandParameter like this:
CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl}"
I have a ContextMenu in a ResourceDictionary. The ContextMenu should hide or show depending on the value of a view-model property, but it does not work.
This is my XAML code (ControlBase derives from UserControl):
<control1:ControlBase>
<UserControl.Resources>
<ResourceDictionary>
<HierarchicalDataTemplate ItemsSource="{Binding InfraNetworkItems}">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Delete"
Visibility="{Binding
DataContext.MyViewModel.DeleteEnabled,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=control1:ControlBase},
Converter={StaticResource
BooleanVisibilityConverter}}" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</HierarchicalDataTemplate>
</ResourceDictionary>
</UserControl.Resources>
</control1:ControlBase>
DeleteEnabled is a bool property on the view-model.
My previous attempts of solving the problem are based on this assumptions:
The ContextMenu is inside a HierarchicalDataTemplate which has the ItemsSource set. My property is not a member of this ItemSource, it belongs to the view-model. Therefore I've tried this line of code, but without any effect:
Visibility="{Binding DataContext.MyViewModel.DeleteEnabled,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=control1:ControlBase},
Converter={StaticResource BooleanVisibilityConverter}}"
But if I copy the DeleteEnabled property from the view-model to the ItemSource object, it works:
Visibility="{Binding DeleteEnabled, Converter={StaticResource BooleanVisibilityConverter}}"
what is the DataContext of your view? If it's an instance of MyViewModel you have to change the path of your Binding.
Please try this one:
<Visibility="{Binding DataContext.DeleteEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=control1:ControlBase}, Converter={StaticResource BooleanVisibilityConverter}}" />
With setting the path to DataContext you already have access to your viewmodel and of course to the DeleteEnabled-Property.
Hope this helps.
I have a problem with binding a value in the Resource of a DataGrid.
Outside of the resources-Tag it works perfectly, but inside it doesn't work. I think maybe the Datacontext changed or is null.
I don't know what to do. I read something about freezables, but I didn't get them to work too.
Is that the solution or is that, what I'm doing not possible.
Here my code with the non-working and the working part - just for demonstration.
I need the Contextmenu in the Resources-Section to get it only, if clicked on the header-row.
<UserControl x:Class="testapp.test.testManager.Window"
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:local="clr-namespace:testapp.test.testManager"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600"
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
<Grid>
<DataGrid ItemsSource="{Binding Lst, UpdateSourceTrigger=PropertyChanged}"
AutoGeneratingColumn="dg_AutoGeneratingColumn">
<DataGrid.Resources>
<ContextMenu x:Key="DataGridColumnHeaderContextMenu">
<MenuItem Header="{StaticResource General}">
<!-- HERE the Binding cannot find "TestCheck" -->
<CheckBox Content="Testentry Header" IsChecked="{Binding TestCheck, UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"/>
<!-- ... --->
</MenuItem>
</ContextMenu>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource DataGridColumnHeaderContextMenu}" />
</Style>
</DataGrid.Resources>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="{StaticResource General}">
<!-- Here the Binding can find "TestCheck" -->
<CheckBox Content="Testentry" IsChecked="{Binding TestCheck, UpdateSourceTrigger=PropertyChanged,Mode=OneWay}"/>
<!-- ... -->
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
Issue is ContextMenu doesn't lie in same Visual tree as that of DataGrid and hence can't inherit DataContext of DataGrid.
You can use x:Reference to get the DataGrid instance and bind with it's DataContext. (x:Reference is available from WPF 4.0)
Give x:Name to dataGrid and bind with it:
<DataGrid ItemsSource="{Binding Lst, UpdateSourceTrigger=PropertyChanged}"
x:Name="dataGrid">
<DataGrid.Resources>
<ContextMenu x:Key="DataGridColumnHeaderContextMenu">
<MenuItem Header="{StaticResource General}">
<CheckBox Content="Testentry Header"
IsChecked="{Binding DataContext.TestCheck,
Source={x:Reference dataGrid}}"/>
....
</DataGrid>
Also you can achieve that using Freezable class like you mentioned in question. Refer to my answer over here for the details to achieve that via Freezable.
I was too fast with my comment the post ahead (I deleted it).
Both ways are working, but when I try to do it with a Dictionary BOTH are not working.
This works:
<CheckBox Content="Test" IsChecked="{Binding Path=Data.TestCheck, Source={StaticResource proxy}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
This not:
<CheckBox Content="Test" IsChecked="{Binding Path=Data.MyCheckState[MachineName], Source={StaticResource proxy}, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
The two Properties are defined like this:
public Dictionary<string, bool> MyCheckState
{
get { return _MyCheckState; }
}
public bool TestCheck
{
get { return true; }
}
I have an ItemsControl that uses a DataTemplate which is located in an external ResourceDictionary.xaml:
<ResourceDictionary ... >
<DataTemplate x:Key="My_UserControl">
<local:MyUserControl/>
</DataTemplate>
MyUserControl.xaml file:
<UserControl ...>
<Button Content="{Binding Path=Test, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<UserControl/>`
MainWindow.xaml uses that template in an ItemsControl.
The binding to the Window in UserControl doesn't work.
How do I bind from an external file like this UserControl to any parent, using RelativeSource so it works ?
Thanks
Try
<Button Content="{Binding Path=DataContext.Test, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
In my xaml file, I have:
<DataTemplate DataType="{x:Type Configuration:Drivers}">
<ItemsControl ItemsSource="{Binding Cars}" FontWeight="Normal" />
<DataTemplate>
<DataTemplate DataType="{x:Type Configuration:Car}">
<UniformGrid HorizontalAlignment="Stretch" Margin="5,1,5,2" Columns="2">
<CheckBox IsChecked="{Binding Enabled, UpdateSourceTrigger=PropertyChanged}"/>
<CheckBox Visibility="{Binding SaveImage, UpdateSourceTrigger=PropertyChanged}"/>
</UniformGrid>
</DataTemplate>
For each car, it has: Enabled property but does not have SaveImage property.
Car
{
public bool Enabled {}
}
The 'SaveImage' is set in globally. I don't know how to bind that: bool SaveImage inside the DataTemplate?
DataTemplates are an encapsulation boundary, so you cannot allways use FindAncestor to get the desired data. A good solution is to put your ViewModel in your XAML as a StaticResource and then set the DataContext of you LayoutRoot grid to this StaticResource, then all other DataTemplates can access the DataContext via the same StaticResource
EXAMPLE
<Window.Resources>
<local:MyViewModel x:Key="viewmodel" />
<DataTemplate DataType="{x:Type Configuration:Car}">
<UniformGrid HorizontalAlignment="Stretch" Margin="5,1,5,2" Columns="2">
<CheckBox IsChecked="{Binding Enabled, UpdateSourceTrigger=PropertyChanged}"/>
<CheckBox Visibility="{Binding Source={StaticResource viewmodel},
Path=SaveImage, UpdateSourceTrigger=PropertyChanged}"/>
</UniformGrid>
</DataTemplate>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource viewmodel}}">
</Grid>
If SaveImage is available in the DataContext of the ItemsControl you can bind to it this way:
<CheckBox IsChecked="{Binding DataContext.SaveImage, UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"/>
Solution 1:
You can try RelativeSource to Bind IsCheck to the abcestor object.
{Binding Path=SaveImage, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Solution 2:
Add a property SaveImage to view-model class Car and ref to model SaveImage.It's not a good solution.