How to properly compute URI from ValueConverter in WPF - wpf

I have a simple application where I want to swap out image based on a value converter. Upon testing the images shows fine when directly bound to the code behind of a user control.
public partial class HypercombControls : Grid
{
public static readonly DependencyProperty MoveImageSourceProperty =
DependencyProperty.Register("MoveImageSource", typeof(Uri), typeof(HypercombControls),
new PropertyMetadata(new Uri("pack://application:,,/Media/control-bar/move.png")));
...
}
<Button Click="MoveButton_OnClick" Style="{StaticResource DragModeButtonStyle}">
<Image Source="{Binding ElementName=HyperControls, Path=MoveImageSource}">
</Image>
</Button>
The image shows as expected. I then added a MultiBinding with a MultiValueConverter to change the image source dynamically based on some logic. The image always comes up blank when I return the URI even if I return the exact same image URI as constructed initially. Is there something I am missing?
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture)
{
// logic omitted but tested with the following ouput.
// same as new PropertyMetadata(...)
return new Uri("pack://application:,,/Media/control-bar/move.png");
}

Related

Is it possible to concatenate an imagesourece Uri in xaml

Hi I've been looking at ways to dynamically change an image Uri in xaml and in my research came across the following answer, which has certainly given me hope that what I really want to do might just be possible. In the original question the questioner wanted to swap the image itself, in my case I want to swap the directory where the image is located.
So when one looks at the answer that #Clemens provided one ends up with an images source being bound to a dependency property that is dynamically set when the form loads.
What I'd like to know is whether it would be feasible to set the location part of the uri dynamically (as per the logic that #Clemens is advocating and then simply append the Image name in the actual binding statement so that it might look something like this:
<Image Source="{Binding ImageUri & myImage.png}"/>
Essentially I have a number of buttons to which I would like to assign a default image og a size to be determined by the end user. To that end the Images would be stored in different folders in the application (in fact its a custom control) and then the relevant path bit of the URI would be set as per the suggestion in the referenced answer and I'd just append the Image name (which would be the same for the button irrespective of the size) and it would then have the correct image to display.
MainWindow.xaml.cs :
namespace MainWindowNamespace
{
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
string fullPath = Path.GetFullPath((string)value);
return new BitmapImage(new Uri(fullPath));
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ throw new NotImplementedException(); }
}
}
MainWindow.xaml :
<Window
xmlns:imgConvert="clr-namespace:MainWindowNamespace">
<Window.Resources>
<imgConvert:ImageConverter x:Key="ImageConverter" />
</Window.Resources>
<Image ImageSource="{Binding ImagePath, Converter={StaticResource ImageConverter}}" />
</Window>

Using Resourse.resx in WPF application to set color

I´m trying to make an application that has to be able to easily change a dll file which could change colors in the application.
I´m trying to use resource manager to do this but am having problems with setting color values so that the styles for views can easily accept it.
We know that(in this case) the background of a button takes in SolidColorBrush, and while
Value="Black" works,
Value={x:Static res:AppResources.Btn_Background}
which gives the string Black does not (current theory being that converters make the former work but not the latter).
This is all being done in wpf & mvvm.
Have you guys an idea about how this could be done.
Greetings
You could use a Binding:
Background="{Binding Source={x:Static res:AppResources.Btn_Background}}"
This will cause the CoerceValue to fire for the DependencyProperty controlling the background.
#Snowbear mentioned it may be a Color rather than a String, in which case you would need to provide a trivial IValueConverter.
public class ColorConverter: IValueConverter
{
#region IValueConverter Members
private Dictionary<Color, Brush> brushes = new Dictionary<Color, Brush>();
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
Brush brush;
var color = (Color)value;
if (!brushes.TryGetValue(color, out brush))
{
brushes[color] = brush = new SolidColorBrush(color);
brush.Freeze();
}
return brush;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Your specific issue is that you are bypassing the default string to Brush conversion and would need to handle that manually.
As sixlettervariables states, you'd can use a Binding if your source is a string, but that is probably overkill. At a minimum, you'd want to set Mode=OneTime on the Binding.
You can also create a custom MarkupExtension that performs the conversion.
Your conversion, whether it be through a custom IValueConverter or MarkupExtension, can leverage the BrushConverter class. So things like "Black" or "#000" will work as they do when defining the color in XAML like your first example.
EDIT:
Actually a markup extension that derives from StaticExtension, makes this easier:
public class BrushStaticExtension : StaticExtension {
private static BrushConverter converter = new BrushConverter();
public BrushStaticExtension() { }
public BrushStaticExtension(string member) : base (member) { }
public override object ProvideValue(IServiceProvider serviceProvider) {
return converter.ConvertFrom(base.ProvideValue(serviceProvider));
}
}
If you specify a string then XAML parser uses a converter from string which automatically creates a SolidColorBrush. As far as I understand at the moment Btn_Background resource is Color but it should be a SolidColorBrush instead.

ImageSource dependency property on a user control - XAML value set throws

I've created a small user control consisting of a button whose content is an Image. I created an "ImageSource" dependency property on the user control in order to bind to it from the Image inside the button.
However in the XAML where I placed an instance of my user control setting the property throws an error at runtime :
<ctrl:ImageButton ImageSource="/Resources/Images/Icons/x.png" Command="{Binding Reset}" DisabledOpacity="0.1"/>
and at runtime :
'/Resources/Images/Icons/x.png' string is not a valid value for 'ImageSource' property of type 'ImageSource'. 'ImageSource' type does not have a public TypeConverter class.
I then created a converter :
public class StringToBitmapImage : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new BitmapImage(new Uri((string) value, UriKind.RelativeOrAbsolute));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then decorated my dependency property with it :
[TypeConverter(typeof(StringToBitmapImage))]
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
LambdaHelper.GetMemberName<ImageButton>(ib => ib.ImageSource), typeof (ImageSource), typeof (ImageButton));
[TypeConverter(typeof(StringToBitmapImage))]
public ImageButton ImageSource
{
get { return (ImageButton)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
but still WPF does not convert my string to an ImageSource (BitmapImage) instance...
What to do?
There are several incorrect things here:
First, your CLR property is returning an ImageButton whereas the dependency property is defined as an ImageSource.
Second, a type converter is not the same as a binding value converter. Your type converter should derive from TypeConverter and be applied on the ImageSource class rather than on the property itself.
Third, the framework ImageSource type already has a TypeConverterAttribute with ImageSourceConverter as the type converter so everything should work out of the box without having to write a custom converter. Make sure you're not referencing another custom ImageSource class in another namespace.
To finish, use ImageBrush.ImageSource.AddOwner rather than redefining a whole new dependency property.
Edit: to answer Berryl's comment:
public static readonly DependencyProperty ImageSourceProperty = ImageBrush.ImageSource.AddOwner(typeof(ImageButton);
This piece of code will reuse the existing ImageSource property rather than defining a new (remember that each different dependency property gets registered in a global static dictionary), only defining a new owner and optionally a new metadata. It's like OverrideMetadata but from an outside class.

Display WinForm Image in WPF application

I have a dll with quite a bit of System.Drawing.Image resources that I have wrapped into static properties so I can update them dynamically.
I would like to use the images through xaml in a WPF application, but the only way I can figure is to do it through the code behind manually.
Is there a way to do the winform to wpf image convertion on a static property in xaml?
You could bind to your images directly, using a converter. Here is an example in a window:
<Window.Resources>
<WinForms2WPFImageConverter x:Key="WF2WPFDrawingConverter" />
</Window.Resources>
...
This SO question has a drawing converter, which I adapted here as a ValueConverter.
public class WinForms2WPFImageConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
System.Drawing.Image i = (System.Drawing.Image) value;
using (MemoryStream drawingStream = new MemoryStream())
{
i.Save(drawingStream);
i.Seek(0, SeekOrigin.Begin);
return System.Windows.Media.Imaging.BitmapFrame.Create(drawingStream);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Of course, you need to account for namespaces in the declaration of the resource.
I advise not using static properties, in order to leverage INotifyPropertyChanged (or dependency properties), so that the display automatically changes when the properties point to other images.
(note: this was typed, not copied from VS, so there may be a syntax error somewhere.)
You could bind to your images using a Method in WPF. Here is an example in a link
http://microsoftdotnetsolutions.blogspot.com/2016/07/convert-winforms-image-to-wpf.html

How to add data into RichTextBox from two datasourses in WPF

I need to put data from two different datasourses in the same textbox. The text that comes from the first one have to be bolded and the secound normal.
It's there a possibility to do this in WPF?
You cannot bind (or multibind) to Document property of RichTextBox, because it is NOT a DependencyProperty (strange!!!)!!! See this link for a really easy way of subclassing RichTextBox to create your own BindableRichTextBox or this post for another workaround.
Now you can use MultiBinding with a custom IMultiValueConverter to achieve the results. Since you have not given much details of your problem, I can only give you an overall idea of what you should do:
<!--NOTE: Include xmlns:local=" .. " appropriately for your project-->
<Window.Resources>
<sys:String x:Key="SourceA">This text will be normal..</sys:String>
<sys:String x:Key="SourceB">This text will be Bold!!!</sys:String>
</Window.Resources>
And now you can do like this:
<local:BindableRichTextBox>
<!--<local:BindableRichTextBox.Document>-->
<MultiBinding Converter="{x:Static local:MySourceBToBoldConverter.Instance}">
<Binding Source="{StaticResource SourceA}" />
<Binding Source="{StaticResource SourceB}" />
</MultiBinding>
<!--</local:BindableRichTextBox.Document>-->
</local:BindableRichTextBox>
And then create a class MySourceBToBoldConverter that inherits from IMultiValueConverter like this:
public class MySourceBToBoldConverter : IMultiValueConverter
{
public static readonly MySourceBToBoldConverter Instance = new MySourceBToBoldConverter();
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//Now you'll get value from Source A as value[0]
// and value from Source B as value[1]
//Do whatever you want like bold etc...
//and return the result
string normalText = values[0] as string;
string boldText = values[1] as string;
Bold bold = new Bold();
bold.Inlines.Add(boldText);
Paragraph para = new Paragraph();
para.Inlines.Add(normalText);
para.Inlines.Add(bold);
FlowDocument rtbDocument = new FlowDocument();
rtbDocument.Blocks.Add(para);
return rtbDocument;
}
public object[] ConvertBack(object value, ... )
{
//Convert the object returned by Convert() back
//to its original form if it's possible;
//otherwise throw not supported exception ;)
throw new NotImplementedException();
}
}
Currently I don't have my work PC with me that has VS installed, so I can't give you a working example, but go ahead and search google/msdn/stackoverflow 4 MultiBinding and IMultiValueConverter and you'll find some good examples out there.
Check the working example here.
Regards,
Mihir Gokani

Resources