Relative Path not resolved in Template Binding WPF - wpf

I am trying to create an Image Button in WPF. What I have done is
Create a user control inherited from Button and declare a dependency property in it which supposed to have the required image.
In resource dictionary declare the xaml template for it.
Pass relative and full path to that dependency property, full path works but relative not.
User Control Class
public class ButtonWithImage : Button
{
public ImageSource ButtonImage
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonImageProperty =
DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(ButtonWithImage));
}
Resource Dictionary Code
<Style x:Key="ButtonWithImageStyle" TargetType="complaintRegister:ButtonWithImage">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="complaintRegister:ButtonWithImage">
<StackPanel>
<Image Source="{Binding ButtonImage, RelativeSource={RelativeSource TemplatedParent}}" Width="50" Height="50"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
XAML
<complaintRegister:ButtonWithImage x:Name="ButtonAdd" Content="Add New Complaint" Style="{StaticResource ButtonWithImageStyle}"
ButtonImage="Resources\Plus.png"
Width="150" Height="75" Margin="10 0" Command="{Binding NewCommand}">
</complaintRegister:ButtonWithImage>
Error
It does displays the image in design mode, but at runtime throws this exception
Cannot locate resource 'resources/plus.png'
I can't help but thinking it is trying to resolve 'resources/plus.png' instead of 'Resources/Plus.png.

I bet there is a binding error displayed in output window. :)
How about you post us that error message here.
Btw, in such cases its more propriate to use TemplateBinding instead of RelativeSource TemplatedParent.
TemplateBinding is lets say it like this, specified to work well in control's template. :)
Check those links out:
http://msdn.microsoft.com/en-us/library/ms742882(v=vs.110).aspx
http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate
Edit :
Ahhh you are talking about location of your image.
By default, the Image control only recognize compiled resource image when using XAML to specify its Source property. But, we can use converter or custom markup extension to achieve this goal. The following links contain information about data binding converter and markup extension.
Therefore set that png to build action -> resource and rebuild your solution or use this:
<Image>
<Image.Source>
<BitmapImage UriSource="../Relative/Path/To/Image.png" />
</Image.Source>
</Image>
Though if you wish to use MVVM on this and change path based on data I suggest you to use Bindings:
<Image Source="{Binding MyPath}" Height="50"... />

Related

TemplatedParent Binding in Resources of ControlTemplate

here is my scenario: I have a control template for a custom control which is rather big. To encapsulate the code of one of the features of that custom control, I'd like to introduce a helper class (called Internals). This class has logic code and provides some properties, which should be used in Bindings within the ControlTemplate.
Therefore I need to create an instance of this class in XAML and bind the TemplatedParent to a dependency property of the Internals class. My problem now is a concrete binding to the object the ControlTemplate is applied on. I've created a little proof of concept:
MainWindow.xaml:
<Window x:Class="WpfProofOfConcept.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfProofOfConcept="clr-namespace:WpfProofOfConcept"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Height="650"
Width="525">
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.Resources>
<wpfProofOfConcept:Internals x:Key="InternalsKey"
Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />
</Grid.Resources>
<Grid.RenderTransform>
<RotateTransform Angle="20" />
</Grid.RenderTransform>
<!-- if uncommented, Binding to Base is working -->
<!--<wpfProofOfConcept:Internals Base="{Binding Path=., RelativeSource={RelativeSource TemplatedParent}}" />-->
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button>
<TextBlock>Some text</TextBlock>
</Button>
</Grid>
</Window>
Internals.cs:
public sealed class Internals : FrameworkElement
{
public static readonly DependencyProperty BaseProperty =
DependencyProperty.Register("Base", typeof(object), typeof(Internals), new PropertyMetadata((o, s) => { }));
public object Base
{
get { return (object)GetValue(BaseProperty); }
set { SetValue(BaseProperty, value); }
}
public Internals()
{
}
}
I need a reference to the concrete object the Template is applied on in my Internals object - thus the TemplatedParent binding. It does not work and I do not get any binding errors in the output.
The strange thing is that it works when the Internals object is created outside of the resources section (see commented lines in the XAML code). I do not
And one more thing thats keeps me confused: In Silverlight, binding to the TemplatedParent within a resource section works. This seems to be a WPF issue.
Do you have any ideas how to get this binding done? Or can you explain why TemplatedParent binding does not work in the resources section?
Thanks for all your hints!
It is possible to make Bindings from the Resources! But the object in resources has to be a freezable object, e.g. its class has to extend Freezable. For more information, have a look at this link from Dr. WPF http://drwpf.com/blog/2008/05/22/leveraging-freezables-to-provide-an-inheritance-context-for-bindings/:
In addition to the above scenario (where a freezable is set as a
dependency property value on a dependency object), it is this enhanced
notion of an inheritance tree and inheritance context that allows
bindings on brushes, animations, and other freezable objects to work
when those objects are placed within a resource dictionary.
You have to change the class implementation to:
public sealed class Internals : Freezable
Grüße,
Peter ;)
When you are creating Internals as Resource, it is not the part of the VisualTree and hence binding is not getting applied to it
Once your define it inside the Grid, it become the part of VisualTree.

Setting Properties a Control in xaml

I am creating a custom WPF control that uses an image inside of it. This custom control will be like any other, it will be declared in xaml. I want to have a public property for this control to specify the source of the internal image, much the same way you do this when using an Image control:
<Image Source="http://foo.com/bar.jpg"></Image>
What I want to do is have the following usage of my control:
<MyCustomControl ImageSource="http://foo.com/bar.jpg"></MyCustomControl>
And then internally, something like:
<UserControl class="MyCustomControl" ...>
<Image Source="{Binding Imagesource}"></Image>
</UserControl>
What kind of setup do I need in my codebehind to get this to work? i've tried a few things but can get nothing to work.
What you need is a dependency property of type ImageSource and a proper binding, either use ElementName or RelativeSource, do not use the DataContext on UserControls.
<UserControl Name="control" x:Class="MyCustomControl" ...>
<Image Source="{Binding ImageSource, ElementName=control}"/>
</UserControl>
<UserControl x:Class="MyCustomControl" ...>
<Image Source="{Binding ImageSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>

Custom WPF tooltip

I want to create a WPF tooltip containing a label for the header of the tooltip and then a textblock containing more detailed text. I've created the following style in a resource dictionary:
<Style x:Key="AppToolTip"
TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<StackPanel>
<Label Content="{TemplateBinding Content}" FontWeight="Bold" Background="Blue" Foreground="White">
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value></Setter>
</Style>
And can successfully apply this style to a button like so and have the tooltip header appear:
<Button.ToolTip>
<ToolTip Style="{DynamicResource PalletToolTip}">
<Binding Source="{x:Static ResStrings.New}"/>
</ToolTip>
</Button.ToolTip>
What i'm stuck on is how can i set the content of the extra descriptive text from the usage above ? I'm already data binding to the Content property when showing the tooltip header.
Anyone who's read Adam Nathan's WPF Unleashed book will recognise that i'm using his example tooltip XAML but in his case, he's used hard coded strings for the content of the label and textblock. I want to create something that's more reusable and hence want to use data binding to achieve the same effect.
I would inherit a HeaderedToolTip class from ToolTip and add a Header property. I would specify the template for that control much as you've done. Then I would be able to use it like so:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="My Title" Content="My Content"/>
</Button.ToolTip>
</Button>
Or, with bindings:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="{Binding ToolTipTitle}" Content="{Binding ToolTipText}"/>
</Button.ToolTip>
</Button>
You can use an object or ViewModel that contains all the necessary properties you need in the tooltip.
class MyToolTipViewModel : INotifyPropertyChanged
{
public string Header
{
get{ return mHeader;}
set{ mHeader = value; RaisePropertyChanged("Header"); }
}
public void RaisePropertyChanged(string aProperty)
{
// .. implementation of INotifyPropertyChanged
}
}
then you can set the tolltip directly on an instance of this class.
myButton.ToolTip = new MyToolTipViewModel();
now after that, your tooltip will just show the full qualified name of the ViewModel class.
What you need now is a DataTemplate, which tells WPF how to convert the class into visual object.
<DataTemplate DataType="{x:Type MyToolTipViewModel}">
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
The DataTemplate need to be placed in the resource tree. In the resource section of a higher level object or directly on the application or window resources level.
Hope that helps.

How to bind to parent position changes in WPF using XAML and no code behind

I have a visio-like interfact but have actual model data behind some of the elements. The elements can be moved by the user.
I use a contentcontrol on a canvas whereby the viewmodels of the elements are places in the content which can then be displayed differently depending on their type but using the same contentcontrol. It is simple to bind the view to the different properties in the viewmodel. However, I have to save the position in the model, and I cannot find a binding solution.
1) The Application.Save Command is handled in the main view model, so I do not have access to the view there. That means I must save the postion data when the elements are moved, or is there a better approach?
2) Assuming that I am right with 1), I am looking to avoid code behind, i.e. I do not want the contentcontrol to deal with the elements that they have in their content. However, so far the code behind version is all I could come up with:
My code behind solution so far:
All model elements implement an interface:
public interface IViewElement
{
String Position { get; set; }
}
And in the contentcontrol:
void ContentControl_LayoutUpdated(object sender, EventArgs e)
{
IViewElement content = this.Content as IViewElement;
content.Position = new Point(Diagram.GetLeft(this), Diagram.GetTop(this)).ToString();
}
The XAML:
<Style TargetType="{x:Type diagram:Item}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type diagram:Item}">
<Grid Canvas.Top="{Binding ElementName=PART_ContentPresenter, Path=Content.Position, Mode=TwoWay}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{x:Null}">
<!-- PART_ContentPresenter -->
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<DataTemplate DataType="{x:Type local:ViewModel}">
<StackPanel>
...
</StackPanel>
Just encapsulate the codebehind you've used in a Behavior
Why are you using a string to store the position? Use either a Point or two decimal values, and then bind your ContentControl's Canvas.Top and Canvas.Left position to these values using two-way binding.
It will automatically update the model when the Top and Left positions change.
Edit:
Here's an example:
<ContentControl Canvas.Top="{Binding ContentModel.Top, Mode=TwoWay}"
Canvas.Left="{Binding ContentModel.Left, Mode=TwoWay}"
Content="{Binding ContentModel}" />

How to use template binding inside data template in custom control (Silverlight)

I am trying to create control which will take ItemsSource and InnerTemplate and will show all the items wrapped in CheckBoxes.
The control has 2 dependency properties:
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CheckBoxWrapperList), null);
public static readonly DependencyProperty InnerTemplateProperty = DependencyProperty.Register("InnerTemplate", typeof(DataTemplate), typeof(CheckBoxWrapperList), null);
and here is the template:
<ControlTemplate TargetType="local:CheckBoxWrapperList">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="wrapper">
<CheckBox>
<ContentPresenter ContentTemplate="{TemplateBinding InnerTemplate}" Content="{Binding}" />
</CheckBox>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemTemplate="{StaticResource wrapper}" ItemsSource="{TemplateBinding ItemsSource}" />
</Grid>
</ControlTemplate>
However, this approach does not work.
Binding in the ControlPresenter.ContentTemplate using TemplateBinding does not work.
However, when I don't use template binding and reference the template as static resource, then it works as expected.
Why cannot I use the template binding inside the content presenter in datatemplate?
What am I missing here? Any special markup required?
Is there a way to achieve the expected behavior?
Thanks in advance.
Silverlight and WPF
You can get around this with a relative source binding:
Instead of:
{TemplateBinding InnerTemplate}
You would use:
{Binding RelativeSource={RelativeSource AncestorType=local:CheckBoxWrapperList}, Path=InnerTemplate}
It's a bit messier but it works.
WinRT
WinRT doesn't have AncestorType. I've got something that works but it's kind of horrifying.
You can use an attached property to store a TemplateBinding value and then access it using ElementName...
<ControlTemplate TargetType="local:CheckBoxWrapperList">
<Grid x:Name="TemplateGrid" magic:Magic.MagicAttachedProperty="{TemplateBinding InnerTemplate}">
<Grid.Resources>
<DataTemplate x:Key="wrapper">
<CheckBox>
<ContentPresenter ContentTemplate="{Binding ElementName=TemplateGrid, Path=(magic:Magic.MagicAttachedProperty)}" Content="{Binding}" />
</CheckBox>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemTemplate="{StaticResource wrapper}" ItemsSource="{TemplateBinding ItemsSource}" />
</Grid>
</ControlTemplate>
I don't know if there's a better way for WinRT.
TemplateBinding can only be used within a ControlTemplate, you're using it within a DataTemplate. (The fact that the DataTemplate is within a ControlTemplate doesn't matter)

Resources