How to achieve this either in .XAML or programmatically?
<Image>
<!--<iconPacks:PackIconMaterial Kind="{Binding Status}"/>-->
</Image>
I had same problem.
I am using a package allows only System.Windows.Controls.Image
I solved from C# side.
Lets create a Mahapps Icon
PackIconFontAwesome icon = new PackIconFontAwesome();
icon.Kind = PackIconFontAwesomeKind.AddressBook;
And convert to Geometry
Geometry geo = Geometry.Parse(icon.Data);
GeometryDrawing gd = new GeometryDrawing();
gd.Geometry = geo;
gd.Brush = icon.BorderBrush;
gd.Pen = new Pen(Brushes.White, 100);
icon.Data gives XAML Path of icon Path Markup Syntax
Now we can convert to Image How to: Use a Drawing as an Image Source
DrawingImage geoImage = new DrawingImage(gd);
geoImage.Freeze();
Image img = new Image();
img.Source = geoImage;
Now you can bind where ever you want.
Using a Converter is the easiest way of doing this (expanded on tetralobita's answer) but I didn't see the need to create a bunch of template classes so here is a working version using just one Converter:
namespace MyProject.Converters
{
/// <summary>
/// Converts a <see cref="PackIcon{TKind}" /> to an DrawingImage.
/// Use the ConverterParameter to pass a Brush.
/// </summary>
public class PackIconToImageConverter : IValueConverter
{
/// <summary>
/// Gets or sets the thickness to draw the icon with.
/// </summary>
public double Thickness { get; set; } = 0.25;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
GeometryDrawing geoDrawing = new GeometryDrawing();
geoDrawing.Brush = parameter as Brush ?? Brushes.Black;
geoDrawing.Pen = new Pen(geoDrawing.Brush, Thickness);
if (value is PackIconFontAwesome)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconFontAwesomeKind>).Data);
else if (value is PackIconMaterial)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconMaterialKind>).Data);
else if (value is PackIconMaterialLight)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconEntypoKind>).Data);
else if (value is PackIconModern)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconMaterialLightKind>).Data);
else if (value is PackIconEntypo)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconEntypoKind>).Data);
else if (value is PackIconOcticons)
geoDrawing.Geometry = Geometry.Parse((value as PackIcon<PackIconOcticonsKind>).Data);
var drawingGroup = new DrawingGroup { Children = { geoDrawing }, Transform = new ScaleTransform(1, -1) };
return new DrawingImage { Drawing = drawingGroup };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And then to use the Converter:
Add the Namespace to your .xaml file:
xmlns:converters="clr-namespace:MyProject.Converters"
Then to Display the Icon as an Image:
<Image Source="{Binding Source={icons:PackIconMaterial sitemap},
Converter={converters:PackIconToImageConverter},
ConverterParameter={StaticResource TextBrush}}"/>
Or to use a Dependency Property as in a UserControl or Custom Control add the following to your Control:
/// <summary>
/// Identifies the <see cref="Icon"/> dependency property.
/// </summary>
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(object), typeof(MyControl), new PropertyMetadata(null));
/// <summary>
/// Gets or sets a value that specifies an user specific object which can be used as icon.
/// </summary>
public object Icon {
get { return GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
Then Bind to the Property, in your template add:
<ControlTemplate x:Key="MyControlTemplate" TargetType="{x:Type local:MyControl}">
<Image Source="{TemplateBinding Icon,
Converter={StaticResource IconPackConverter},
ConverterParameter={StaticResource TextBrush}}" />
</ControlTemplate>
Maybe I'm a little too late but I was working on this.
I think that sometimes the solution could be way easier
Create this xmlns on your xaml page:
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
and then...
<Image Source="{Binding Source={iconPacks:BootstrapIconsImage Kind=Upload}}"/>
and the image will show up.
Enjoy coding
Why do you need this in an image? The icons are SVG assets rendered as a font in a custom control. Unless you're trying to do something particularly advanced, you can simply remove the <Image> tags and use the <iconPacks:PackIconMaterial /> control directly as shown here.
There is a better solution which I found it on github, more explanation about the code is here
You can convert it via a converter and use it in XAML like this
ImageSource="{Binding Source={x:Static
iconPacks:PackIconEntypoKind.AddToList}, Converter=
{converters:PackIconEntypoImageSourceConverter}, ConverterParameter=
{StaticResource TextBrush}}"
Converter code is
/// <summary>
/// Converts a <see cref="PackIcon{TKind}" /> to an ImageSource.
/// Use the ConverterParameter to pass a Brush.
/// </summary>
public abstract class PackIconImageSourceConverterBase<TKind> : MarkupExtension, IValueConverter
{
/// <summary>
/// Gets or sets the thickness to draw the icon with.
/// </summary>
public double Thickness { get; set; } = 0.25;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is TKind))
return null;
var foregroundBrush = parameter as Brush ?? Brushes.Black;
return CreateImageSource(value, foregroundBrush, Thickness);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
protected abstract ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness);
}
/// <summary>
/// Converts a <see cref="PackIcon{TKind}" /> to an ImageSource.
/// Use the ConverterParameter to pass a Brush.
/// </summary>
public class PackIconImageSourceConverter : MarkupExtension, IValueConverter
{
/// <summary>
/// Gets or sets the thickness to draw the icon with.
/// </summary>
public double Thickness { get; set; } = 0.25;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is null)
return null;
if (value is PackIconFontAwesomeKind)
return new PackIconFontAwesomeImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
if (value is PackIconMaterialKind)
return new PackIconMaterialImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
if (value is PackIconMaterialLightKind)
return new PackIconMaterialLightImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
if (value is PackIconModernKind)
return new PackIconModernImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
if (value is PackIconEntypoKind)
return new PackIconEntypoImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
if (value is PackIconOcticonsKind)
return new PackIconOcticonsImageSourceConverter { Thickness = Thickness }.Convert(value, targetType, parameter, culture);
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
public class PackIconEntypoImageSourceConverter : PackIconImageSourceConverterBase<PackIconEntypoKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconEntypo { Kind = (PackIconEntypoKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing }, Transform = new ScaleTransform(1, -1) };
return new DrawingImage { Drawing = drawingGroup };
}
}
public class PackIconFontAwesomeImageSourceConverter : PackIconImageSourceConverterBase<PackIconFontAwesomeKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconFontAwesome { Kind = (PackIconFontAwesomeKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing }, Transform = new ScaleTransform(1, -1) };
return new DrawingImage { Drawing = drawingGroup };
}
}
public class PackIconMaterialImageSourceConverter : PackIconImageSourceConverterBase<PackIconMaterialKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconMaterial { Kind = (PackIconMaterialKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing } };
return new DrawingImage { Drawing = drawingGroup };
}
}
public class PackIconMaterialLightImageSourceConverter : PackIconImageSourceConverterBase<PackIconMaterialLightKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconMaterialLight { Kind = (PackIconMaterialLightKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing } };
return new DrawingImage { Drawing = drawingGroup };
}
}
public class PackIconModernImageSourceConverter : PackIconImageSourceConverterBase<PackIconModernKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconModern { Kind = (PackIconModernKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing } };
return new DrawingImage { Drawing = drawingGroup };
}
}
public class PackIconOcticonsImageSourceConverter : PackIconImageSourceConverterBase<PackIconOcticonsKind>
{
protected override ImageSource CreateImageSource(object value, Brush foregroundBrush, double penThickness)
{
var packIcon = new PackIconOcticons { Kind = (PackIconOcticonsKind)value };
var geometryDrawing = new GeometryDrawing
{
Geometry = Geometry.Parse(packIcon.Data),
Brush = foregroundBrush,
Pen = new Pen(foregroundBrush, penThickness)
};
var drawingGroup = new DrawingGroup { Children = { geometryDrawing } };
return new DrawingImage { Drawing = drawingGroup };
}
}
I have been successfully using the following XAML-only solution:
<ContentControl Margin="10,2" Background="Blue" Foreground="White"
Content="{iconPacks:Material Kind=GestureTap}"
ToolTip="This is a tooltip"
IsEnabled="{Binding ShouldThisBeEnabled}"
Visibility="{Binding ShouldThisBeVisible, Converter={StaticResource VisibilityFromBool}}" />
Related
I am using RowStyleSelector to color my Datagridrow based on the item. It works fine when the row are drawn, though it does not get triggered when the property changed on the item. The value of the item is shown in the datagrid on change thanks to inotifyPropertyChange.
ListViewFileList.RowStyleSelector= new ErrorStyleSelector();
enter code here
private class ErrorStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is myFile)
{
if ((item as myFile).ErrorStatus.IsErrorfile())
{
Style st = new Style(typeof(DataGridRow));
st.Setters.Add(new Setter(BackgroundProperty, Brushes.Red));
return st;}
}
}
I would like to trigger the RowStyleSelector on item change. Without using xaml.
EDIT
I ended up using IvalueConverter
Style st = new Style(typeof(DataGridRow));
DataTrigger tig = new DataTrigger()
{
Binding = new Binding(nameof(myFile.ErrorStatus))
{
Converter = new ConverterError(),
}
};
st.Triggers.Add(tig);
ListViewFileList.RowStyle = st;
private class ConverterError : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
TRCFile obj = value as myError;
if (obj!= null && othercondition)
{
Style st = new Style(typeof(DataGridRow));
return st;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The StyleSelector won't create a new style when a PropertyChanged event is raised.
You should make IsErrorfile() a public property and bind to it using a DataTrigger in the Style:
myFile myFile = item as myFile;
if(myFile != null)
{
Style st = new Style(typeof(DataGridRow));
DataTrigger dataTrigger = new DataTrigger()
{
Binding = new Binding("ErrorStatus.IsErrorfile"),
Value = true
};
dataTrigger.Setters.Add(new Setter(BackgroundProperty, Brushes.Red));
st.Triggers.Add(dataTrigger);
return st;
}
How do you make binding to a collection item in this case?
//Both collections have OnPropertyChanged("");
public ObservableCollection<Grid> ConnectorsGrids { get; set; }
public ObservableCollection<double> Connectors { get; set; }
//Coordinates collection
Connectors = new ObservableCollection<double>() { 10 , A - 20 };
ConnectorsGrids = new ObservableCollection<Grid>();
foreach (var e in Connectors)
{
ConnectorsGrids.Add(DrawConnector(new Thickness(e * YB1, 0, 0, 0)));
}
YB1 is the coefficient for the element's size. It is variable depending on the size of the screen
YB1 is not in any collection. Is a parameter in the class
DrawConnector() takes the margin as a parameter
I would like the margin to change depending on the size of the screen
At the moment, it calculates the output value and afterwards when changing the size of the screen, the changes remain
You can try this:
xaml:
<ItemsControl x:Name="PART_ItemsControl"
ItemsSource="{Binding Connectors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding Converter={local:ToGridConverter}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Converter:
sealed class ToGridConverter : MarkupExtension, IValueConverter{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
double e = (double)value;
return DrawConnector(new Thickness(e * YB1, 0, 0, 0));
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private ToGridConverter _converter;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
{
_converter = new ToGridConverter ();
}
return _converter;
}}
My view model has a PointCollection property like this:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private PointCollection points;
public PointCollection Points
{
get { return points; }
set
{
points = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Points)));
}
}
}
This is usually shown as a polyline:
<Polyline Points="{Binding Points}" Stroke="Black" StrokeThickness="2"/>
How would I efficiently show it as a collection of separate circles?
It may well be done by an ItemsControl with e.g. a Path element with an EllipseGeometry in its ItemTemplate, however that would involve a large number of UI elements, which may not perform well for a large number of Points in the PointsCollection.
A Binding Converter like shown below could convert an IEnumerable<Point> into a StreamGeometry that consists of a set of zero-length lines.
These could be drawn as circles by a Path with StrokeStartLineCap and StrokeEndLineCap set to Round.
public class LinePointsConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var geometry = new StreamGeometry();
var points = value as IEnumerable<Point>;
if (points != null && points.Any())
{
using (var sgc = geometry.Open())
{
foreach (var point in points)
{
sgc.BeginFigure(point, false, false);
sgc.LineTo(point, true, false);
}
}
}
return geometry;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
The Path would look like this:
<Path Data="{Binding Points, Converter={StaticResource LinePointsConverter}}"
Stroke="Black" StrokeThickness="5"
StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
I have the following XAML that uses a classes CurrencyImg property to look up a static resource at runtime, as there are a lot of these in a treeview, I don't want to have to load the same image 1000 times, I keep it in a resourcedictionary so it is only loaded once.
<Image Name="imgCurrency" Grid.Column="5" Margin="0,0,0,0" Source="{w:ImageStaticResource {Binding CurrencyImg}}" Height="22" VerticalAlignment="Top"/>
With a sample resource dictionary entry as
<ImageSource x:Key="..\Resources\Images\USD.ico">../Resources/Images/USD.ico</ImageSource>
The property is as follows
public string CurrencyImg
{
get
{
if (DisplayUSDRate)
{
return AppString.General.ImagePath + AppString.CurrencyId.USD + ".ico";
}
else
{
return AppString.General.ImagePath + curr + ".ico";
}
}
}
And the property action which does the work
public override bool DisplayUSDRate
{
get { return _customer.DisplayUSDRate; }
set
{
_customer.DisplayUSDRate = value;
OnPropertyChanged("CurrencyImg");
}
}
Now when this runs, the OnPropertyChanged("CurrencyImg") does nothing, and it's clear why. In the XAML I'm not directly binding to the CurrencyImg property, I'm using it as a parameter to my StaticResourceExtension class, so when I call OnPropertyChanged, it thinks there are no bound properties to update, so the XAML image does not update. I can understand this, but obviously this does not help me as what I need to happen is the following.
1) For the class to determine which currency image gets displayed for that row
2) For the images to come from a resource dictionary so they don't get loaded more than once or performance hugely suffers
3) For changes to the DisplayUSDRate property to then reflect by updating the flag to the appropriate image. This is the bit I can't figure out how to do with my StaticResourceExtension (code below)
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Citi.Rbcs.UI.Windows
{
public class ImageStaticResource : StaticResourceExtension
{
public Binding Binding { get; set; }
private static readonly DependencyProperty DummyProperty;
public ImageStaticResource()
{
}
public ImageStaticResource(Binding binding)
{
Binding = binding;
}
static ImageStaticResource()
{
DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof (Object),
typeof (DependencyObject),
new UIPropertyMetadata(null));
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (FrameworkElement)target.TargetObject;
Binding.Source = targetObject.DataContext;
var DummyDO = new DependencyObject();
BindingOperations.SetBinding(DummyDO, DummyProperty, Binding);
ResourceKey = DummyDO.GetValue(DummyProperty);
var resourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/Windows/Images.xaml")
};
var key = (string) ResourceKey;
if (!resourceDictionary.Contains(key)) ResourceKey = "Default";
return base.ProvideValue(serviceProvider);
}
}
}
You could write a very simple binding converter that converts image URI strings to cached ImageSource objects:
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var imageUrl = value as string;
var image = MemoryCache.Default.Get(imageUrl) as ImageSource;
if (image == null)
{
image = new BitmapImage(new Uri(imageUrl));
MemoryCache.Default.Set(imageUrl, image, new CacheItemPolicy());
}
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have a Collection of Model-objects in my ViewModel. I would like to be able to bind a TabControl to these and use a DataTemplate to extract the information from the Model-objects. When I try to do this I get the errormessage: Unable to cast object of type Model to object of type TabItem. After spending some time looking for a solution I found the following:
The Silverlight TabControl is
broken. Use a combination of ListBox
and ContentControl to mimic the
behaviour of a TabControl. (Means
that I have to skin the ListBox to
look like a TabControl)
TabControl does not override
PrepareContainerForItemOverride and
the solution is to make a
Converter. (Not so good because I
then need to specify the type of the
convertee in the Converter)
Anyone know any better solution?
XAML
<sdk:TabControl ItemsSource="{Binding Items, ElementName=MyControl}">
<sdk:TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</sdk:TabControl.ItemTemplate>
</sdk:TabControl>
C#
public ObservableCollection<Model> Items { get; set; }
public ViewModel()
Items = new ObservableCollection<Model>{
new Model { Name = "1"},
new Model { Name = "2"},
new Model { Name = "3"},
new Model { Name = "4"}
};
}
Suggested Converter:
public class TabConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<TabSource> source = value as List<TabSource>;
if (source != null)
{
List<TabItem> result = new List<TabItem>();
foreach (TabSource tab in source)
{
result.Add(new TabItem()
{
Header = tab.Header,
Content = tab.Content
});
}
return result;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Create converter
public class SourceToTabItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
var source = (IEnumerable)value;
if (source != null)
{
var controlTemplate = (ControlTemplate)parameter;
var tabItems = new List<TabItem>();
foreach (object item in source)
{
PropertyInfo[] propertyInfos = item.GetType().GetProperties();
//тут мы выбираем, то поле которое будет Header. Вы должны сами вводить это значение.
var propertyInfo = propertyInfos.First(x => x.Name == "name");
string headerText = null;
if (propertyInfo != null)
{
object propValue = propertyInfo.GetValue(item, null);
headerText = (propValue ?? string.Empty).ToString();
}
var tabItem = new TabItem
{
DataContext = item,
Header = headerText,
Content =
controlTemplate == null
? item
: new ContentControl { Template = controlTemplate }
};
tabItems.Add(tabItem);
}
return tabItems;
}
return null;
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// ConvertBack method is not supported
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("ConvertBack method is not supported");
}
Create ControlTemplate:
<ControlTemplate x:Key="MyTabItemContentTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=name}" />
</StackPanel>
</ControlTemplate>
And binding convert, controltemplate
<controls:TabControl x:Name="tabControl"
ItemsSource="{Binding ElementName=tabControl,
Path=DataContext,
Converter={StaticResource ConverterCollectionToTabItems},
ConverterParameter={StaticResource MyTabItemContentTemplate}}">
</controls:TabControl>
taken from the blog binding-tabcontrol