WPF Hyperlink Image - wpf

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.

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>

WPF show small number beside all Controls

I have many FrameworkElements (TextBlock, CheckBox, ListBox..) and I would like to make something allowing me to show a small number besides every one control.
Some text ³
I came with the idea to write a MarkupExtension, where I could write that number like this:
..
<TextBlock Text="Some Text" SomeExtension="3" />
..
and then to add it somehow to the template of the Control.
But I'm sure, you guys have better solution for this problem ;)
One way to go with it would be create a Attached Property. Upon setting it on a control, a custom Adorner would be added for that control showing specified number.
Use the tag property to provide the number you want and inside the custom template databind to the property
<TextBlock Text="Some Text" Tag="3" />
and inside the controltemplate
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}"/>

WPF: How to embed button inside text flow (wrap text around button)?

I'd like an advice to the following problem: I want to embed a Button into a text flow, but when I embed a Button and Label (or TextBlock) into the WrapPanel, I get the first figure:
alt text http://sklad.tomaskafka.com/files/wpf-wrappanel-problem.png
I think that one of solutions could be FlowDocument, but I feel that this is far too heavy for a control simple like this (which could be used in several hundred instances). Do you have some other ideas about how to implement this? Thank you!
EDIT:
One solution could be the following (I didn't know it was possible to put more stuff into TextBlock), but I would lose the ability to bind (which I need):
<TextBlock TextWrapping="Wrap">
<Span>
<Button x:Name="MyButton" Command="{Binding Path=MyCommand}" Content="+" />
<Run x:Name="MyLabel" Text="{Binding Path=Subject}" />
<!--
Problem: binding makes following error:
A 'Binding' cannot be set on the 'Text' property of type 'Run'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
-->
</Span>
</TextBlock>
To bind to Run.Text, checkout the BindableRun class by Fortes. Simple to implement, I use it all over my projects.
I found that implementing BindableRun correctly is pretty tricky - and almost all other available implementations will cause an exception from wpf layouting engine when the bound content changes from null to something non-null - see this problem, keyword "Collection was modified; enumeration operation may not execute."
Corrrect implementation from Microsoft is here - it shows how tricky this really is.
Solution: BindableRun class + the following markup:
<TextBlock>
<Button x:Name="MyButton" Command="{Binding Path=MyCommand}" Content="+" />
<common:BindableRun x:Name="Subject" BindableText="{Binding Path=Subject}"/>
</TextBlock>
Funny thing it works on the designer of a UserControl...
In that case, using the Property Change of your control to set the value to the Run is enough. I mean, if you had something like:
<TextBlock>
<Run Text="{Binding ElementName=thisCtrl, Path=Description}" />
</TextBlock>
Then just name the run, and on your property change handler of your UserControl DependencyProperty get/set the value.

WPF relative path problem

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.

Display images in TextBlock (WPF)

I'm working on a simple chat application. Currently the messages are binded to a custom-styled listbox like this (simplified XAML):
<ListBox ItemsSource="{Binding MessageCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now I would like to be able to put images (like graphical smileys) into the displayed message text. Is there any way to achieve this using TextBlock (or any other standart component) or do I need to use some special control for this?
Thanks in advance
Just use the InlineUIContainer.
<TextBlock TextWrapping="Wrap">
<Run>Some text.</Run>
<InlineUIContainer>
<Image Source="http://sstatic.net/stackoverflow/img/apple-touch-icon.png" Height="20"></Image>
</InlineUIContainer>
<Run>Some more text.</Run>
</TextBlock>
The Content of a TextBlock is always just a series of Inlines, so you should use the InlineUIContainer. You can insert this container as one of the Inlines in your TextBlock wherever you want an Image to appear, alternating with text Runs. You could parse a message and at the same time keep adding the tokens (either text or images) that you find to the Inlines collection of the TextBlock.
If you want the Images actually inside the text (like an emoticon), then you are going to have to do some work. This sounds like one of the few times I would actually want a User Control, the point of which would be one that scans the Text looking for emoticon values and building a Data Template on the fly.
Remember that anything you can do in XAML you can do in code, so the code I'm thinking of would follow this general idea:
Scan text for emoticon values and
create a list of values for data
elements.
Create a DockPanel.
Foreach element in the List, add
either a TextBlock or an Image
(based on value).
Set this.Content to the DockPanel.
I think something like this is actually what you are looking for, but if you want just an Image, then the ValueConverter suggestion would work.
You could use a value converter to convert the text to another type which has a list of segments which are composed of either text or the smiley face (in the order in which they appear).
Then, you can use a data template to bind to that new type and display the text and smiley faces appropriately.
I also encountered this problem recently and I overcome this by
Creating an ListBox ItemTemplate containing an ItemsControl that has a WrapPanel in the ItemsPanelTemplate and then binding my string to the ItemsSource of the ItemsControl with a IValueConverter that houses all the logic.
Split out your words and query/search your emoticons strings, hyperlinks etc and create your TextBlock, Image, Hyperlink, Button elements and set your values and event handles.
In the function create a List<UIElement> and populate the List with the controls you have generated and return the List as the object in the Convert function of the IValueConverter.
Because you have the WrapPanel in there you get your wrapping done.
Use the Image element instead of the TextBlock and use a Converter to map the text value to the smile image.
<ListBox ItemsSource="{Binding MessageCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Text, Converter={StaticResource MyImageConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>

Resources