WPF relative path problem - wpf

I have created a custom TaskButton control that takes an image and text. The properties are set like this:
<custom:TaskButton Text="Calendar" ImagePath="Images/calendar.png" ... />
My custom control class implements Text and ImagePath properties, and the control template for the custom control (in Themes\Generic.xaml) sets its content like this, using a RelativeSource object to get the image path:
<!-- Button Content -->
<StackPanel>
<Image Source="{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}" Width="24" Height="24" Stretch="Fill" Margin="10,0,0,0" />
<TextBlock Text="{TemplateBinding Text}" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Segoe UI" FontWeight="Bold" Margin="6,0,10,0" Foreground="Black" />
</StackPanel>
The control works fine in most cases, but in a particular project, the relative path to the button's image does not get resolved correctly, and the button image is not displayed. Here is what I have figured out so far:
I am entering the path correctly when I use the custom control. If I place an image control on the same design surface with the same relative path, it is resolved correctly.
The problem is with the relative path. If I replace the relative path with an absolute path, the path is resolved correctly and the image is displayed.
As I mentioned above, the control works fine in most cases. The one case where it isn't working is a Prism 2.1 project, where the control is instantiated on a user control in a Prism module. The module is a simple class library, but it has all of the references of a WPF project.
Any idea why the relative path would fail? Thanks in advance for your help.

I finally figured out the problem. It was actually in the C# backing class for my control. I declared an ImagePath property as a string, since that was how I was going to specify the image. Oops--bad call on my part. That property should actually be an ImageSource property, not a string. WPF has a built-in ImageSourceConverter class that will resolve the path and return the specified image. So, I simply changed the property name from ImagePath to Image, and changed its type from string to ImageSource. That solved the problem.
Thanks to Aviad P. for taking a crack at this. It was unsolvable without the C# code showing the property declarations. I'll post all code and markup next time.

Related

How do I bind to the X position of a MouseDragElementBehavior?

My goal is to display the X position of my control in a TextBlock as I drag it around.
xmlns:mb="http://schemas.microsoft.com/xaml/behaviors"
<cc:CardControl Name="SevenOfSpades" Canvas.Left="350" Canvas.Top="124" Width="60" Height="80" Face="S7">
<mb:Interaction.Behaviors>
<mb:MouseDragElementBehavior ConstrainToParentBounds="True"/>
</mb:Interaction.Behaviors>
</cc:CardControl>
<TextBlock Text="{Binding ElementName=SevenOfSpades, Path=(mb:Interaction.Behaviors)[0].X}"/>
I'm struggling with the syntax of the Binding Path. At runtime I get an exception:
InvalidOperationException: Property path is not valid. 'Interaction' does not have a public property named 'Behaviors'.
The property is there because the drag works when the TextBlock is removed. I've tried various combinations of parentheses, I even tried x:static. Any help?
Edit
Having reread WPF Attached Property Data Binding, it does not solve my problem. Path= is in the Xaml and parentheses are included. The error is not a binding error it's a runtime error that occurs inside InitializeComponent.
MouseDragElementBehavior is part of the Microsoft.Xaml.Behaviors.Wpf Nuget package installed into my project.
Ah, ok. In that case, the code for MouseDragElementBehavior is most certainly available, and even if it wasn't you could just open up the assembly with JustDecompile or something and browse it that way.
If you check the documentation for MouseDragElementBehavior you'll see this:
XProperty Dependency property for the X position of the dragged
element, relative to the left of the root element.
So basically you're trying to bind one dependency property (TextBlock.Text) to another (MouseDragElementBehavior.X), but in order for this to work they have to be part of the same visual or logical tree (which they aren't, MouseDragElementBehavior is a behavior). If one of them was an attached property then you could bind them directly, but in your case you have to link them together with either a property in your DataContext that supports INPC, or some kind of proxy object.
However, even if you do this, you're going to run into problems. If you click the "Go to Live Visual Tree" button while your application is running and look at the properties for your SevenOfSpades control you'll see this:
So far, so good. Now drag the control around a bit and repeat this process. Suddenly a RenderTransform field has appeared:
Looking back at the code for MouseDragElementBehavior reveals that sure enough, that behaviour does the drag by changing the render transform.
So basically you're trying to set the position with Canvas.Top/Canvas.Left, but the behaviour is setting it by applying a render transform offset. Pick one. I personally use MVVM where everything is implemented in the view model layer, so it's easy to bind Canvas.Top/Canvas.Left to properties there. If you want to continue using MouseDragElementBehavior then you'll need to bind both the position of your cards, as well as your TextBlock text, to the render transform instead:
<Canvas>
<Rectangle Name="SevenOfSpades" Width="60" Height="80" Fill="Blue">
<Rectangle.RenderTransform>
<TranslateTransform X="350" Y="124" />
</Rectangle.RenderTransform>
<mb:Interaction.Behaviors>
<mb:MouseDragElementBehavior ConstrainToParentBounds="True" />
</mb:Interaction.Behaviors>
</Rectangle>
<TextBlock Text="{Binding ElementName=SevenOfSpades, Path=RenderTransform.Value.OffsetX}" />
</Canvas>

Images bound to images added to resx files using XAML

My WPF application includes a resource file MyResources.resx, containing several strings and images. Because the application will need to be localized, all my references to globalized resources must be made via named properties of the auto-generated MyResources class. The following code works well for string resources:
<Button Content="{x:Static local:Properties.MyResources.ButtonText}" />
However the same does not work for images. Assuming I have an image eflag.bmp added to the resources as a resource named Flag, I would like to be able to do something like this:
<Image Source="{x:Static local:Properties.MyResources.Flag}" />
Please note that the following alternative approach:
<Image Source="/MyNamespace;component/Resources/eflag.bmp" />
cannot be used in this case because it will not be able to handle localization. The problem can be solved using code behind but I am looking for a XAML based solution.
Turn your x:Static into a Binding.Source and add a Converter which does Bitmap to ImageSource.
Source="{Binding Source={x:Static local:Properties.MyResources.Flag},
Converter={StaticResource BitmapToImageSourceConverter}}"
Alternatively you can make the converter a custom markup extension which takes a Bitmap and returns the ImageSource in ProvideValue.
Source="{me:BitmapToImageSource {x:Static local:Properties.MyResources.Flag}}"

binding an image source in XAML

I am trying to bind an image source to my XAML through c#
this works
<Image Source="images/man.jpg"></Image>
this does not work
<Image Source="images/{Binding imagesource}"></Image>
where imagesource is a string variable in the c# file of this xaml and is being set equal to "man.jpg"
here is a way how to do it in XAML:
add this to the namespace:
xmlns:System="clr-namespace:System;assembly=mscorlib"
then add your images paths
<System:String x:Key="ImageRefresh">/Theme;component/Images/icon_refresh.png</System:String>
<System:String x:Key="ImageSearch">/Theme;component/Images/icon_search.png</System:String>
This is how you use it
<Image Height="16" Source="{StaticResource ImageSearch}" Stretch="Uniform" Width="16"/>
This works ok, but if you load your xaml style in Blend it will go bogus..
An object of type "System.String" cannot be applied to a property that expects the type "System.Windows.Media.ImageSource".
I haven't figured out yet, how to replace System:String with that Media.ImageSource... but hey.. it works for me in Visual Studio.
You can't stick a binding mid-way through the value like that. It's either a binding, or it's not. Assuming imagesource is publicly accessible via your DataContext, you could do this:
<Image Source="{Binding imagesource}"/>
However, if it's been set to "man.jpg" then it won't find the image. Either set imagesource to the full path ("images/man.jpg") or use a converter:
<Image Source="{Binding imagesource, Converter={StaticResource RelativePathConverter}}"/>
The converter would prepend "images/" onto its value. However, it may be necessary for the converter to return an ImageSource rather than a string.
Images have bitten me in the past. There is a certain lookup order involved.
When you use "image/man.jpg" it could refer to a file inside your silverlight xap, or relative to the location of XAP file. For example, it could be in YourProject.Web/ClientBin/image/man.jpg.
You should troubleshoot by using full URLs first and find out if this works.
imagesource needs to be an actual Image object, not a string.
Here is a method that will create a new Image object given a path:
public BitmapImage Load(string path)
{
var uri = new Uri(path);
return new BitmapImage(uri);
}

Pack URI and path not resolving image in WPF

I have the following directory structure
Project
\Images
+view.png
control.xaml
and in the control I have a button defined by the following XAML:
<Button Click="Search"
Grid.Column="1"
Margin="0,5,5, 0"
HorizontalAlignment="Right">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Image Source="pack://application:,,,/images/view.png"
Width="16"
Height="16"
ToolTip="Search"
Cursor="Hand"
Opacity="0.8" />
</ControlTemplate>
</Button.Template>
</Button>
However, neither this pack URI method nor the "/images/view.png" is working. As I understand it, this is the same issue this question raises. However, I get the same error. The confusing thing is that in designer in Visual Studio 2008, the image renders correctly, but on the call to the InitializeComponent() call, I get:
Cannot convert string 'pack://application:,,,/images/view.png' in attribute 'Source' to object of type 'System.Windows.Media.ImageSource'. Cannot locate resource 'images/view.png'. Error at object 'System.Windows.Controls.ControlTemplate' in markup file 'RecapSpecEditControl;component/modaltreadgroupdatadialog.xaml' Line 61 Position 40.
I thought that maybe there was a namespace that I had to declare but according to the msdn site I believe I don't have to do anything like that.
I actually got this to work, but had to set my source to "/ProjectName;component/images/view.png" Because I have the ProjectName as a referenced assembly this is then the same as the Path: portion at the msdn page that I referenced in the question.
Set the Build Action for 'view.png' to Resource instead of Content and this problem should go away. I was able to reproduce your problem this way and it works correctly when set as a Resource.
Xaml.VB
Call the Image from Application folder and Design Page
Private Sub LoadImages()
Dim strUri As String
strUri = AppDomain.CurrentDomain.BaseDirectory() & "\NavigationImages\settingsicon.png"
Image2.Source = New BitmapImage(New Uri(strUri))
End Sub
Page load in Xaml.VB
Call LoadImages()
Xaml Design Page
Image Name="Image2"Height="32" HorizontalAlignment="Left"

WPF Hyperlink Image

In my app I want to resize thumbinal Image when I click it, all images are in ItemTemplate where its Source property is bind to url to picture.
I try something like this (this is in my DataTemplate file)
<TextBlock>
<Hyperlink TextDecorations="None"
Command="helpers:StatusesCommands.ShowPicture"
CommandParameter="{Binding}">
<Image Source="{Binding Path=FirstPictureUrl}" Margin="5" />
</Hyperlink>
</TextBlock>
But in Command handler I don't know how to get to this image. The OriginalSource propoerty on ExecutedRoutedEventArgs is set to HyperLink and Source is set to my control.
Maybe it's possible to set CommangParameter to my nested Image, but I don't know how to do it. Do you have any idea how to solve this?
You are binding the command parameter to the data context, so you will have access to the image path in the command parameter, if you cast the parameter correctly.
To pass the image as the command parameter directly, first name the Image:
<Image x:Name="myImg" Source="{Binding Path=FirstPictureUrl}" Margin="5"/>
Bind the CommandParameter to this image:
CommandParameter="{Binding ElementName=myImg}"
It's not clear where you are handling this command. If you are handling it in code behind for this XAML, you could name the Image element and refer to it by name in the code behind file. However, it's usually better practice to pass what you need to the command as a parameter. Sometimes the command is handled far away from where the command is triggered.

Resources