I've defined a FlowDocument in a WPF control library (Add New Item..., FlowDocument -- is the root element of the file). I intend this to be used in several contexts such as in a user control or on a window, referenced in code for data-binding and export to xps, etc. But I can't figure out how to get a reference to an instance of this document. It doesn't seem to create an object in the compiled assembly.
More specifically, this is my problem
<MyUserControl ........ >
<FlowDocumentScrollViewer>
<!-- doesn't work --><namespaceRef:MyFlowDocument />
<FlowDocumentScrollViewer>
</MyUserControl>
The easiest solution is probably to put your FlowDocument inside a Resource Dictionary and then use the x:Key like this
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<FlowDocument x:Key="myFlowDocument" ..>
</FlowDocument>
</ResourceDictionary>
<FlowDocumentScrollViewer Name="flowDocumentScrollViewer">
<StaticResource ResourceKey="myFlowDocument"/>
</FlowDocumentScrollViewer>
Otherwise you'll have to set the FlowDocument to build action Embedded Resource and lode it in code behind with something like this
Stream flowDocumentStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("DocumentNameSpace.FlowDocumentName.xaml");
FlowDocument flowDocument = (FlowDocument)XamlReader.Load(flowDocumentStream);
flowDocumentScrollViewer.Document = flowDocument;
Update
I think it might be possible to use an ObjectDataProvider to load the FlowDocument if you want to look into it. Anyway, a ResourceDictionary seems like the easy way out.
Related
Having the following very simple xaml:
<DocumentViewer Name="dv">
<FixedDocument Name="fd" Loaded="fd_loaded">
<FixedDocument.Resources>
<Style x:Key="TestStyle">
<Style.Setters>
<Setter Property="TextBlock.Foreground" Value="BlueViolet"/>
</Style.Setters>
</Style>
<SolidColorBrush x:Key="foregroundBrush" Color="Orange"/>
</FixedDocument.Resources>
<PageContent Name="pc">
<FixedPage Name="fp" Width="800" Height="600" Name="fp">
<TextBlock Name="tb" Style="{DynamicResource TestStyle}">
Lorem ipsum
</TextBlock>
<TextBlock Foreground="{DynamicResource foregroundBrush}" Margin="20">
Lorem ipsum
</TextBlock>
</FixedPage>
</PageContent>
</FixedDocument>
</DocumentViewer>
The use of Dynamic Resources (which I actually need in a more complex situation) here doesn't work. Using Static Resources colors the TextBlocks in the desired colors. Moving the Resources to the level of the FixedPage also does the trick. But I would like to have one generic resource dictionary on a top level element (because of runtime changes the user can make for colours, fonts, etc.). Placing the resources on Application level also does work. But it's not an option for good reasons.
Anybody have any clue why this doesn't work. Does it have something to do with the Logical Tree from the TextBlock upwards?
MSDN Resources Overview states that:
The lookup process checks for the requested key within the resource dictionary defined by the element that sets the property.
If the element defines a Style property, the Resources dictionary within the Style is checked.
If the element defines a Template property, the Resources dictionary within the FrameworkTemplate is checked.
The lookup process then traverses the logical tree upward, to the parent element and its resource dictionary. This continues until the root element is reached.
I also tried putting the Brush and the Style into the Resources of a (dummy) Style according to the above explanation of MSDN. But that didn't work either.
Really have the feeling that this can not be that complex, but most probably I oversee something. Any help is appreciated.
EDIT
When naming the TextBlock to "tb" and then using tb.FindResource("TestStyle") throws an exception. So that resource clearly can't be found. If I check out LogicalTreeHelper.GetParent(tb) and repeat that for the parents found I get the expected result: TextBlock > FixedPage > PageContent > FixedDocument ...
EDIT2
This works perfect. What's the difference with the XAML projected earlier?
<Window x:Class="WpfDynamicStyles2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="resBrush" Color="Orange"></SolidColorBrush>
</Grid.Resources>
<StackPanel>
<Button>
<TextBlock Foreground="{DynamicResource resBrush}">Dummy text...</TextBlock>
</Button>
</StackPanel>
</Grid>
</Window>
EDIT3
private void fd_Loaded(object sender, RoutedEventArgs e)
{
Object obj = pc.TryFindResource("foregroundBrush");
obj = fp.TryFindResource("foregroundBrush");
obj = tb.TryFindResource("foregroundBrush");
}
The dynamic resource placed on the Foreground property of the textbox cannot be resolved (the actual resource is at the FixedDocument.Resources level). Also using the TryFindResource in code behind works from pc (PageContent) but from fp (FixedPage) and tb (TextBlock) it cannot resolve the resource (obj is null). When using a Static Resource in the XAML Markup everything works fine.
Well going the style way is a good design but not necessary. To make dynamic color based same named (keyed) brushes, we can use as dynamic color dictionaries (NOT brush dictionaries)
The solution can be as below ...
Create a single brush resource dictionary.
Refer the color in the brush with DynamicResource attribute.
Create multiple resource dictionaries with same keyed Color resource in each of them them.
Based on user's requirement, clear and add into Application.Current.resources.MergedDictionaries.
Example
Make a WPF project with a window that has following XAML....
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Window11Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<DockPanel LastChildFill="True">
<ComboBox DockPanel.Dock="Top" VerticalAlignment="Top"
SelectionChanged="ComboBox_SelectionChanged"
SelectedIndex="0">
<ComboBox.ItemsSource>
<Collections:ArrayList>
<System:String>Orange</System:String>
<System:String>Red</System:String>
<System:String>Blue</System:String>
</Collections:ArrayList>
</ComboBox.ItemsSource>
</ComboBox>
<DocumentViewer>
<FixedDocument>
<PageContent>
<FixedPage Width="793.76" Height="1122.56">
<TextBlock
FontSize="30"
Foreground="{StaticResource LabelColorBrush}"
Text="Test"/>
</FixedPage>
</PageContent>
</FixedDocument>
</DocumentViewer>
If you observe the window doesnt need to use anything which is dynamic. All refernces can remain static. So LabelColorBrush can be found in dictionary Window11Resources.xaml
In Window11Resources.xaml dictionary add a dynamic color brush.
<SolidColorBrush x:Key="LabelColorBrush"
Color="{DynamicResource DynamicColor}"/>
Add following 3 color brush dictionaries in some folder from your project...
<!-- Name = OrangeColorResource.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="DynamicColor">Orange</Color>
</ResourceDictionary>
<!-- Name = BlueColorResource.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="DynamicColor">Blue</Color>
</ResourceDictionary>
<!-- Name = RedColorResource.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="DynamicColor">Red</Color>
</ResourceDictionary>
Note that the key remains the same i.e. DynamicColor.
Now clear and recreate color dictionaries in App.Resources. I have done that in the code behind of Window.xaml.cs
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(
new ResourceDictionary()
{
Source = new Uri("Resources\\"
+ ((ComboBox)sender).SelectedItem.ToString()
+ "ColorResource.xaml",
UriKind.RelativeOrAbsolute) });
}
Now as and when you select a color from the drop down, the dynamic color changes on the static resource brush. Note that there is no Style involved.
I hope this answers what you are asking.
By the way: the reason for this post had some more complex background. I really wanted to use a single resource dictionary whereby I could change colors, fonts, fontsizes, etc. dynamically during runtime (by an end-user). I am working on a tax form application. And the tax forms presented on screen for user input are resolving their resources at the Application level. So far so good.
But at the same time I want to present the user a print preview of the tax form where the same resourcedictionary (as object type, not as object instance) is used to define color schemes, fonts, fontsizes, etc. to be used for printing. Which can differ from the styling used for on screen user input. That's why I wanted to "attach" the resource dictionary on (for example) the FixedDocument level so that all pages in the document would refer to it. And when changing colors, fonts etc. all pages with common elements (TextBlocks, TextEditors, etc.) would respond to the change.
And then I became stuck...as described in this post.
For now I have a nice workaround by creating a specific instance of the resourcedictionary, storing it as a private variable and attach it to every individual page I put in the fixed document. It works and that already pleases me. But my programmers heart is still a little ached, because I would prefer to use a single instance of the resource dictionary and put it at some top level control so all controls in it can use it.
As a programmer we have to live with workarounds as well ;) If you have any further suggestions, I'm open to receive them. Thanx for your support thus far.
Greetz, Joep.
Also see: MSDN Forum Post about this issue
I have a a IMultiValueConverter that dynamically creates TextBlock controls. The issue is that it has no styles.
How can I tell my new TextBlock to use a style that was defined in my XAML resource dictionary?
See the following question: how to use DynamicResource in the code behind?
Use SetResourceReference, it's equivalent to use DynamicResource in Xaml
So if your Style has the Key myTextBlockStyle
TextBlock textBlock = new TextBlock();
textBlock.SetResourceReference(FrameworkElement.StyleProperty, "myTextBlockStyle");
I have never tried this before, and depending on what your converter is doing, I think if your XAML resource dictionary is external, then link it into the Window where you are displaying the TextBlocks:
<Window.Resources>
<ResourceDictionary Source="[the path to the resource dictionary]"/>
</Window.Resources>
Then in your textblocks, ensure they have the Style attached that is defined in the resource dictionary. If the textblocks are being created in code behind I believe you should be able to use FindResource to locate the style that is linked in by the resource dictionary. Then do something like this:
textBlock1.Style = (Style)FindResource("myTextBlockStyle");
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.
I always have problems making new prefixes in xaml. Most of the time, i get the message that the URI cannot be found in the assembly. My setup:
I have a WPF project (in a solution with class libs and asp.NET projects) with a MainWindow.xaml file. The XAML starts with : Window x:Class="MainWindow" ... .
So as default, there's no namespace given to it. In that same project i made a folder "Folder". In that folder, i have resx-files. What i need to do is make a prefix in xaml so i can address those files. I was thinking of :
xmlns:p="clr-namespace:WpfApplication.Folder"
and then for my controls
<Label Content="{x:Static p:NameResxFile.KeyName></Label>
However, the prefix generates the "URI cannot be found in the assembly" error. I'm i just failing at making prefixes?
Thanks in advance.
EDIT
If you cannot make a namespace ref to a folder, what is happening here?
xmlns specifies namespaces, it does not bother with folders or files, if you need access to an external resource you can load it into your control's resources via ResourceDictionary.
There was something like this i think:
<Window.Resources>
<ResourceDictionary x:Key="ExternalRes" Source="Folder/File.xaml"/>
....
</Window.Resources>
To reference an element of resource dictionary you should add that dictionary to your control's Resources collection or register it in the App.xaml file. After that you could just use StaticResource extension to get access to the element. Your code will look like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Folder/NameResxFile.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<!-- ... -->
<Label Content="{StaticResource KeyName}"/>
Things get trickier if you want to put the resource dictionary to another assembly and reference it. For this purpose refer to PackURIs in WPF article.
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...