I am building a small Wpf aplication to learn myself wpf.
And i have encountered a problem with one of the controlers.
i Have an object with a list of url's in a string format, and i want to bind them to an image and use the wpf converter class to convert the url's to bitmaps.
But when i implement the converter the program throws the following error:
'XmlParseException was unhandled'
And in the details it says this:
"{"Unable to cast object of type
'ChanGrabber.Converter' to type
'System.Windows.Data.IValueConverter'."}"
This is the code for referencing the converter in the xaml:
xmlns:local="clr-namespace:ChanGrabber">
<Window.Resources>
<local:Converter x:Key="Convert"/>
</Window.Resources>
This is the code where i use the control:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ThumbImgUrl, Converter={StaticResource Convert}}" />
</StackPanel>
</DataTemplate>
and here is the code for the converter:
namespace ChanGrabber
{
class Converter
{
[valueconversion(typeof(string), typeof(bitmapimage))]
public class imageconverter : ivalueconverter
{
public object convert(object value, type targettype, object parameter, system.globalization.cultureinfo culture)
{
try
{
string mypath = (string)value;
uri myuri = new uri(mypath);
bitmapimage animage = new bitmapimage(myuri);
return animage;
}
catch (exception)
{
return new bitmapimage(new uri("ikke funket"));
}
}
public object convertback(object value, type targettype, object parameter, system.globalization.cultureinfo culture)
{
throw new notimplementedexception();
}
}
And this is is the object i am binding to the image
class MainPosts : MainLinks
{
public MainPosts(string _title, string _link, String _postText, string _imageUrl, string _thumbUrl) :base(_title,_link)
{
PostText = _postText;
ImageUrl = _imageUrl;
ThumbImgUrl = _thumbUrl;
}
public String PostText { get; set; }
public String ImageUrl { get; set; }
public string ThumbImgUrl { get; set; }
}
I have no idea why it won't work, and i am getting abit frustrated on the program.
Any help will be so incredibly appreciated
use <local:imageconverter x:Key="Convert"/>
Your converter needs to implement the IValueConverter interface, otherwise WPF won't know what to do with it (so it gives you that exception.)
class Converter : IValueConverter
{
...
}
Related
I have a ComboBox that I need to do a converter on the SelectedItem. Problem is the IValueConverter needs the binding value but a Collection as well. Configured a DependencyObject but it is giving me an error message of
Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Collections.ObjectModel.ObservableCollection`1[MyClass]'.
Here is my IValueConverter
public class MyConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty FoldersProperty =
DependencyProperty.Register(nameof(MyCollection), typeof(ObservableCollection<MyClass>), typeof(MyClassModelToMyClassID), new FrameworkPropertyMetadata(new ObservableCollection<MyClass>()));
public ObservableCollection<MyClass> MyCollection
{
get { return GetValue(FoldersProperty) as ObservableCollection<MyClass>; }
set { SetValue(FoldersProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//Amazing Convert code that uses MyCollection and Value
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//Amazing ConvertBack code that uses MyCollection and Value
}
}
Here is how I am calling it:
<Page.Resources>
<converter:MyConverter x:Key="Converter" MyCollection="{Binding DataCollection}" />
</Page.Resources>
....
<ComboBox
ItemsSource="{Binding DataCollection}"
SelectedItem="{Binding Path=MyValue, Converter={StaticResource TaxCodeConverter}}" />
edit: added the full IValueConvert subtracted the Convert and ConvertBack code
Like a BindingProxy, it needs to be a Freezable. Also, don't pass a new observable collection to your metatadata. That's a reference type, so all instances of this converter will be initialized with the same actual collection instance.
Let me know if you run into some other issue, but I've done this and been able to bind to the dependency property.
Many would argue that a better approach would be a multibinding and a multi-value converter. I think there's value in having a strongly typed property with a descriptive name.
public class MyConverter : Freezable, IValueConverter
{
/* omitted: Convert() and ConvertBack() */
public MyConverter()
{
// Initialize here if you need to
MyCollection = new ObservableCollection<MyClass>();
}
protected override Freezable CreateInstanceCore()
{
return new MyConverter();
}
public static readonly DependencyProperty MyCollectionProperty =
DependencyProperty.Register(nameof(MyCollection),
typeof(ObservableCollection<MyClass>), typeof(MyConverter),
new FrameworkPropertyMetadata(null));
public ObservableCollection<MyClass> MyCollection
{
get { return GetValue(MyCollectionProperty) as ObservableCollection<MyClass>; }
set { SetValue(MyCollectionProperty, value); }
}
}
XAML usage will be just as you have it in your question: Bind the dependency property, and the binding will update that property of that instance of MyConverter, provided that your Page's DataContext has an appropriately typed property named DataCollection.
<Page.Resources>
<converter:MyConverter x:Key="Converter" MyCollection="{Binding DataCollection}" />
</Page.Resources>
I am trying to load a ListView thats binded to a database that brings a list of items. In the ListView i am showing two colums, "Items" and "Status." I am able to bring the values, but now I want to replace the value in Status to an Image.
Example:
1 = images/green.png
2 = images/red.png
3 = images/orange.png
And I would like to show the Image in the list, so as the user navigates, they see all the images automatically. I found something similar in another question but that has the image tag built in which I can't do in a ListView.
WPF Convert Integer to Image without DB Binding
Thanks for Help.
EDIT
Partial Class ImgConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object
Dim imageIndex As Integer
If Integer.TryParse(value.ToString(), imageIndex) Then
Select Case imageIndex
Case 1
Return BitmapSource = "Images/green.png"
Case 2
Return BitmapSource = "Images/red.png"
End Select
End If
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object
Throw New NotImplementedException()
End Function
End Class
With this code I am getting a IntelliSense error BitmapSource and Implements IvalueConverter saying that I need to implement a ConvertBack, which I did but it still bugs me.
EDIT #3
OK THE BITMAPSOURCE ERROR WAS BC I DIDN'T DECLARE THE VARIABLE. THATS SOLVED.
The IValueConverter error says:
Error 2 Class 'ImgConverter' must implement 'Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object' for interface 'System.Windows.Data.IValueConverter'. ...\Config (ABM,20)\RangoPage.xaml.vb 14 Cogent
Use an IValueConverter,
Which will take an integer as a parameter, and return a BitmapSource
<ListView ItemsSource="{Binding Collection}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Textblock Text="{Binding Item}" />
<Image Source="{Binding Status, Converter={StaticResource ValueConverter}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class IntToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int imageIndex;
if(int.TryParse(value.ToString(), out imageIndex))
{
switch(imageIndex)
{
case 1:
return new ImageSource("images/red.png")
etc...
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just in case if yo need to use it in UWP app there is working approach.
C#
public sealed class IntStateToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
try
{
var imagePath = "ms-appx:///Assets/Buttons/";
DeviceNodeTechStateEnum state = value.ObjectToEnums<DeviceNodeTechStateEnum>();
switch (state)
{
case DeviceNodeTechStateEnum.OK:
imagePath += "correct.png";
break;
case DeviceNodeTechStateEnum.BAD:
imagePath += "incorrect.png";
break;
default:
imagePath += "unknown.png";
break;
}
Uri imageUri = new Uri(imagePath, UriKind.Absolute);
BitmapImage imageBitmap = new BitmapImage(imageUri);
return imageBitmap;
}
catch (Exception)
{
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return !(value is bool && (bool)value);
}
}
Where is ms-appx:///Assets/Buttons/ is your project folder to keep images.
XAML of UserControl
<Image Source="{Binding State, Converter={StaticResource intStateToImageConverter}}" Width="20" Height="20" ></Image>
Where is State is a field of the class has type DeviceNodeTechStateEnum.
XAML of APP
<Application.Resources>
<ResourceDictionary>
<common:IntStateToImageConverter x:Key="intStateToImageConverter" />
C# Enums
public enum DeviceNodeTechStateEnum
{
Undefined = 1,
OK = 2,
BAD = 3,
}
Method to convert object to enums.
public static class Extensions
{
public static T ObjectToEnums<T>(this object o)
{
T enumVal = (T)Enum.Parse(typeof(T), o.ToString());
return enumVal;
}
}
I wanted to experiment with being able to have a converter whose arguments can be bound with the current data context. Can anyone tell me why when reaching the Convert() function, the Source property is always null?
namespace WpfApplication32
{
public class ConverterTest : DependencyObject, IValueConverter
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(DependencyObject), typeof(ConverterTest));
public DependencyObject Source
{
get { return (DependencyObject)this.GetValue(SourceProperty); }
set { this.SetValue(SourceProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
Value = 7;
InitializeComponent();
DataContext = this;
}
public float Value
{
get;
set;
}
}
}
<Window x:Class="WpfApplication32.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication32">
<Slider>
<Slider.Value>
<Binding Path="Value">
<Binding.Converter>
<local:ConverterTest Source="{Binding}"/>
</Binding.Converter>
</Binding>
</Slider.Value>
</Slider>
</Window>
One possible solution is to make your Converter inherit from Freezable instead (Hillberg Freezable trick). Then you can even define your Converter in your Resources and reference it in your binding as an attribute instead of an extra child element.
First of all, I am new to WPF and Xaml, so I just hope that you understand what I am asking.
I got this situation: There is a listBox of Animals. Every Animal has Weight property. What I am trying to achieve is whenever Weight of Animal is greater then 300 kg, that Weight should be displayed red.
You could use custom converter to achieve that. If your item looks like that:
public class Animal
{
public int Weight { get; set; }
public string Name { get; set; }
}
and ItemTemplate like that:
<DataTemplate x:Key="AnimalTemplate">
<TextBlock Text="{Binding Name}" Foreground="{Binding Weight, Converter={StaticResource AnimalColorSelector}}"/>
</DataTemplate>
Your converter will be like the following one:
public class AnimalColorSelector : IValueConverter
{
private readonly Color _overweightColor = Colors.Red;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int)
{
return (int) value > 300 ? new SolidColorBrush(_overweightColor) : Binding.DoNothing;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
This approach has the following pros:
You don't need to hardcode the default color, but inherit it by using Binding.DoNothing.
You don't need to store any style information in a view model.
You could create a ViewModel for Animals that would contain necessary logic for color setting. Like this:
public class VMAnimal : INotifyPropertyChanged
{
private int _weight;
public int Weight
{
get { return _weight; }
set
{
_weight = value;
RaisePropertyChanged("Weight");
RaisePropertyChanged("Color");
}
}
public Brush Foreground
{
get
{
if (Weight > 300)
return new SolidColorBrush(Color.Red);
return new SolidColorBrush(Color.Black);
}
}
}
And use it with binding like this:
<TextBlock Text="{Binding Weight}" Foreground="{Binding Foreground}" />
Here is what happens:
I have a listbox with items. Listbox has focus. Some item (say, 5th) is selected (has a blue background), but has no 'border'.
When I press 'Down' key, the focus moves from ListBox to the first ListBoxItem.
(What I want is to make 6th item selected, regardless of the 'border')
When I navigate using 'Tab', the Listbox never receives the focus again.
But when the collection is emptied and filled again, ListBox itself gets focus, pressing 'Down' moves the focus to the item.
How to prevent ListBox from gaining focus?
P.S.
listBox1.SelectedItem is my own class, I don't know how to make ListBoxItem out of it to .Focus() it.
EDIT: the code
Xaml:
<UserControl.Resources>
<me:BooleanToVisibilityConverter x:Key="visibilityConverter"/>
<me:BooleanToItalicsConverter x:Key="italicsConverter"/>
</UserControl.Resources>
<ListBox x:Name="lbItems">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ProgressBar HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Visibility="{Binding Path=ShowProgress, Converter={StaticResource visibilityConverter}}"
Maximum="1"
Margin="4,0,0,0"
Value="{Binding Progress}"
/>
<TextBlock Text="{Binding Path=VisualName}"
FontStyle="{Binding Path=IsFinished, Converter={StaticResource italicsConverter}}"
Margin="4"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<me:OuterItem Name="Regular Folder" IsFinished="True" Exists="True" IsFolder="True"/>
<me:OuterItem Name="Regular Item" IsFinished="True" Exists="True"/>
<me:OuterItem Name="Yet to be created" IsFinished="False" Exists="False"/>
<me:OuterItem Name="Just created" IsFinished="False" Exists="True"/>
<me:OuterItem Name="In progress" IsFinished="False" Exists="True" Progress="0.7"/>
</ListBox>
where OuterItem is:
public class OuterItem : IOuterItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public bool IsFolder { get; set; }
public bool IsFinished { get; set; }
public bool Exists { get; set; }
public double Progress { get; set; }
/// Code below is of lesser importance, but anyway
///
#region Visualization helper properties
public bool ShowProgress
{
get
{
return !IsFinished && Exists;
}
}
public string VisualName
{
get
{
return IsFolder ? "[ " + Name + " ]" : Name;
}
}
#endregion
public override string ToString()
{
if (IsFinished)
return Name;
if (!Exists)
return " ??? " + Name;
return Progress.ToString("0.000 ") + Name;
}
public static OuterItem Get(IOuterItem item)
{
return new OuterItem()
{
Id = item.Id,
Name = item.Name,
IsFolder = item.IsFolder,
IsFinished = item.IsFinished,
Exists = item.Exists,
Progress = item.Progress
};
}
}
Сonverters are:
/// Are of lesser importance too (for understanding), but will be useful if you copy-paste to get it working
public class BooleanToItalicsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool normal = (bool)value;
return normal ? FontStyles.Normal : FontStyles.Italic;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool exists = (bool)value;
return exists ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But most important, is that UserControl.Loaded() has:
lbItems.Items.Clear();
lbItems.ItemsSource = fsItems;
where fsItems is ObservableCollection<OuterItem>.
The usability problem I describe takes place when I Clear() that collection (fsItems) and fill with new items.
Please provide your code. Usually the cause of this problem lies in ContentPresenters and KeyboardNavigation.IsTabStop property. But sometimes it's not. So the code would help.
The answer to your question may depend on the way your listbox is getting focus. Here is the solution if you are using an access key (ex: alt+c). You have to implement your own listbox control and override the OnAccessKey method. If this is not your scenario, then I would suggest looking into the OnIsKeyboardFocusWithinChanged method. Try using the same approach I did in the code below.
protected override void OnAccessKey(System.Windows.Input.AccessKeyEventArgs e)
{
if (SelectedIndex >= 0)
{
UIElement element = ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as UIElement;
if (element != null)
{
element.Focus();
}
}
}