Get UserControl DataContext properties to bind to from within the control - wpf

Here is the code for me UserControl:
<UserControl x:Class="UZ.ActivitySink.GUI.Views.POSsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:UZ.ActivitySink.GUI.Views">
<DockPanel>
<TreeView ItemsSource="{Binding Types}" x:Name="POSTree" Background="{x:Null}" HorizontalAlignment="Left" FontSize="14"
Visibility="{Binding DataContext.TreeVisibility, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:POSsView}}, Mode=TwoWay}">
</TreeView>
<StackPanel x:Name="ErrorPanel"
Visibility="{Binding DataContext.ErrorMessageVisibility, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:POSsView}}, Mode=TwoWay}" Margin="20">
</StackPanel>
</DockPanel>
</UserControl>
I am assigning a datacontext object to the control in it's constructor
DataContext = _viewModel;
_viewModel has the properties named TreeVisibility and ErrorMessageVisibility of type Visibility, but still the Visual elements on the screen don't bind their visibility values to these properties.
What is the correct way to reference the controls' viewmodel properties from xaml declaration in my case?
Thank you.

Your bindings are more complicated than necessary.
This one:
Visibility="{Binding DataContext.TreeVisibility,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Views:POSsView}}, Mode=TwoWay}"
in this case should be equivalent to the much simpler
Visibility="{Binding TreeVisibility}"
That said, even though the current bindings are complicated they should still have worked (at least given the information you already provide).
If you still can't get them to work, run your app in the debugger and look at the Output window -- binding errors are reported there by default, and they contain information that will help you get to the root of the problem.

Related

Bind Converter to the parent resource in xaml

I have a usercontrol1.xaml where I have a resource defined:
<UserControl x:Class="FrameworkDemo.usercontrol1View">
<UserControl.Resources>
<local:DemoManger x:Key="demoManager"/>
<local:DemoManagerConverterx x:Key="demoManagerConverter" Manager="{StaticResource strategyManager}"/>
</UserControl.Resources>
<telerik:RadTileView MinimizedItemsPosition="Top">
<telerik:RadTileViewItem>
<local:UserControl2View/>
</telerik:RadTileViewItem>
<telerik:RadTileViewItem>
........
</telerik:RadTileViewItem>
</telerik:RadTileView>
</UserControl>
Then in the user control view 2, I want to have this situation:
ss
<UserControl x:Class="FrameworkDemo.usercontrol2View">
<DockPanel>
<ComboBox MinWidth="270" Margin="0,0,5,5"
ItemsSource="{Binding Path=Demos, RelativeSource={RelativeSource AncestorType={x:Type local:DemoManager}}}"
SelectedValue="{Binding Path=CurrentStrategy, Converter={ ????}, Mode=TwoWay}"
IsEnabled="{Binding CanRefreshExecutionList, ElementName=Instance}"
DropDownOpened="StrategyComboBox_DropDownOpened">
</DockPanel>
</UserControl>
I was able to link to the parent control for the ItemSource, but for the converter how can I do it?. I can not event move the resource definition from control1 to control2. Inside of the RadTileViewItem is not possible to add another resource. Exactly in usercontrol1View I have a tabcontrol inside the RadTileViewItem and inside the tabiteam i have inlcluded the UserControl2View.
How can I link to the parent resource for the covnerter?
How can I link to the parent resource for the covnerter?
You can't. If you need to use the same converter in both UserControls, you have defined the resource in the wrong place actually.
You could either move it to your App.xaml file:
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
StartupUri="MainWindow.xaml">
<Application.Resources>
...
<local:DemoManagerConverterx x:Key="demoManagerConverter" Manager="{StaticResource strategyManager}"/>
</Application.Resources>
</Application>
Then you will be able to reference it across your entire application. The other option would be to define another resource of the same type in UserControl2:
<DockPanel>
<DockPanel.Resources>
<local:DemoManagerConverterx x:Key="demoManagerConverter" Manager="{StaticResource strategyManager}"/>
</DockPanel.Resources>
<ComboBox MinWidth="270" Margin="0,0,5,5"
ItemsSource="{Binding Path=Demos, RelativeSource={RelativeSource AncestorType={x:Type local:DemoManager}}}"
SelectedValue="{Binding Path=CurrentStrategy, Converter={StaticResource demoManagerConverter}, Mode=TwoWay}"
IsEnabled="{Binding CanRefreshExecutionList, ElementName=Instance}"
DropDownOpened="StrategyComboBox_DropDownOpened" />
</DockPanel>
But you can't reference a resource that is defined in a parent element using bindings.
I suppose you wanted bind converter object to the binding's Converter property.
You can't bind to the binding's Converter property since it is not a ´DependencyProperty´. You can acces a resource object and bind it e.g. to the ´Tag´, but it doesn't solve your problem:
<ComboBox MinWidth="270" Margin="0,0,5,5"
ItemsSource="{Binding Path=Demos, RelativeSource={RelativeSource AncestorType={x:Type local:DemoManager}}}"
SelectedValue="{Binding Path=CurrentStrategy, Converter={ ????}, Mode=TwoWay}"
Tag="{Binding Path='Resources[demoManagerConverter]', RelativeSource={RelativeSource AncestorType={x:Type localFrameworkDemo:usercontrol1View}}}"
IsEnabled="{Binding CanRefreshExecutionList, ElementName=Instance}"
DropDownOpened="StrategyComboBox_DropDownOpened">
If object is nested, you could just set(not bind) the Converter to the resource object:
Converter = {StaticResource demoManagerConverter}

WPF + Binding Command and Header of a Context Menu Item using MVVM

I created a WPF application and am following MVVM Pattern. I have a context menu in my xaml and I need to bind the command and Header text. Using the following code I can bind the Header of the context menu with the "MenuItemName" which is a property in BOList which is an observable collection. My issue is that command is not getting fired? I changes the Item source of the Context Menu to datacontext
(DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}")
Command is working fine but my header is getting blank. Is there a way to bind my header and command of a menu item? Here the command MenuClick is a Icommand property in the VM and MenuItemName is a property inside BOList which is an observable collection binded to my ListBox.
<Grid>
<StackPanel Orientation="Vertical">
<Button x:Name="btnClickMe" Command="{Binding ButtonCommand}" Content="Click Me" />
<ListBox ItemsSource="{Binding BOList}" x:Name="lstDemo" SelectedItem="{Binding BussinessObj,Mode=OneWayToSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stkStyleRender" Orientation="Horizontal" Background="Cyan" Width="525" Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" >
<TextBlock x:Name="txtId" Text="{Binding FirstName}"></TextBlock>
<TextBlock x:Name="txtName" Text="{Binding LastName}"></TextBlock>
<StackPanel.ContextMenu>
<ContextMenu x:Name="cntMnuTest" ItemsSource ="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" >
<MenuItem Header="{Binding MenuItemName}" Command="{Binding MenuClick}" CommandParameter="Icon"></MenuItem>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Hi Identified the issue.
If we set Item Source of the context menu to BOList (observable collection ) command wont get fired because the ICommand definition is in Window data context (vm).
We need to handle the code like wise.
Since debugging is not possible for binding , I was beating around the bush :-)
This link helped me a lot WPF Tutorial - Debug Databinding Issues in WPF
In context menu use DataContext instead of using Items source
then bind your menu item
try this:
<MenuItem Header="{Binding Path=PlacementTarget.MenuItemName, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}" Command="{Binding Path=PlacementTarget.MenuClick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}" />

UserControl's DataContext

I'm creating a UserControl I want to use something like this:
<controls:ColorWithText Color="Red" Text="Red color" />
So far, I've implemented similar controls like this:
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" >
<Border Width="15" Height="15" Background="{Binding Color, ElementName=ThisControl}" />
<TextBlock Text="{Binding Text, ElementName=ThisControl}" />
</StackPanel>
</UserControl>
where Color and Text are dependency properties of the control defined in code. This works, but specifying ElementName every time seems unnecessary.
Another option that works is using
<UserControl x:Class=… DataContext="{Binding ElementName=ThisControl}" Name="ThisControl">
and not specifying ElementNames, but that doesn't seem like a clean solution to me either.
I have two questions:
Why doesn't <UserControl DataContext="{RelativeSource Self}"> work?
What is the best way to do something like this?
For first one, try :
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
And for second question, I think using ElementName or AncestorBinding is best way to bind to UserControl's properties.
Why can't you use <UserControl DataContext="{RelativeSource Self}">?
This is how you would use the control
<Grid DataContext="{StaticResource ViewModel}">
<!-- Here we'd expect this control to be bound to -->
<!-- ColorToUse on our ViewModel resource -->
<controls:ColorWithText Color="{Binding ColorToUse}" />
</Grid>
Now because we've hardcoded our data-context in the control it will instead attempt to lookup ColorToUse property on the ColorWithText object not your ViewModel, which will obviously fail.
This is why you can't set the DataContext on the user control. Thanks to Brandur for making me understand that.
What is the best way to do something like this?
Instead you should set the DataContext in the first child UI element in your control.
In your case you want
<StackPanel
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
Orientation="Horizontal" >
Now you have a DataContext which refers to your control so you can access any properties of that control using relative bindings.
I know this has been answered but none of the explanations give an Understanding of DataContext and how it works. This link does a great job for that.
EVERYTHING YOU WANTED TO KNOW ABOUT DATABINDING IN WPF, SILVERLIGHT AND WP7 (PART TWO)
In answer to your question #1
Why doesn't <UserControl DataContext="{RelativeSource Self}"> work?
This is a summary of the above link.
DataContext should not be set to Self at UserControl Element level. This is because it breaks the Inheritance of the DataContext. If you do set it to self and you place this control on a Window or another control, it will not inherit the Windows DataContext.
DataContext is inherited to all lower Elements of the XAML and to all the XAML of UserControls unless it is overwritten somewhere. By setting the UserControl DataContext to itself, this overwrites the DataContext and breaks Inheritance. Instead, nest it one Element deep in the XAML, in your case, the StackPanel. Put the DataContext binding here and bind it to the UserControl. This preserves the Inheritance.
See also this link below for a detailed explanation of this.
A SIMPLE PATTERN FOR CREATING RE-USEABLE USERCONTROLS IN WPF / SILVERLIGHT
In answer to your question #2
What is the best way to do something like this?
See code example below.
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=ThisControl}">
<Border Width="15" Height="15" Background="{Binding Color" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
Note that once you do this, you will not need the ElementName on each binding.
You should be using
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=Color}
for Databinding Related doubts always refer this sheet.
http://www.nbdtech.com/Blog/archive/2009/02/02/wpf-xaml-data-binding-cheat-sheet.aspx
You can set the datacontext to self at the constructor itself.
public ColorWithText()
{
InitializeComponent();
DataContext = this;
}
Now you can simply say
<UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
<StackPanel Orientation="Horizontal" >
<Border Width="15" Height="15" Background="{Binding Color}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</UserControl>
For the desperate souls, who are trying to make pdross's answer work and can't:
It's missing an essential detail - Path=DataContext.
The lower code segment starts working when you add it there with this being the result:
<StackPanel DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext}">

WPF Binding to ElementName inside ItemsControl

I have a checkbox, and an ItemsControl populating several DataGrids the following way:
<Checkbox Content="Birthday Column Visible" x:Name="UI_BirthdayVisibleCB" />
<ItemsControl ItemsSource="{Binding Path=ParentsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Children}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Birthday" Width="120" Visibility="{Binding IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" >
...
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Rest of closing tags>
This creates binding output errors as it tries to find IsChecked on the DataGridTemplateColumn. If I try to search for a Relative Ancestor I receive the exception:
Binding.RelativeSource cannot be set while using Binding.ElementName.
I have a ViewModel, and stick to MVVM mostly, but in this case I'd really like to keep the column visibilities on the View layer. Note that BoolToVis just converts Boolean to Visibility.
Edit
Here is an example of what I'm trying to do:
<DataGridTemplateColumn Header="Birthday" Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=IsChecked, ElementName=UI_BirthdayVisibleCB, Converter={StaticResource BoolToVis}}" />
It compiles but doesn't run however, it throws the exception above.
You are using RelativeSource, which can't be mixed with ElementName, but you once you have the correct RelativeSource, you can drill down deeper using path.
e.g.
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyView} }, Path=UI_BirthdayVisibleCB.IsChecked, Converter={StaticResource BoolToVis}}"
presumably you have some xaml like this:
<UserControl class="MyView" ... >...<CheckBox Name="UI_BirthdayVisibileCB"/> ...
The above binding should find this UserControl by type based on RelativeSource, then it will try to find a property named UI_BirthdayVisibleCB, which it won't find because WPF XAML implements this named element as a field.
The easy work around is to go into your codebehind and expose a property for it.
public object BirthdayVisibileCB_4_binding {
get { return UI_BirthdayVisibileDB; }
}
and bind to it instead:
Visibility="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:MyView} },
Path=BirthdayVisibileCB_4_binding.IsChecked, Converter={StaticResource BoolToVis}}"
Yes, it kind of a pain to do this, but MVVM only matches WPF so far... its not a great fit, its only the best fit we have around.
If you want to try RelativeSource, you have to remove ElementName from the declaration:
However, only one of the three
properties, ElementName, Source, and
RelativeSource, should be set for each
binding, or a conflict might occur.
This property throws an exception if
there is a binding source conflict.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx
Your usage of ElementName seems correct, so I'll continue to look at the problem if you prefer that over RelativeSource.

Binding parent container props to content child control in Silverlight

Example:
<UserControl x:Name="userControl"
<StackPanel x:Name="container" Margin="0">
<TextBox Text="{Binding Path=SettingValue, RelativeSource={RelativeSource Mode=Self}}"/>
</StackPanel>
</UserControl>
UserControl contains SettingValue dependency property, TextBox doesn't,
so this example won't work.
I could've done this if I had AncestorType, like in WPF:
RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControlType}
Is there any possibility to bind to UserControl.SettingValue property?
Did you try the following? Use the ElementName source (the syntax might be a bit off).
<TextBox Text="{Binding Path=SettingValue, ElementName=userControl"/>
The answer I've found here:
Binding Silverlight UserControl custom properties to its' elements

Resources