Resolving resources from one UserControl to a parent UserControl - wpf

I've got a user control UserControl1, which defines a style in its resources. That user control contains an instance of UserControl2, which references that style:
<UserControl x:Class="UserControl1">
<UserControl.Resources>
<Style x:Key="MyStyle" />
</UserControl.Resources>
<Grid>
<UserControl2 />
</Grid>
</UserControl>
<UserControl x:Class="UserControl2">
<Grid Style="{StaticResource MyStyle}">
</Grid>
</UserControl>
However, UserControl2 cannot find that style resource, even though it is in the logical tree (within the resources of UserControl1). How can I get UserControl2 to find the resources in UserControl1?

You can do it but, but I would suggest using a ResourceDictionary instead.
Anyway, if you want to do it this way you can use FindAncestor to find the parent and access the Resource you want out of the Parent ResourceDictionary
<UserControl x:Class="UserControl1">
<UserControl.Resources>
<Style x:Key="MyStyle" />
</UserControl.Resources>
<Grid>
<UserControl2 />
</Grid>
</UserControl>
<UserControl x:Class="UserControl2">
<Grid Style="{Binding Resources[MyStyle], RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}}">
</Grid>
</UserControl>
Beacause Resource is a dictionary you can access using the key just like in code behind

I had the same issue and was able to solve it by referencing the resource through DynamicResource instead of StaticResource:
<UserControl x:Class="UserControl2">
<Grid Style="{DynamicResource MyStyle}">
</Grid>
</UserControl>
The compiler does still give a warning that the resource cannot be resolved.

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 bind to type Window as a DataTemplate

My WPF application uses a Resource Dictionary. I'm also using MVVM.
I am binding to a ResourceDictionary, but want to bind my MainWindow ViewModel to the MainWindow (of type Window) but MVVM won't let me as MainWindow it's not type UserControl.
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<Binding Source="{StaticResource Mwvm}" />
</Grid.DataContext>
</Grid>
This means I can't do this
<DataTemplate DataType="{x:Type viewModel:MainWindowViewModel}">
<root:MainWindow x:Key="Mwvm" />
</DataTemplate>
Does any one know how I can do the same thing but when the object is a Window and only using XAML (I know I can do this with code behind in the app.xaml onstartup())?
EDIT
To make this very clear, I know that within my MainWindow I can declare a namespace to my ViewModel, but is this the correct way when the namespace is already referenced in my ResourceDictionary and I'm referencing my ResourceDictionary.
Uhm how about?
<Window>
<Window.DataContext>
<someNs:YourVmClass /> <!-- calls the empty c_tor of the class-->
</Window.DataContext>
</Window>
(I'm not sure, if I understood your question. But I guess, that's what you really want.)
According to your edit:
Sure you could do something like
<!-- Define an instance of your VM in the ResourceDictionary -->
<ResourceDictionary>
<someNs:YourVmClass x:Key="InstOfYourVmClass" />
</ResourceDictionary>
In your view you could do something like this.
<Grid>
<Grid.Resources>
<ResourceDictionary Source="Resources\ResourceDictionary.xaml" />
</Grid.Resources>
<Grid.DataContext>
<StaticResource ResourceKey="InstOfYourVmClass" />
</Grid.DataContext>
</Grid>
But I would strongly recommend not to choose this approach. Problem is, everytime you're referencing this ResourceDictionary the current instance InstOfYourVmClass will be overwritten by a new instantiated version.

WPF Visibility Resource with Binding

I got a WPF Application using MVVM Light. In one View i got many Controls which are using the same visibility binding. Because i dont like to insert my long binding path to every control. I'd like to use a Resource...
<SomeControl>
<SomeControl.Resource>
<Visibility x:Key="myVisibilityResource" >
<Binding Path="somePath" Converter="BoolToVisibilityConverter"></Binding>
</Visibility>
</SomeControl.Resource>
<SomeControl>
With this version i get the Error that Visibility does not support direct content. I would have to write it like this:
<Visibility x:Key="myVisibilityResource" >
Collapsed
</Visibility>
Any ideas?
If all you want is to be able to write "Visible" or "Collapsed" in your XAML (as in your last example) you can do it by binding to and ObjectDataProvider which is uses Enum.Parse to parse the given text (which can be "Visible", "Collapsed", or "Hidden"):
<Grid xmlns:sys="clr-namespace:System;assembly=mscorlib" >
<Grid.Resources>
<ObjectDataProvider x:Key="visibilityProvider"
MethodName="Parse"
ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="Visibility" />
<sys:String>Visible</sys:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Grid.Resources>
<Button x:Name="button1" Visibility="{Binding Source={StaticResource visibilityProvider}}" />
</Grid>
In the above code I am using an ObjectDataProvider to return a Visibility enumeration. The ObjectDataProvider calls Enum.Parse when binding. The button's Visibility property binds to the ObjectDataProvider.
The MethodParameters property specifies the parameters for Enum.Parse.
Unfortunately there is no way to bind MethodParameters directly to an underlying ViewModel. If you want to do this, I guess you could write your own ObjectDataProvider or you could use your own class/method instead of Enum.Parse which returns the correct Visibility value.
Create a style in resources:
<Style TargetType="Control" x:Key="VisibilityStyle">
<Setter Property="Visibility"
Value="{Binding Converter={StaticResource VisibleIfFalse}}"/>
</Style>
Then simply add the reference to the item to hide/show.
<TextBlock Text="123" Style="{StaticResource VisibilityStyle}"/>
<TextBlock Text="123" Style="{StaticResource VisibilityStyle}"/>
<TextBlock Text="123" Style="{StaticResource VisibilityStyle}"/>
<TextBlock Text="123" Style="{StaticResource VisibilityStyle}"/>
This also works for headers of grids...
How about something like this? It's not exactly what you asked for, but you would be copying less code around since you wouldn't have to include the converter every time.
<UserControl>
<Grid>
<SomeElement x:Name="Master" Visibility="{Binding somePath, Converter={StaticResource BoolToVisibilityConverter}}" />
<SomeOtherElement Visibility="{Binding Visibility, ElementName=Master}" />
<SomeOtherElement Visibility="{Binding Visibility, ElementName=Master}" />
</Grid>
</UserControl>

How to set tooltip for user control (from style file)

I created my control which looks like that
<UserControl BorderBrush="#A9C2DE" HorizontalAlignment="Left" x:Class="WPFDiagramDesignerControl.Components.UcWBSBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="86" Width="151" >
<UserControl.Resources>
<ResourceDictionary Source="Tooltip.xaml"/>
</UserControl.Resources>
<Grid x:Name="MainGrid">
<TextBox Name="txtBox" Style="{StaticResource DefaultStyle}" >
</TextBox>
</Grid>
I also have a file with style for tooltip "Tooltip.xaml"
How can I use this style for entire UserControl?
Usually I did this with that code
<TextBox ToolTip="{StaticResource tooltipname}"/>
But it was easy because file with style was in resource dictionary of control where I placed textbox. However I can't do sth like that
<UserControl BorderBrush="#A9C2DE" HorizontalAlignment="Left" ToolTip="{StaticResource tooltipname"}/>
Because at this point my style isn't in resource dicionary yet.
I was trying to use this syntax
<UserControl.ToolTip> </UserControl.ToolTip>
but I don't konow how should I refer to static resource
Maybe it is lame question but I just don't konow how to do it :)
One option is to just use DynamicResource instead of StaticResource to defer the lookup until runtime and then use the attribute syntax:
<UserControl ... ToolTip="{DynamicResource tooltipname}" ...
You can also write the StaticResourceExtension using element syntax so that you can write it after the Resources section:
<UserControl.Resources>
<ResourceDictionary Source="Tooltip.xaml"/>
</UserControl.Resources>
<UserControl.ToolTip>
<StaticResourceExtension ResourceKey="tooltipname"/>
</UserControl.ToolTip>

Is there a way to group RadioButtons generated from the ItemTemplate of an ItemsControl

<DataTemplate x:Key="Genre_DataTemplate">
<RadioButton GroupName="One" Content="{Binding...
</DataTemplate>
Above code is the ItemTemplate of my ItemsControl, I want all the Radiobuttons instantiated should behave as if it is in a group, I know the reason because the generated RadioButtons are not adjacent in the visualtree.
Any solution or workaround to group them together?. GroupName property also doesn't have any effect here.
[Update] I am trying this in Silverlight
The problem is that the RadioButton.GroupName behavior depends on the logical tree to find a common ancestor and effectively scope it's use to that part of the tree, but silverlight's ItemsControl doesn't maintain the logical tree. This means, in your example, the RadioButton's Parent property is always null
I built a simple attached behavior to fix this. It is available here: http://www.dragonshed.org/blog/2009/03/08/radiobuttons-in-a-datatemplate-in-silverlight/
I think the problem is somewhere else in the control tree. Can you post more details?
Here is a sample xaml code that works as expected:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.Resources>
<XmlDataProvider x:Key="flickrdata" Source="http://api.flickr.com/services/feeds/photos_public.gne?tags=flower&lang=en-us&format=rss_200">
<XmlDataProvider.XmlNamespaceManager>
<XmlNamespaceMappingCollection>
<XmlNamespaceMapping Prefix="media" Uri="http://search.yahoo.com/mrss/"/>
</XmlNamespaceMappingCollection>
</XmlDataProvider.XmlNamespaceManager>
</XmlDataProvider>
<DataTemplate x:Key="itemTemplate">
<RadioButton GroupName="One">
<Image Width="75" Height="75" Source="{Binding Mode=OneWay, XPath=media:thumbnail/#url}"/>
</RadioButton>
</DataTemplate>
<ControlTemplate x:Key="controlTemplate" TargetType="{x:Type ItemsControl}">
<WrapPanel IsItemsHost="True" Orientation="Horizontal"/>
</ControlTemplate>
</Grid.Resources>
<ItemsControl
Width="375"
ItemsSource="{Binding Mode=Default, Source={StaticResource flickrdata}, XPath=/rss/channel/item}"
ItemTemplate="{StaticResource itemTemplate}"
Template="{StaticResource controlTemplate}">
</ItemsControl>
</Grid>
</Page>
P.S.: In order grouping to work elements radio buttons should have same parent (as they usually have when generated from ItemsControl)

Resources