WPF Dependency Property for Image source with IValueConverter - wpf

I intend to create Usercontrol with boolean dependency property called IsPowerOn, When I change it True the PowerOn image load to Image.source and when I set IsPowerOn to Fals, the PowerOff image load to Image.source.
Here is my UserControl:
<UserControl x:Class="...UcPower"
...
<UserControl.Resources>
<local:PowerBoolean2Image x:Key="PowerBoolean2Image"/>
</UserControl.Resources>
<Grid>
<Image x:Name="imgPower" Source="{Binding Source, Converter={StaticResource PowerBoolean2Image}, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UcPower}}}" />
</Grid>
And Code behind:
public static readonly DependencyProperty IsPowerOnProperty = DependencyProperty.Register("IsPowerOn", typeof(bool), typeof(UcPower),
new FrameworkPropertyMetadata(false) { BindsTwoWayByDefault = true });
public bool IsPowerOn
{
get
{
return (bool)GetValue(IsPowerOnProperty);
}
set
{
SetValue(IsPowerOnProperty, value);
}
}
And IValueConverter:
public class PowerBoolean2Image : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool))
{
return null;
}
if (value.Equals(true))
{
// Power On
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
else
{
// Power Off
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But it doesn't work I expect, whats the wrong with me?

You should bind to the IsPowerOn property:
<Image Source="{Binding IsPowerOn, ...}" />
instead of
<Image Source="{Binding Source, ...}" />
Besides that, the expression if (value.Equals(true)) looks rather strange. You could replace that by
if ((bool)value)
{
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"));
}
return new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));
or shorter:
return (bool)value
? new BitmapImage(new Uri("pack://application:,,,/Resources/Power-On.png"))
: new BitmapImage(new Uri("pack://application:,,,/Resources/Power-Off.png"));

When I use this code in IValueConverter I get Error: IOException: Cannot locate resource 'resources/power-on.png'. and cant see my form in design mode:
Uri("pack://application:,,,/Resources/Power-On.png")
But I can use Assembly name to solve the problem like this code:
Uri("pack://application:,,,/ReferencedAssembly;component/Resources/Power-On.png")

Related

I want to rearrange the ObservableCollection which is completed in nature

I am working on WPF MVVM.
I am fetching the records from database and
I have binded ObservableCollection to xaml Itemsource.
Fetch List Code :
private async void getKYCUserDetails()
{
var data = _DataService.Client;
var AllKYCUserList = await data.For<vwKYCUserDetails>().FindEntriesAsync();
lstTempKYCUserDetails= new ObservableCollection<vwKYCUserDetails>(AllKYCUserList);
}
I want to rearrange the ObservableCollection such that System must show clients whose details are complete in nature on top of the report so user can act as per JIT approach.
Xaml :
<syncfusion:SfDataGrid x:Name="dgKYCUser"
ItemsSource="{Binding Path = lstTempKYCUserDetails,Mode=TwoWay}"
SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
NavigationMode="Row"
SelectionMode="Multiple"
ColumnSizer="Auto"
HeaderRowHeight="30"
AllowFiltering="True"
AllowSorting="True"
AllowGrouping="True"
ShowGroupDropArea="True"
HeaderStyle="{StaticResource headerStyle}"
AllowResizingColumns="True"
AllowDraggingColumns="True"
AllowDrop="True"
AutoGenerateColumns="False"
Foreground="#3e4345"
AllowEditing="True"
FrozenColumnCount="2"
BorderBrush="Red"
Background="White"
>
I have multiple columns in sfDatagrid in that I am converting boolean value to image using Converter and show image.
e.g
<syncfusion:GridImageColumn
ImageHeight="20" Width="30" ShowHeaderToolTip="True"
MappingName="bMobileVerify" HeaderText="Mobile Verified"
ValueBinding="{Binding bMobileVerify, Converter={StaticResource ImageConverter}}"
/>
The ImageConverter Code :
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((Boolean)value == true)
{
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "check.png"), UriKind.Relative));
}
else if ((Boolean)value == false)
{
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "crosssqr.png"), UriKind.Relative));
}
return new BitmapImage(new Uri(string.Format(#"..\..\Images\{0}", "TransparentBackground.png"), UriKind.Relative));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I want the records which has most ticks should on the top likewise it should have arranged, I don't have any clue how to rearrange it.
you can use ICollectionView
ICollectionView view = CollectionViewSource.GetDefaultView(lstTempKYCUserDetails);
view.SortDescriptions.Add(new SortDescription("tickscounter", ListSortDirection.Descending));

Updating the value of another property when a DependencyProperty changes

I have a DependencyProperty in my UserControl with a property changed callback. The property works as expected and the callback works as expected.
public double CurrentFlow
{
get { return (double)GetValue(CurrentFlowProperty); }
set { SetValue(CurrentFlowProperty, value); }
}
public static readonly DependencyProperty CurrentFlowProperty = DependencyProperty.Register("CurrentFlow", typeof(double), typeof(MyUserControl), new PropertyMetadata(0.0, OnCurrentFlowPropertyChanged));
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("CurrentFlow changed.");
}
However, I have a TextBlock in my UserControl where I want to display CurrentFlow as a formatted string. Currently, I have the Text property of the TextBlock binded to CurrentFlow, and it works, but I'm not getting the format I need. (Too many numbers after the decimal.)
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />
Ideally, I'd like to have a property named CurrentFlowString that takes the value from CurrentFlow and formats it to what I want. For example: CurrentFlow.ToString("0.00");
What's the best way to go about this with DependencyProperties? I know how to do this with regular properties but I'm kinda stuck here.
Thanks!
If you want to have more flexibility than using StringFormat, you can also use a custom converter. For example,
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double d)
return $"{d:f2}";
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then add it to your UserControl.Resources, and use it in your Binding:
<UserControl.Resources>
<local:MyConverter x:Key="MyConverter" />
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding Path=CurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource MyConverter}}" />
</Grid>
Solution 2:
Based on your comment below, here's an alternative solution. First, create a new dependency property; for example, FormattedCurrentFlow:
public static readonly DependencyProperty FormattedCurrentFlowProperty = DependencyProperty.Register(
"FormattedCurrentFlow", typeof(string), typeof(MyControl), new PropertyMetadata(default(string)));
public string FormattedCurrentFlow
{
get { return (string)GetValue(FormattedCurrentFlowProperty); }
set { SetValue(FormattedCurrentFlowProperty, value); }
}
Since you already have a method to handle changes in CurrentFlow, update the new FormattedCurrentFlow when CurrentFlow changes:
private static void OnCurrentFlowPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var myControl = (MyControl)source;
myControl.FormattedCurrentFlow = $"{myControl.CurrentFlow:f2}";
}
The TextBox in the UserControl can now bind to FormattedCurrentFlow:
<TextBlock Text="{Binding Path=FormattedCurrentFlow, RelativeSource={RelativeSource AncestorType=UserControl}}" />

How do I set background image to a Canvas

Wpf Canvas Background image does not display selected image from local path
XAML Code
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding BGImage}"/>
</Canvas.Background>
</Canvas>
MVVM code
private String _BGImage = #"‪C:/Users/sam/Desktop/photo-5.jpg";
public String BGImage
{
get
{
return this._BGImage;
}
set
{
this._BGImage = value;
NotifyPropertyChanged("BGImage");
}
}
Why this image not display on canvas background
or you can try using a converter
<UserControl.Resources>
<local:StringToImageConverter x:Key="StringToImageConverter" />
</UserControl.Resources>
...
<Canvas x:Name="LayoutRoot" Margin="485,24,0,0" HorizontalAlignment="Left" Width="341" Height="506" VerticalAlignment="Top">
<Canvas.Background>
<ImageBrush ImageSource="{Binding Path=BGImage, Converter={StaticResource StringToImageConverter}}"/>
</Canvas.Background>
</Canvas>
and this is the converter
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.GetType() != typeof(string))
{
throw new InvalidOperationException("The value must be a string");
}
return new BitmapImage(new Uri((string)value));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
of course you would still need to check if the string is a valid URI
Your viewmodel code for BGImage should look something like this:
private ImageSource _BGImage = new BitmapImage(new Uri(#"C:\Users\sam\Desktop\photo-5.jpg", UriKind.Absolute))
public ImageSource BGImage
{
get { return _BGImage; }
set
{
_BGImage= value;
NotifyPropertyChanged("BGImage");
}
}
Well you need to have BGImage as BitmapImage rather than string
public BitmapImage BGImage
{
get
{
return new BitmapImage((new Uri(this._BGImage, UriKind.Absolute)));
}
}
If you are changing image dynamically then you have to raise property changed to notify UI

WPF/XAML: How to make all text upper case in TextBlock?

I want all characters in a TextBlock to be displayed in uppercase
<TextBlock Name="tbAbc"
FontSize="12"
TextAlignment="Center"
Text="Channel Name"
Foreground="{DynamicResource {x:Static r:RibbonSkinResources.RibbonGroupLabelFontColorBrushKey}}" />
The strings are taken through Binding. I don't want to make the strings uppercase in the dictionary itself.
Or use
Typography.Capitals="AllSmallCaps"
in your TextBlock definition.
See here: MSDN - Typography.Capitals
EDIT:
This does not work in Windows Phone 8.1, only in Windows 8.1 ...
Implement a custom converter.
using System.Globalization;
using System.Windows.Data;
// ...
public class StringToUpperConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is string )
{
return ((string)value).ToUpper();
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Then include that in your XAML as a resource:
<local:StringToUpperConverter x:Key="StringToUpperConverter"/>
And add it to your binding:
Converter={StaticResource StringToUpperConverter}
You can use an attached property like this:
public static class TextBlock
{
public static readonly DependencyProperty CharacterCasingProperty = DependencyProperty.RegisterAttached(
"CharacterCasing",
typeof(CharacterCasing),
typeof(TextBlock),
new FrameworkPropertyMetadata(
CharacterCasing.Normal,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.NotDataBindable,
OnCharacterCasingChanged));
private static readonly DependencyProperty TextProxyProperty = DependencyProperty.RegisterAttached(
"TextProxy",
typeof(string),
typeof(TextBlock),
new PropertyMetadata(default(string), OnTextProxyChanged));
private static readonly PropertyPath TextPropertyPath = new PropertyPath("Text");
public static void SetCharacterCasing(DependencyObject element, CharacterCasing value)
{
element.SetValue(CharacterCasingProperty, value);
}
public static CharacterCasing GetCharacterCasing(DependencyObject element)
{
return (CharacterCasing)element.GetValue(CharacterCasingProperty);
}
private static void OnCharacterCasingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.TextBlock textBlock)
{
if (BindingOperations.GetBinding(textBlock, TextProxyProperty) == null)
{
BindingOperations.SetBinding(
textBlock,
TextProxyProperty,
new Binding
{
Path = TextPropertyPath,
RelativeSource = RelativeSource.Self,
Mode = BindingMode.OneWay,
});
}
}
}
private static void OnTextProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(System.Windows.Controls.TextBlock.TextProperty, Format((string)e.NewValue, GetCharacterCasing(d)));
string Format(string text, CharacterCasing casing)
{
if (string.IsNullOrEmpty(text))
{
return text;
}
switch (casing)
{
case CharacterCasing.Normal:
return text;
case CharacterCasing.Lower:
return text.ToLower();
case CharacterCasing.Upper:
return text.ToUpper();
default:
throw new ArgumentOutOfRangeException(nameof(casing), casing, null);
}
}
}
}
Then usage in xaml will look like:
<StackPanel>
<TextBox x:Name="TextBox" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="abc" />
<TextBlock local:TextBlock.CharacterCasing="Upper" Text="{Binding ElementName=TextBox, Path=Text}" />
<Button local:TextBlock.CharacterCasing="Upper" Content="abc" />
<Button local:TextBlock.CharacterCasing="Upper" Content="{Binding ElementName=TextBox, Path=Text}" />
</StackPanel>
If it's not a big deal you could use TextBox instead of TextBlock like this:
<TextBox CharacterCasing="Upper" IsReadOnly="True" />
While there's already a great answer here that uses a converter, I'm providing an alternative implementation that simplifies the conversion to a single line (thanks to null coalescing), as well as making it a subclass of MarkupExtension so it's easier to use in XAML.
Here's the converter...
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace IntuoSoft.Wpf.Converters {
[ValueConversion(typeof(string), typeof(string))]
public class CapitalizationConverter : MarkupExtension, IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (value as string)?.ToUpper() ?? value; // If it's a string, call ToUpper(), otherwise, pass it through as-is.
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}
}
And here's how you use it (Note: This assumes the above namespace is prefixed with is in your XAML):
<TextBlock Text={Binding SomeValue, Converter={is:CapitalizationConverter}}" />
Because it's a MarkupExtension subclass, you can simply use it right where/when it's needed. No need to define it in the resources first.
I use a character casing value converter:
class CharacterCasingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var s = value as string;
if (s == null)
return value;
CharacterCasing casing;
if (!Enum.TryParse(parameter as string, out casing))
casing = CharacterCasing.Upper;
switch (casing)
{
case CharacterCasing.Lower:
return s.ToLower(culture);
case CharacterCasing.Upper:
return s.ToUpper(culture);
default:
return s;
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Is there a way to make this image binding more performant?

I am binding a list of 500 odd nicks to a list with a status image for each. The scrolling of the list is painfully slow, and so is flicking between tabs with different lists.
This is all caused by my recent change in which I added these images.
Is there a way to speed it up?
My bitmaps (very small 16*16) :
<BitmapImage x:Key="ActiveIcon" UriSource="/WPFClient;component/Images/active.png" />
<BitmapImage x:Key="IdleIcon" UriSource="/WPFClient;component/Images/idle.png" />
<BitmapImage x:Key="AwayIcon" UriSource="/WPFClient;component/Images/away.png" />
<BitmapImage x:Key="UnknownIcon" UriSource="/WPFClient;component/Images/unknown.png" />
My List :
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image Source="{Binding Status, Converter={StaticResource UserStatusToIconConverter}}" Height="16" Width="16" Margin="0,0,5,0" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My converter :
public class UserStatusToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string userStatus = value.ToString();
string iconName = "UnknownIcon";
switch (userStatus)
{
case "Active":
iconName = "ActiveIcon";
break;
case "Idle":
iconName = "IdleIcon";
break;
case "Away":
iconName = "AwayIcon";
break;
}
return iconName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
NOTE: The code also DOES NOT work as it currently stands, no image is actually displayed. However I assume thats a minor detail somewhere.
Try using VirtualizationStackPanel will give some improvements
<ListBox VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
</ListBox>
in framework 4.5 VirtualizingPanel is available and setting VirtualizingPanel.ScrollUnit="Item" will give good performance improvement
The easiest way to solve that is to change converter to:
public class UserStatusToIconConverter : IValueConverter
{
private static readonly Uri ActiveIcon = new Uri("pack://application:,,,/WPFClient;component/Images/active.png");
private static readonly Uri IdleIcon = new Uri("pack://application:,,,/WPFClient;component/Images/idle.png");
private static readonly Uri AwayIcon = new Uri("pack://application:,,,/WPFClient;component/Images/away.png");
private static readonly Uri UnknownIcon = new Uri("pack://application:,,,/WPFClient;component/Images/unknown.png");
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var userStatus = value.ToString();
switch (userStatus)
{
case "Active":
return ActiveIcon;
case "Idle":
return IdleIcon;
case "Away":
return AwayIcon;
default:
return UnknownIcon;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The slow-down is caused by improper binding.

Resources