Binding to UserControl.Resource from within datatemplate created in Code (WPF) - wpf

I want to reference my datacontext from inside a datatemplate. In XAML it works fine: I create a BindingProxy that is referenced as staticResource from inside the DataTemplate and so I can access the required property.
<UserControl.Resources>
<ResourceDictionary>
<helpers:BindingProxy x:Key="DataContextProxy" Data="{Binding}" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.Resources>
<helpers:ComboBoxPerformanceConverter x:Key="AntriebsArtConverter" LookupDictionary="{Binding Source={StaticResource DataContextProxy}, Path=Data.AntriebsArtenDict }" />
</Grid.Resources>
...
<telerik:RadGridView ItemsSource="{Binding MyItemSource }">
<telerik:RadGridView.Columns>
<telerik:GridViewComboBoxColumn
ItemsSource="{Binding Source={StaticResource DataContextProxy}, Path=Data.AntriebsArten}"
DataMemberBinding="{Binding Fahrzeug.AntriebsArtId}"
SelectedValueMemberPath="AntriebsArtId"
DisplayMemberPath="Bezeichnung">
<telerik:GridViewComboBoxColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Fahrzeug.AntriebsArtId, Converter={StaticResource AntriebsArtConverter}}" />
</DataTemplate>
</telerik:GridViewComboBoxColumn.CellTemplate>
</telerik:GridViewComboBoxColumn>
</telerik:RadGridView.Columns>
...
If I try the same in Code I get everything working except the binding for LookupDictionary. Here I try to reference the datacontext not from the datagrid but from the surrounding UserControl. It gives me the error "Resource with name ""DataContextProxy"" cannot be found. any advice on how I could solve this?
Private Function CreateDataTemplate(textblockName As String) As DataTemplate
Dim memoryStream As MemoryStream
Dim parserContext As New ParserContext()
Dim xaml = "<DataTemplate xmlns:helpers=""clr-namespace:ViewModel.Helpers;assembly=ViewModel"">"
xaml += "<DataTemplate.Resources>"
xaml += "<helpers:ComboBoxPerformanceConverterVm x:Key=""AntriebsArtConverter"" LookupDictionary=""{Binding Path=Data.AntriebsArtenDict, Source={StaticResource DataContextProxy}}""/>"
xaml += "</DataTemplate.Resources><TextBlock Text=""{Binding Fahrzeug.AntriebsArtId, Converter={StaticResource AntriebsArtConverter}}"" /></DataTemplate>"
memoryStream = New MemoryStream(Encoding.UTF8.GetBytes(xaml))
parserContext.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
parserContext.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml")
Dim dataTemplate = CType(XamlReader.Load(memoryStream, parserContext), DataTemplate)
Return dataTemplate
End Function
Thanks for your help in advance!

In your xaml file, the helpers:ComboBoxPerformanceConverterVm is defined outside , its "DataContext" is the UserControl, and knows DataContextProxy.
But in the code, you moved this converter to inside the DataContext for this converter is now: "Column".
The best solution is to move this converter definition outside of like what you did in Xaml.

Related

WPF DataGRidTextColumn binding Visibility is not working [duplicate]

How can I hide a column in a WPF DataGrid through a Binding?
This is what I did:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility}" />
And this is what I got (besides the column still visible):
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyColumnVisibility; DataItem=null; target element is 'DataGridTextColumn' (HashCode=1460142); target property is 'Visibility' (type 'Visibility')
How to fix the binding?
First of all, DataGridTextColumn (or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid. Hence, by default it doesn't inherit the DataContext of the DataGrid. However, it works for Binding DP only and for no other DP's on DataGridColumn.
Since they don't lie in the same VisualTree, any attempt to get the DataContext using RelativeSource won't work as well because DataGridTextColumn is unable to traverse up to the DataGrid.
There are two other ways to achieve this though:
First using a Freezable class. Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.
First, create a class inheriting from Freezable and Data DP which we can use to bind in XAML:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
Now, add an instance of it in DataGrid resources so that it can inherit the DataGrid's DataContext and can bind with its Data DP:
<DataGrid>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
Second, you can refer to any UI element in XAML using ElementName or x:Reference. However, ElementName works only in the same visual tree, whereas x:Reference doesn't have such constraints.
So, we can use that as well to our advantage. Create a dummy FrameworkElement in XAML with Visibility set to collapsed. The FrameworkElement will inherit the DataContext from its parent container, which can be a Window or UserControl.
And can use that in DataGrid:
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Test"
Binding="{Binding Name}"
Visibility="{Binding DataContext.IsEnable,
Source={x:Reference dummyElement}}"/>
</DataGrid.Columns>
</DataGrid>
<Window.Resources>
<ResourceDictionary>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
</ResourceDictionary>
</Window.Resources>
<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
AutoGenerateColumns="False"
FlowDirection="LeftToRight"
ItemsSource="{Binding PayStructures}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70"
Header="name"
IsReadOnly="True"
Visibility="{Binding DataContext.IsShowName,
Source={StaticResource ProxyElement}}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding FieldName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</mch:MCHDataGrid>
Sample of bound property in view model:
private Visibility _isShowName;
public Visibility IsShowName
{
get { return _isShowName; }
set
{
_isShowName = value;
OnPropertyChanged();
}
}
Another easy solution I like is to add a dummy collapsed FrameworkElement at the same level as the DataGrid. The FrameworkElement can then be used as the Source of the Binding with the x:Reference markup extension.
For example like this:
<FrameworkElement x:Name="FrameWorkElementProxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.DataGridColumnVisibility, Source={x:Reference Name=FrameWorkElementProxy}}"/>
</DataGrid.Columns>
</DataGrid>
Another fast option if you have created the Window/Page/UserControl DataContext object in the XAML like this:
<Window.DataContext>
<local:ViewModel x:Name="MyDataContext"/>
</Window.DataContext>
is that you can add x:Reference using the x:Name of the DataContext object in the Source of the binding:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility, Source={x:Reference Name=MyDataContext}}"
That way you can avoid using Binding DataContext.MyColumnVisibility, and just use Binding MyColumnVisibility

How ist the DataContext of a Unsercontrol related to its DependencyProperties?

while working with UserControls having DependencyProperties i realized that it is curcial to consider where to set the DataContext. To picture it ive created a sample application. There are two UserControls, both equal except on where the DataContext is set:
Working UserControl:
<UserControl x:Class="DpropTest.OkUserControl"
...>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:OkUserControl }}">
<TextBlock Text="{Binding Path=MyDepProp}"></TextBlock>
</Grid>
</UserControl>
Not working user control:
<UserControl x:Class="DpropTest.NotOkUserControl"
...
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:NotOkUserControl}}"
>
<Grid >
<TextBlock Text="{Binding Path=MyDepProp}"></TextBlock>
</Grid>
Both UserControls have a DependencyProperty called MyDepProp,
#region Dependency Property Declaration
public static readonly DependencyProperty MyDepPropProperty = DependencyProperty.Register(
"MyDepProp", typeof(string), typeof(NotOkUserControl), new PropertyMetadata(default(string)));
public string MyDepProp
{
get { return (string)GetValue(MyDepPropProperty); }
set { SetValue(MyDepPropProperty, value); }
}
#endregion Dependency Property Declaration
This is how i integrated the UserControls to the mainWindow:
<Grid x:Name="ParentGrid">
<StackPanel>
<dpropTest:OkUserControl MyDepProp="{Binding Path=ActualWidth, ElementName=ParentGrid}"/>
<dpropTest:NotOkUserControl MyDepProp="{Binding Path=ActualWidth, ElementName=ParentGrid}"/>
</StackPanel>
</Grid>
The running application shows the actualWith for the first UserControlonly only, the second UserControl remains unset as the DP doesnt bind.
There is no error in the output window regarding the second UserControl...
Maybe there is an WPF Pro out there with an brief explanation?
Thank you!
Uli
I don't think FindAncestor will start with the element itself, but apart from that: you can either set this on the UserControl:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
or set this in the constructor of the user control, before the InitializeComponent:
DataContext = this;
As a sidenote: it's often not necessary to bind with the ActualWidth of some ancestor; in this case the width of the stackpanel is the same as the width of its parent grid and the width of the usercontrols is the same as the width of ths stackpanel. So in effect MyDepProp is equal to the ActualWidth of the usercontrol.
<UserControl x:Class="DpropTest.NotOkUserControl"
...
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:OkUserControl }}"
Seems to me you are binding to wrong parent! You are inside NotOkUserControl but you are asking for unreachable AncestorType...

WPF: relativesource data binding on a custom dependencyproperty

I'm trying to create a custom multi value combobox. So basically a combobox with some checkboxes as items. The idea is, to keep the whole control fully bindable, so that I can be reused any time.
Here's the XAML
<ComboBox x:Class="WpfExtensions.Controls.MultiSelectComboBox"
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:WpfExtensions.Controls"
mc:Ignorable="d" d:DesignHeight="23" d:DesignWidth="150">
<ComboBox.Resources>
<local:CheckBoxConverter x:Key="CheckBoxConverter" />
</ComboBox.Resources>
<ComboBox.ItemTemplateSelector>
<local:MultiSelectBoxTemplateSelector>
<local:MultiSelectBoxTemplateSelector.SelectedItemsTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MultiSelectComboBox}}, Path=SelectedItems, Converter={StaticResource CheckBoxConverter}}" />
</DataTemplate>
</local:MultiSelectBoxTemplateSelector.SelectedItemsTemplate>
<local:MultiSelectBoxTemplateSelector.MultiSelectItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" HorizontalAlignment="Stretch"
Checked="CheckBox_Checked" Unchecked="CheckBox_Checked" Indeterminate="CheckBox_Checked" Click="CheckBox_Checked" />
</DataTemplate>
</local:MultiSelectBoxTemplateSelector.MultiSelectItemTemplate>
</local:MultiSelectBoxTemplateSelector>
</ComboBox.ItemTemplateSelector>
</ComboBox>
And the code behind for the custom property "SelectedItems"
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register("SelectedItems", typeof(IList), typeof(MultiSelectComboBox));
[Bindable(true)]
public IList SelectedItems
{
get
{
return (IList)GetValue(SelectedItemsProperty);
}
private set
{
SetValue(SelectedItemsProperty, value);
}
}
Now when I test the project, the RelativeSource is resolved correctly towards the control itself, however the Binding on the path "SelectedItems" fails with the debugger stating, that there is no such Path on the RelativeSource object.
Did I mess up the binding or did I make a complete logical error?
You are setting a RelativeSource as the Source, instead set the RelativeSource propperty like so:
<TextBlock Text="{Binding Path=SelectedItems, RelativeSource={RelativeSource AncestorType={x:Type local:MultiSelectComboBox}}, Converter={StaticResource CheckBoxConverter}}" />

Binding Visibility for DataGridColumn in WPF

How can I hide a column in a WPF DataGrid through a Binding?
This is what I did:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility}" />
And this is what I got (besides the column still visible):
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyColumnVisibility; DataItem=null; target element is 'DataGridTextColumn' (HashCode=1460142); target property is 'Visibility' (type 'Visibility')
How to fix the binding?
First of all, DataGridTextColumn (or any other supported dataGrid column) does not lie in the Visual tree of the DataGrid. Hence, by default it doesn't inherit the DataContext of the DataGrid. However, it works for Binding DP only and for no other DP's on DataGridColumn.
Since they don't lie in the same VisualTree, any attempt to get the DataContext using RelativeSource won't work as well because DataGridTextColumn is unable to traverse up to the DataGrid.
There are two other ways to achieve this though:
First using a Freezable class. Freezable objects can inherit the DataContext even when they’re not in the visual or logical tree –We can take advantage of that.
First, create a class inheriting from Freezable and Data DP which we can use to bind in XAML:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
Now, add an instance of it in DataGrid resources so that it can inherit the DataGrid's DataContext and can bind with its Data DP:
<DataGrid>
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Visibility="{Binding Data.MyColumnVisibility,
Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
Second, you can refer to any UI element in XAML using ElementName or x:Reference. However, ElementName works only in the same visual tree, whereas x:Reference doesn't have such constraints.
So, we can use that as well to our advantage. Create a dummy FrameworkElement in XAML with Visibility set to collapsed. The FrameworkElement will inherit the DataContext from its parent container, which can be a Window or UserControl.
And can use that in DataGrid:
<FrameworkElement x:Name="dummyElement" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="Test"
Binding="{Binding Name}"
Visibility="{Binding DataContext.IsEnable,
Source={x:Reference dummyElement}}"/>
</DataGrid.Columns>
</DataGrid>
<Window.Resources>
<ResourceDictionary>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
</ResourceDictionary>
</Window.Resources>
<!-- Necessary for binding to resolve: adds reference to ProxyElement to tree.-->
<ContentControl Content="{StaticResource ProxyElement}" Visibility="Collapsed" />
<mch:MCHDataGrid Height="350"
AutoGenerateColumns="False"
FlowDirection="LeftToRight"
ItemsSource="{Binding PayStructures}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70"
Header="name"
IsReadOnly="True"
Visibility="{Binding DataContext.IsShowName,
Source={StaticResource ProxyElement}}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding FieldName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</mch:MCHDataGrid>
Sample of bound property in view model:
private Visibility _isShowName;
public Visibility IsShowName
{
get { return _isShowName; }
set
{
_isShowName = value;
OnPropertyChanged();
}
}
Another easy solution I like is to add a dummy collapsed FrameworkElement at the same level as the DataGrid. The FrameworkElement can then be used as the Source of the Binding with the x:Reference markup extension.
For example like this:
<FrameworkElement x:Name="FrameWorkElementProxy" Visibility="Collapsed"/>
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn Header="post"
Visibility="{Binding DataContext.DataGridColumnVisibility, Source={x:Reference Name=FrameWorkElementProxy}}"/>
</DataGrid.Columns>
</DataGrid>
Another fast option if you have created the Window/Page/UserControl DataContext object in the XAML like this:
<Window.DataContext>
<local:ViewModel x:Name="MyDataContext"/>
</Window.DataContext>
is that you can add x:Reference using the x:Name of the DataContext object in the Source of the binding:
<DataGridTextColumn Header="Column header"
Binding="{Binding ColumnValue}"
Width="100"
ElementStyle="{StaticResource DataGridRightAlign}"
Visibility="{Binding MyColumnVisibility, Source={x:Reference Name=MyDataContext}}"
That way you can avoid using Binding DataContext.MyColumnVisibility, and just use Binding MyColumnVisibility

WPF binding user control with data in C# code

I've create user control like this:
public partial class View
{
public View()
{
InitializeComponent();
}
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(TeaserView) );
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
XAML:
<UserControl x:Class="Controls.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="164">
<Grid VerticalAlignment="Stretch"
x:Name="Preview">
<Label Height="28" Content="{Binding ElementName=Preview, Path=Name}" Background="LightYellow" x:Name="name" VerticalAlignment="Top" ></Label>
</Grid>
</UserControl>
and use it in Window1 simply in XAML:
<controls:View Height="200" Name="View1" Width="164" />
and I try set the Content in C# (Name property in this sample) but it does'n work, label's content is still empty. (All refereces, etc. are good) What's wrong?
Your code is wrong. You bind to Grid.Name property, which is "Preview", not to View.Name.
I really encourage you to go read from A to Z "DataBinding Overview" on MSDN. It worth your time, trust me :). In fact whole "Windows Presentation Foundation" section would be worth your attention.
As for your code, the following will work:
<UserControl x:Class="WpfApplication5.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300"
Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Label Height="28"
Content="{Binding Path=Name}"
Background="LightYellow"
VerticalAlignment="Top"/>
</Grid>
</UserControl>
But are you sure you want to hide "Name" property from parents?
Have you set the datacontext on the user control? Try setting it to point to its own codebehind:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I've put the Name property just as sample. I'm trying to set Label Content in Window1.xaml.cs like:
View1.Name = "Casablanca";
Try the following binding, it should work:
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:View}}, Path=Name}" />
You should also define a xmlns:local="whatever_path_you_have" on the top of the file.
I also suggest renaming "Name" DP to something else to avoid name collusion.
Copied your exact code and it works fine.
However, it's not doing what you're probably expecting it to do. You're setting the source of the binding to the Grid instance. Therefore, the Name property will yield "Preview". The Name property you've defined in your UserControl is ignored because there's already a Name property on UserControl.

Resources