Xaml: Finding a control defined within ControlTemplate from C# at ResourceDictionary level - c#

Library.xaml
<ResourceDictionary>
<ControlTemplate x:Key="test" TargetType="{x:Type Button}">
<Button Width="200" Height="40" Name="my_custom_btn" Click="r_Click_1" Style="{StaticResource button_style}">Button</Button>
</ControlTemplate>
</ResourceDictionary>
I want to access this Button element defined within my ControlTemplate from my C# file when template is not applied in Main.xaml.
I have tried all methods
Template.FindName, Application.Current.Resource, Control.GetTemplateChild(), using VISUALTREE HELPER search but i am not getting this element in my c# code.
As far as I know these methods are used to find elements when a control template which is defined somewhere in Library.xaml is loaded in Main.xaml file by giving its TargetType.
But what if this template is not applied and Still I want to access its element from my code.
I have looking for a proper solution for a past few days. plz help!!
Please explain it fully as I am new to WPF..

Related

How to add a svg/xaml file in C# WPF windows just like image?

How to add a .svg file in a WPF window in C# as an image (,png || ,jpg)?
I use the code
<Image HorizontalAlignment="Left" Height="53" Margin="34,39,0,0"
VerticalAlignment="Top" Width="71"
Source="Test.svg" Name="MyImage"/>
But I get an error:
Blend does not support format svg.
I found that I could change the .svg file into a .xaml file. But I still do not know how to add the xaml as an image.
Based on an answer, I changed my code like this:
<Window x:Class="NIA_UI_Demo_CSharp.ShareDocumentsWin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ShareDocumentsWin" Height="350" Width="569">
<ResourceDictionary>
<Style x:Key="TheAwesomeXAMLimage" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
my code
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<Grid Margin="0,0,2,3">
<ContentControl Style="{StaticResource TheAwesomeXAMLimage}"/>
</Grid>
</Window>
But I get an error:
Content is set more than once;
As far as I know you cannot include svg-files directly.
Two options:
use library that can handle svg-files in runtime: https://sharpvectors.codeplex.com/ (moved to https://github.com/ElinamLLC/SharpVectors)
convert the svg to xaml and use them with native wpf objects (Path, Image..)
I prefer the second option, so I wrote a tool which can convert a single svg to xaml and can also batch convert a bunch of svg-files. The workflow is: just put the svg-file to your images-folder, call the batch-converter and find the images.xaml file (a resource-dictionary) updated with the new icons/images.
See https://github.com/BerndK/SvgToXaml
I was lucky that I have DevExpress available where you can use WpfSvgRenderer.CreateImageSource. Don't want to advertise here, but since it's a widely used library, probably some are happy to know.
Unfortunately text element inside the svg is not supported yet.

FindResource not working in Custom Control DLL

While I am still learning WPF I have yet run into another problem. I have a DLL which is of type Custom Control. I've implemented my base control and I have several controls which derive from this base; the base control is never used. The problem is whenever I call FindResource or TryFindResource it always fails. I have a separate dictionary that I merged in my Themes/Generic.xaml file:
Gernieric.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/DllProject;component/Themes/NewResource.xaml" />
<!-- I've tried several other ways for the Source format, i.e. pack:... -->
</ResourceDictionary.MergedDictionaries>
Now I am trying to set the Style of my derived controls in the derived controls constructor without the control being on a visible canvas or panel at the time. I also want to export a VisualBrush of the control as a BitmapSource. All of the code was working when I put my NewResource.xaml in the EXE project (where it doesn't belong). I've read articles on adding a dummy tag to the resource dictionary as well as put all of my code in the Generic.xaml file. Like I said all of this works fin if move the xaml file to the main EXE. Its as if the DLL isn't even loading the xaml file or even aware there is anything declared in it until the control is on a visible window.
I have a style for my base control (NewResource.xaml):
<Style TargetType="{x:Type local:MyDerivedControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyDerivedControl}">
<Grid>
<Path x:Name="MyPath" Style="{TemplateBinding DepProp}" />
<TextBlock x:Name="Text" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Text}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
After doing a lot of research on the internet it seems WPF dropped the ball on DLLs and resources.
I found the answer that worked. Basically change the x:Key to contain a ComponentResourceKey. A full description can be viewed here that is straight forward to understand.

WPF Using images in generic.xaml template

I am writing a style for a custom control derived directly from Control. Visual Studio places the style for a "Custom Control (WPF)" in the Themes\generic.xaml file. My style contains an image which I can't get displayed, seems there's something special about how to set the Source for an image from within the generic.xaml file.
I managed to reproduce the issue with a simpler scenario. Create a "WPF Custom Control library" then add a style for buttons like so, in the themes\generic.xaml . Here's my complete generic.xaml:
<ResourceDictionary
...
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
After this I have create a UserControl (in the same project) containing just a button (for the sake of testing out the style) like so:
<UserControl x:Class="BlendControls.UserControl1"
...
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button/>
</UserControl>
I have added the SmallHandle.png in the root project directory, in the themes directory, I have added it also to the good old Resources page, tried changing the build action to resource, embedded resource, tried copying the image manually to the build directory, but to no effect. The image is never displayed.
This must be related to the generic.xaml file, because copying the entire style to the same file where the Button is placed works fine. That is, the following works as expected:
<UserControl x:Class="BlendControls.UserControl1"
...
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button></Button>
</UserControl>
So, how should I set the Source of images from generic.xaml? Or alternatively, where should I put the styles/templates for custom controls?
---- Solution ----
As pointed out by Sheridan, I have to use the "full" pack URI notation as:
pack://application,,,/MyAssembly;components/SmallHandle.png
This looks strange to me, as the image is in the same assembly. Not sure, looks like I am referencing from outside the dll.
There's nothing unusual about accessing an image in Generic.xaml, you're just not referencing it correctly. You can reference a resource file in the project assembly using this format:
<Image Source="/AssemblyName;component/Subfolder/SmallHandle.png" />
If your images are directly inside the project root (which is not recommended), then you can access them like this:
<Image Source="/AssemblyName;component/SmallHandle.png" />
If your images are in a folder in another project, then you can access it like this:
<Image Source="/ReferencedAssembly;component/Subfolder/SmallHandle.png" />
See the Pack URIs in WPF page on MSDN for more information.
UPDATE >>>
In .NET 4, the above Image.Source values would work. However, Microsoft made some horrible changes in .NET 4.5 that broke many different things and so in .NET 4.5, you'd need to use the full pack path like this:
<Image Source="pack://application:,,,/AssemblyName;component/Images/image_to_use.png">
If you don't feel as though your generic.xaml is being picked up, you can reference it from your App.cs.xaml like this:
<App.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MY.NAMESPACE;component/Themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</App.Resources>
Your generic.xaml file should be marked as "Resource".
Also, your image file should be marked as "Resource".
Finally, reference your ImageSource like this:
<Image Source="Themes/IMAGE.png" />
or try
<Image Source="../Themes/IMAGE.png" />
Personally, I like to put my style templates in their own .xaml file, and reference them all as MergedDictionaries.
Typed base style in Themes\Generic style is automatically applied only to Custom Control.
If you need use typed based style in your user control you need add generic.xaml to user control resources.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Also change Image Source URI to
<Image Source="pack://application:,,,/WpfCustomControlLibrary1;component/SmallHandle.png" />

What's the correct way to create a custom control

Edit (as commented: XY-Problem) - Problem:
I want to create my own control which has predefined styles and positions for special elements (Button,...), but in general everything should be able to be placed inside my custom control. The custom control in my case is just a "menubar" which should be able to be used anywhere in the "GUI code" - but there is no need it has to be there. But when it is used it should be the same style and behavior everywhere. A style is - I think - not enough, because there are also predefined elements in this menubar (e.g. Help is already in menubar)
Edit end.
I want to build a custom control (just a special stackpanel) in WPF with the following requirements:
can be used as any other control within a xaml
has defined styles for controls within the custom control
First I simply tried to create a UserControl containing a stackpanel with defined styles (in the xaml) for containing elements (e.g. Button). This UserControl contained the
<ContentPresenter />
in the xaml. With this method it is not possible to name the containing elements. E.g.:
<mynamespace:MyStackPanel>
<Button Name="w00t">This does not work!</Button>
</mynamespace:MyStackPanel>
Next try was to create a "real" custom control. This custom control is just a class without the xaml. Code is very simple. Class inherits from UserControl and just contains:
StackPanel sp = new StackPanel();
sp.Children.Add(new ContentPresenter());
this.AddChild(sp);
Woooohoooo, now it's possible to name the containing elements. But still a big problem: How to define the styles?
I could define the style for my very own custom control in a ResourceDictionary. But i have to add the ResourceDictionary to the global (App.xaml) Resources. And then I can define styles only for my custom control - not for the containing elements? - But anyway... doing it like this just feels wrong!
So the main question is: WHAT is the "correct" way of creating a custom control which can be used in xaml like any other control? If the second way is the correct way - how is it possible to set the style like I do it in a xaml (e.g. every Button in this element has a special style) and has it to be a "global" ResourceDictionary?
How is it implemented in third-party stuff?
Ok I made an example for you, which involves Custom Controls (as Opposed to UserControls)
Step 1:
Create a new class (code only, no XAML) derived from ContentControl (or whatever UI element that has a behavior similar to what you need)
public class ReusableContainer : ContentControl
{
public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register("Button", typeof(Button), typeof(ReusableContainer), new PropertyMetadata(default(Button)));
public Button Button
{
get { return (Button)GetValue(ButtonProperty); }
set { SetValue(ButtonProperty, value); }
}
}
See how I'm defining the Button property as a DependencyProperty here. You can add more DPs for whatever "content placeholders" that you need in your custom control.
Step 2:
Have your predefined Styles for the UI elements inside the container in a separate ResourceDictionary:
CustomStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button">
<Setter Property="Background" Value="Green"/>
</Style>
</ResourceDictionary>
Step 3: in app.xaml, define an application-wide style for the ReusableContainer, which defines it's template:
<Application x:Class="WpfApplication14.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="{x:Type local:ReusableContainer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ReusableContainer}">
<ControlTemplate.Resources>
<ResourceDictionary Source="CustomStyles.xaml"/>
</ControlTemplate.Resources>
<ContentPresenter Content="{TemplateBinding Button}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>
See how I'm using the TemplateBinding expression to define that the ContentPresenter's content is going to be defined by the Button property in the ReusableContainer.
Also notice how I'm Adding the Resources in CustomStyles.xaml to the ControlTemplate.Resources collection. This makes these resources available to all UI elements inside the Template.
Step 4:
Place your ReusableContainer in a Window:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button1" Content="Hello! Button 1"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button2" Content="Hello! Button 2"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
<local:ReusableContainer>
<local:ReusableContainer.Button>
<Button x:Name="Button3" Content="Hello! Button 3"/>
</local:ReusableContainer.Button>
</local:ReusableContainer>
</StackPanel>
</Window>

WPF XAML Global Reference

Is there a way to reuse a 3rd party control reference?
For example, I have this referenced in my App.xaml
xmlns:cust="clr-namespace:ThirdParty.Controls;assembly=ThirdParty.Controls"
I don't want to repeat this 3rd party control xml namespace on each page/control that needs a control from the library.
Is there anyway to centralize these references and use the prefix defined here? The possibility of each control having a different prefix is also worrisome. In asp.net you would put a reference in the web.config and it was available globally, I'm just looking to see if there is a similar method in WPF.
Two options I am thinking
1) Wrap that control into a UserControl and then use your UserControl in all the places.
2) Declare the third party control as a Resource somewhere and then use DynamicResource reference to that on your other places.
The second option can be implemented as bellow.
Where ever you want the third party control put a ContentControl like bellow
<ContentControl Template="{DynamicResource thirdPartyControlTemplate}" />
The ControlTemplate will be in the Resource file or at App.Xaml as bellow.
xmlns:thridParty="clr-namespace:WpfCustomControlLibrary1;assembly=WpfCustomControlLibrary1" >
<Application.Resources>
<ControlTemplate x:Key="thirdPartyControlTemplate" TargetType="{x:Type ContentControl}">
<thridParty:ThirdPartyControl />
</ControlTemplate>
</Application.Resources>
You can see that the namespace declaration will be always on this resource file and you will be able to use that Control control from any place
Take an example from the built-in control template for a Label:
<ControlTemplate TargetType="Label"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
Show Me The Template is an amazingly helpful resource for these kinds of things. HTH

Categories

Resources