How to bind a image source in ControlTemplate from Code behind? - wpf

I need to bind the image source that defined in code behind into ControlTemplate. I have problem making it work correctly. I am wondering if someone can help.
The image is in ControlTemplate of the Button style. I instantiate that Button style in multiple XAML pages. My goal is to define 'Icon' instance in each code behind of these xaml pages to place the icon that is unique for each XAML page. Perphaps, it can be a better way to define unique icons that should be displayed in each XAML page where the button style will be loaded.
Below is the link to the solution I've created. It is a simple sample where I have 2 xaml pages with the buttons using the same style. I need to be able to load icon1.png into the button while mainWindow.xaml is opened and icon2.png when Page1.xaml is opened.
http://cid-0c29483cf3a6a14d.office.live.com/self.aspx/WPF%5E_Tests/BindingImageFromCode.zip

I see a couple of solutions to your problem. You'll never know what the parent of your Button really is so a code behind Binding to a property for Window, Page etc. will be hard to accomplish without to much hardcoding.
Approach 1
Subscribe to the Loaded event for the Button, find the Image in the Button Template with FindName and set the Source from there. This can also be done with an Attached Behavior
Xaml
<Button Style="{DynamicResource ButtonStyle1}"
Loaded="Button_Loaded"
...>
Code behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
Image imgIcon2 = button.Template.FindName("imgIcon2", button) as Image;
Uri uri = new Uri("Resources/icon1.png", UriKind.Relative);
ImageSource imgSource = new BitmapImage(uri);
imgIcon2.Source = imgSource;
}
Approach 2
Create a subclassed Button called e.g ImageButton where you add a new Property UriSource which you can bind to inside the Template.
<Style x:Key="ButtonStyle1" TargetType="{x:Type local:ImageButton}">
<!--...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<!--...-->
<Image x:Name="imgIcon2"
Source="{Binding RelativeSource={RelativeSource self},
Path=UriSource}"
.../>
<!--...-->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usable like
<local:ImageButton Style="{DynamicResource ButtonStyle1}"
UriSource="Resources/icon1.png"
...>
Also, I thought that you could bind to an Attached Property from inside of the ControlTemplate but that doesn't seem to be working..

Related

Relative Path not resolved in Template Binding 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"... />

Add a control using XAML but instantiated using code-behind

I'm trying to understand how XAML and code-behind talk to each other. I know that code-behind can access an element instantiated in XAML using the Name attribute eg:
Instantiate the button in XAML:
<SomeControlParent controlParent>
<Button Name=button1/>
<SomeControlParent controlParent>
Change properties of the button in code-behind:
button1.Content = "I created this button in XAML"
I was wondering if it was possible to do the opposite using XAML eg:
Instantiate the button in code-behind:
Button button1 = new Button();
controlParent.Child.Add(button1);
and then change the Content of the button using XAML.
Thanks!
Soumaya
Having a code-behind lets you reference elements that have x:Name defined on them in XAML. Going the other direction, you can define properties on your UserControl and then reference them in XAML using a RelativeSource binding:
{Binding MyProperty, RelativeSource={RelativeSource Self}}
So in your example, you could have a property on your UserControl (although you'd probably want it to be a dependency property so you have change notification):
public Button Button1 { get; private set; }
And then insert it into your XAML using:
<ContentControl Content={Binding Button1, RelativeSource={RelativeSource Self}}>
<ContentControl.Resources>
<Style TargetType="Button">
<Setter Property="Content" Value="Hey, I changed the name in XAML!"/>
</Style>
</ContentControl.Resources>
</ContentControl>

Clickable ItemsControl item in WPF

I'm using an ItemsControl to display a list of databound items, for each the core of the DataTemplate is a Grid upon which I've placed all the bound controls.
I would like to be able to click on the entire area for each item in the list. But I cannot figure out how to make the area clickable.
Any suggestions of how to make an entire grid area clickable would be great.
To make something clickable you can usually wrap it in a Button, if it should be "invisible" you can change the template:
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter />
</ControlTemplate>
</Button.Template>
You can use the AttachedCommandBehavior classes from C# Disciples to achieve this.
Define a command in the ViewModel, and then on the Grid object use the ACB AttachedProperties to bind the MouseLeftButtonUp event to the command.
Some code to get you started:
<Grid Name="grid" Height="30" ForceCursor="True" Cursor="Hand">
<acb:CommandBehaviorCollection.Behaviors>
<acb:BehaviorBinding Event="MouseLeftButtonUp" Command="{Binding Path=DataContext.EditEventCommand, RelativeSource={RelativeSource AncestorType={x:Type self:Dashboard}}}" CommandParameter="{Binding}" />
</acb:CommandBehaviorCollection.Behaviors>
</Grid>
Edit for non-MVVM solution.
The above code snippet will still work when you have not designed your application following the MVVM guide-lines as you are essentially just binding to a command in the code-behind.
However, if you don't want to go to the trouble of defining commands, you can simply specify an event to hook to, like so:
<Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp"> in the XAML file.
and in the code-behind:
private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}

WPF Styles - Please help me understand why this works the way it does

<Style x:Key="MyStyle">
<Setter Property="Window.Background" Value="Orange"/>
</Style>
<Button Content="Ok" Style="{StaticResource MyStyle}"/>
Why does the button actually get an orange background, if the setter is specified as Window.Background?
This does not give the TextBlock an orange background:
<TextBlock Style="{StaticResource MyStyle}"/>
Thanks
Neither Button nor Window actually define the Background property, they both inherit it from Control.
So even though you wrote Window.Background, the setter is actually bound to the property by using the Control.BackgroundProperty field which also applied to Button.
It works because the Background property is attached to Control class which both Window and Button have as ancestor

MouseBinding overrides EventBindings in style

I created a named style on an image, and in the style I set an EventBinding for the MouseDown event to a handler at the ResourceDictionary's code-behind, it worked good.
When I use the image as the following:
<Style TargetType="{x:Type Image}" x:Key="ImageStyle">
<EventSetter Event="MouseDown" Handler="Image_MouseDown"/>
</Style>
<!---->
<Image Style="{StaticResource ImageStyle}">
<Image.InputBindings>
<MouseBinding Command="Save" MouseAction="LeftClick"/>
</Image.InputBindings>
</Image>
It causes the styled MouseDown eventbinding not to work.
It's hard to say without the style code, but I suppose you're defining InputBindings property in that style, then the Image setting just override it. Whatever you set in the actual element XAML overrides whatever you define in that element's style.
If that's the case, there's no easy way to merge style properties with the actual properties.
HTH.

Resources