Code-behind for DataTemplate in a ResourceDictionary - wpf

I am attempting to use code-behind to support an event handler in a DataTemplate. The below code works fine when it is the code-behind for a Window, but not for a ResourceDictionary. The code will not even compile when put in the code-behind for the ResourceDictionary.
I know that Commands is the better option here, but this is largely a test to make sure I can handle events on resources in a ResourceDictionary, if needed. My goal is to better organize my code, but this is not the straightforward "include" behavior that I thought a separate ResourceDictionary file would provide.
In MainWindow.xaml:
<Window x:Class="Wizbang.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:DevComponents.WpfEditors;assembly=DevComponents.WpfEditors"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:local ="clr-namespace:Wizbang"
xmlns:m ="clr-namespace:Wizbang.Model"
xmlns:vm="clr-namespace:Wizbang.ViewModel"
xmlns:vw="clr-namespace:Wizbang.View"
DataContext="{Binding Path=Main, Source={StaticResource Locator}}"
Title="Wizbang" Height="760" Width="1335" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary Source="Resources/MainWindowResources.xaml" />
</ResourceDictionary>
</Window.Resources>
In code-behind MainWindow.xaml.cs and MainWindowResources.xaml.cs, the same code:
private void btnSave_Click(object sender, RoutedEventArgs e)
{
//switch item template
Button btn = (Button)sender;
//command contains the list item
ContentControl itm = (ContentControl)btn.CommandParameter;
itm.SetValue(ContentTemplateProperty, this.FindResource("DetailedTemplate") as DataTemplate);
//this.UpdateLayout();
}
When I keep the ResourceDictionary inline in MainWindow.xaml, and put the code-behind in MainWindow.xaml.cs, everything works. When I attempt to use a separate file for ResourceDictionary, the code does not compile. The compiler complains about the last line:
itm.SetValue(ContentTemplateProperty, this.FindResource("DetailedTemplate") as DataTemplate);
The this.FindResource() is not a valid method, and "ContentTemplateProperty" is not found:
Error 4 The name
'ContentTemplateProperty' does not
exist in the current
context C:...\Visual Studio
2010\Projects\Wizbang\Wizbang\Resources\MainWindowResources.xaml.cs 36 26 Wizbang
Error 5 'Wizbang.Resources.MainWindowResources'
does not contain a definition for
'FindResource' and no extension method
'FindResource' accepting a first
argument of type
'Wizbang.Resources.MainWindowResources'
could be found (are you missing a
using directive or an assembly
reference?) C:...\Visual Studio
2010\Projects\Wizbang\Wizbang\Resources\MainWindowResources.xaml.cs 36 56 Wizbang
If I remove that last line, the code compiles and runs, but the button has no functionality. I think my issue is mapping that last line's references from the perspective of a ResourceDictionary, but I am not sure why it should be different.
Thanks for any thoughts.
Bill

This is because the code no longer is in the window class. You have to find it again (or whatever control you want to place the template on).
Window parentWindow = Window.GetWindow(btn);
itm.SetValue(Window.ContentTemplateProperty, parentWindow.FindResource("DetailedTemplate") as DataTemplate);

I think that using Commands would be a much cleaner approach.

Related

IOException when referencing App.xaml's ResourceDictionary

I'm trying to reference App.xaml's ResourceDictionary from a separate WPF window. I want to use resources from there in different windows, and this seems like the recommended way to do it. Unfortunately, I don't seem to be able to effectively reference App.xaml from the other window. Here is my App.xaml:
<Application x:Class="View.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ViewModel="clr-namespace:ViewModel;assembly=ViewModel"
xmlns:local="clr-namespace:View"
StartupUri="ClockView.xaml">
<Application.Resources>
<ResourceDictionary>
<local:PriorityToIconConverter x:Key="PriorityToIconConverter" />
</ResourceDictionary>
</Application.Resources>
Notes: I'm not using MainWindow, so I've replaced the startup URI with a form that always comes up. I noted that in some other answers, the location of MainWindow is sometimes the issue. In my case, I haven't seen any difference between using ClockView or MainWindow. Both ClockView and MainWindow exist in the root namespace, MainWindow is just never loaded. I also have more resources, but I've removed them for the sake of conciseness.
Here's a simplified example of the code where I'm trying to reference the ResrouceDictionary from App.xaml:
<local:AssistantWindow
x:Class="View.AutomatorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:properties="clr-namespace:View.Properties"
xmlns:local="clr-namespace:View"
mc:Ignorable="d"
Title="Tool"
x:Name="Tool"
Background="Transparent"
Height="600"
Width="450"
Topmost="{Binding Source={x:Static properties:Settings.Default}, Path=ToolAlwaysOnTop}"
MinHeight="515"
MinWidth="150">
<Window.Resources>
<ResourceDictionary Source="App.xaml" />
</Window.Resources>
Again, this is simplified to be concise. When I try to load this form, I get the exception:
System.Windows.Markup.XamlParseException: ''Set property 'System.Windows.ResourceDictionary.Source' threw an exception.' Line number '21' and line position '10'.'
Inner Exception
IOException: Cannot locate resource 'tool/app.xaml'.
The view for "Tool" is located in a folder that is also named "Tool." However, the xaml and code behind don't reference this namespace, I'm just using the folder to organize my classes. It looks like it's looking for App.xaml in the folder the view resides in. App.xaml resides in the root namespace (View). I've tried modifying the source in the xaml for Tool to:
- View.App.xaml
- View:App.xaml
- View/App.xaml
How can I get this reference to work, so I can share resources throughout my application? Thank you.
You can't load App.xaml like you're trying to do because it's not actually a ResourceDictionary. You can only specify ResourceDictionary files as the target of Source.
However, if you declare a resource in App.xaml, you can reference it anywhere without needing to load the file it's in. That's done for you automatically. Therefore, you can reference your converter at any time with {StaticResource PriorityToIconConverter}.
Note that if you moved it from the default starting location (the base project folder) you may have to update its location. Right click your project, then Properties. Navigate to the "Application" tab (should be the uppermost element on the left-hand sidebar) and look for the "Startup object" field. Set that to [ProjectName].[Namespace?].[Namespace?].App. When I tested it, mine worked without needing to manually change the location, but your setup may be different.

In XAML, specify an Icon from another assembly

I'm new to WPF and XAML. I have the following solution which WORKS using codebehind. But it seems to me to be something that should be easier to specify in XAML.
I want to set the Icon to a resource in another assembly (specifically, so the "FlowDecisionIcon" in System.Activities.Presentation). The following code works, when added to the constructor in the codebehind:
InitializeComponent(); // This was already there
ResourceDictionary dict = new ResourceDictionary { Source = new Uri("pack://application:,,,/System.Activities.Presentation;V4.0.0.0;31bf3856ad364e35;component/themes/icons.xaml") };
this.Resources.MergedDictionaries.Add(dict);
Icon = this.Resources["FlowDecisionIcon"] as DrawingBrush;
I was hoping there would be a nice way for me to specify this either in the XAML as something like:
<Window xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
Icon="sap:FlowDecisionIcon"> ...
Or, just specify it in Visual Studio in the Properties panel for the Icon property. But I've been unsuccessful in figuring out the syntax to do either.
It seems that you're in a Window control.
<Window Icon="{DynamicReosurce FlowDecisionIcon}"
.....
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ReosurceDictionary Source="pack://application:,,,/System.Activities.Presentation;V4.0.0.0;31bf3856ad364e35;component/themes/icons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>
But, it's advised to move the merging of the resources to the App.xaml file.

Silverlight: Access XAML control from code

I'm having a problem accessing a Panel control defined on the XAML of a page, the XAML is defined this way:
<UserControl
x:Class="PhoneBook.SilverlightMainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" Width="400" Height="300" d:DesignWidth="993" d:DesignHeight="887">
<Grid x:Name="LayoutRoot" />
</UserControl>
The class is defined like this:
public partial class SilverlightMainPage : UserControl
{
public SilverlightMainPage()
{
InitializeComponent();
}
}
And I'm trying to instantiate it this way:
var silverlightMainPage = new PhoneBook.SilverlightMainPage();
SomeMethod((silverlightMainPage.LayoutRoot);
What I find strange is that when I put the dot after the object instance, it actually list LayoutRoot as one of the members, but when I try to compile the application it says that there's no member with that name.
Any ideas of what can be hapenning?
Thanks
EDIT: I also tried creating a property on the SilverlightMainPage class that returned the LayoutRoot element, but it also says that the class doesn't contain a definition for Layout root.
Is there any chance that you're trying to access SilverlightMainPage.LayoutRoot from a different assembly? In the MainPage.g.i.cs file, LayoutRoot (and all other controls defined in XAML) are marked "internal", i.e.:
internal System.Windows.Controls.Grid LayoutRoot;
You might want to try creating a public rather than an internal property that does a FindName("LayoutRoot") and returns the appropriate control.
Actually I found the problem.
I was generating the project automatically with a tool built by someone else in the company.
I did some extra tests and added a new UserControl to the project and tried to access the LayoutRoot from a property in the code behind, and it worked.
Then copied the exact same code to the file with the problem (just changing the class name) and it didn't compile.
Then I checked the project file, and found a section like this:
<ItemGroup>
<Compile Include="SilverlightMainPage.xaml.cs">
<DependentUpon>SilverlightMainPage.xaml</DependentUpon>
</Compile>
</ItemGroup>
Which for some reason was causing the compilation to fail.
I removed that section and now everything works fine.
Thanks for your answers though.

wpf custom control not recognized

This is my first foray into custom controls, and it's not going well. I have a custom graph control derived from Canvas.
namespace Grapher2 {
public class SeriesManager : Canvas {
public SeriesManager() {
...
}
}
}
It's defined in the same project and namespace as my app. I tried adding a reference to the control in XAML as follows:
<Window x:Class="Grapher2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:graph="clr-namespace:Grapher2"
Title="Grapher" Width="800" Height="600">
<StackPanel Name="container" Width="700" Height="500">
<graph:SeriesManager Name="seriesManager" Width="700" Height="500" />
</StackPanel>
But when I try to reference the control name "seriesManager" in the code-behind for the Window, I get "The name 'seriesManager' does not exist in the current context."
Furthermore, the XAML editor will not render the Window, giving a huge stack trace with the error: "Type 'MS.Internal.Permissions.UserInitiatedNavigationPermission' in Assembly 'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' is not marked as serializable."
I imagine the solution is something stupidly simple for anyone who's done custom controls. But I'm stumped.
did you try x:Name="seriesManager" in your xaml?
Edit: This may not be the issue seeing how you said your xaml isn't rendering. I'm guessing once you get the xaml to render in the designer... the code behind will work better.
Edit 2: Whenever I've had a problem with the designer rendering, it's because I'm doing something in the constructor of my custom control. Check your SeriesManager to see if you are doing something in its constructor that is causing a problem. Maybe you are referencing something that doesn't exist yet. If you do have extra code in your constructor, consider moving it to the UserControl_Loaded event.
Backing up Scott's answer here, since he helped me solve it:
What I did wrong was trying to access the control BEFORE InitializeComponent(), but was confused by 2 other error messages somewhere else in the code.
Just in case someone else has this error.

Is it possible to set code behind a resource dictionary in WPF for event handling?

Is it possible to set code behind a resource dictionary in WPF. For example in a usercontrol for a button you declare it in XAML. The event handling code for the button click is done in the code file behind the control. If I was to create a data template with a button how can I write the event handler code for it's button click within the resource dictionary.
I think what you're asking is you want a code-behind file for a ResourceDictionary. You can totally do this! In fact, you do it the same way as for a Window:
Say you have a ResourceDictionary called MyResourceDictionary. In your MyResourceDictionary.xaml file, put the x:Class attribute in the root element, like so:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyCompany.MyProject.MyResourceDictionary"
x:ClassModifier="public">
Then, create a code behind file called MyResourceDictionary.xaml.cs with the following declaration:
namespace MyCompany.MyProject
{
partial class MyResourceDictionary : ResourceDictionary
{
public MyResourceDictionary()
{
InitializeComponent();
}
... // event handlers ahead..
}
}
And you're done. You can put whatever you wish in the code behind: methods, properties and event handlers.
== Update for Windows 10 apps ==
And just in case you are playing with UWP there is one more thing to be aware of:
<Application x:Class="SampleProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rd="using:MyCompany.MyProject">
<!-- no need in x:ClassModifier="public" in the header above -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- This will NOT work -->
<!-- <ResourceDictionary Source="/MyResourceDictionary.xaml" />-->
<!-- Create instance of your custom dictionary instead of the above source reference -->
<rd:MyResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
I disagree with "ageektrapped"... using the method of a partial class is not a good practice. What would be the purpose of separating the Dictionary from the page then?
From a code-behind, you can access a x:Name element by using:
Button myButton = this.GetTemplateChild("ButtonName") as Button;
if(myButton != null){
...
}
You can do this in the OnApplyTemplate method if you want to hookup to controls when your custom control loads. OnApplyTemplate needs to be overridden to do this. This is a common practice and allows your style to stay disconnected from the control. (The style should not depend on the control, but the control should depend on having a style).
Gishu - whilst this might seem to be a "generally not to be encouraged practice" Here is one reason you might want to do it:
The standard behaviour for text boxes when they get focus is for the caret to be placed at the same position that it was when the control lost focus. If you would prefer throughout your application that when the user tabs to any textbox that the whole content of the textbox was highlighted then adding a simple handler in the resource dictionary would do the trick.
Any other reason where you want the default user interaction behaviour to be different from the out of the box behaviour seems like good candidates for a code behind in a resource dictionary.
Totally agree that anything which is application functionality specific ought not be in a code behind of a resource dictionary.
Adding on....these days, with the advent of {x:Bind ...}, if you want to put your DataTemplate into a shared ResourceDictionary file, you are required to give that file a code behind.
XAML is for constructing object graphs not containing code.
A Data template is used to indicate how a custom user-object is to be rendered on screen... (e.g. if it is a listbox item) behavior is not part of a data template's area of expertise. Redraw the solution...

Resources