Colorpleth value converter - wpf

I need to display data on a WPF application colored in proportion to the measurement of the item's property value, like on the image below.
My question is how to provide the color/LinearGradientBrush (green-black-red) based on the relative value? Lowest values should return red(dish), middle values black/gray(ish) and highest values greed(ish), as in the image.
I started binding the background color of each panel to the item's value with a converter that can return some individual colors but I'd like to return a full range of possible LinearGradientBrushes based on the relative value.
Public Class ValueToColorConverter
Inherits MarkupExtension
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim v As Double = System.Convert.ToDecimal(value)
If v < 0 Then
Return Brushes.Red
ElseIf v < 0.05 Then
Return Brushes.Gray
Else
Return Brushes.Green
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New System.NotImplementedException()
End Function
Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object
Return Me
End Function
End Class

Related

Convert a SQL Image to WPF ImageBrush [duplicate]

I'm trying to bind a Byte array from my databse to a WPF Image.
My XAML:
<Window.Resources>
<local:BinaryImageConverter x:Key="imgConverter" />
</Window.Resources>
...
<Image Source="{Binding Path=ImageData, Converter={StaticResource imgConverter}}" />
I've modified code published by Ryan Cromwell for a value converter:
Class BinaryImageConverter
Implements IValueConverter
Private Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If value IsNot Nothing AndAlso TypeOf value Is Byte() Then
Dim bytes As Byte() = TryCast(value, Byte())
Dim stream As New MemoryStream(bytes)
Dim image As New BitmapImage()
image.BeginInit()
image.StreamSource = stream
image.EndInit()
Return image
End If
Return Nothing
End Function
Private Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New Exception("The method or operation is not implemented.")
End Function
End Class
The image.EndInit() line of the BinaryImageConverter's Convert() function throws this NotSupportedException:
"No imaging component suitable to
complete this operation was found."
InnerException: "Exception from
HRESULT: 0x88982F50"
I don't understand what I'm doing wrong. How can I get this working?
Update
It seems the problem was the bytes coming out of the database. There must have been a problem with the way I was putting them in.
See my working code below.
You can bind a byte[] to an Image.
Here a Sample:
Xaml:
<Image Source="{Binding UserImage}"/>
Code:
private byte[] userImage;
public byte[] UserImage
{
get { return userImage; }
set
{
if (value != userImage)
{
userImage = value;
OnPropertyChanged("UserImage");
}
}
}
Thanks for all your help. I've now got it working. I'm still not sure exactly what the problem was.
This is how I put images into my database…
Private Sub ButtonUpload_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim FileOpenStream As Stream = Nothing
Dim FileBox As New Microsoft.Win32.OpenFileDialog()
FileBox.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)
FileBox.Filter = "Pictures (*.jpg;*.jpeg;*.gif;*.png)|*.jpg;*.jpeg;*.gif;*.png|" & _
"All Files (*.*)|*.*"
FileBox.FilterIndex = 1
FileBox.Multiselect = False
Dim FileSelected As Nullable(Of Boolean) = FileBox.ShowDialog(Me)
If FileSelected IsNot Nothing AndAlso FileSelected.Value = True Then
Try
FileOpenStream = FileBox.OpenFile()
If (FileOpenStream IsNot Nothing) Then
Dim ByteArray As Byte()
Using br As New BinaryReader(FileOpenStream)
ByteArray = br.ReadBytes(FileOpenStream.Length)
End Using
Dim g As New ZackGraphic
g.Id = Guid.NewGuid
g.ImageData = ByteArray
g.FileSize = CInt(ByteArray.Length)
g.FileName = FileBox.FileName.Split("\").Last
g.FileExtension = "." + FileBox.FileName.Split(".").Last.ToLower
g.DateAdded = Now
Dim bmp As New BitmapImage
bmp.BeginInit()
bmp.StreamSource = New MemoryStream(ByteArray)
bmp.EndInit()
bmp.Freeze()
g.PixelWidth = bmp.PixelWidth
g.PixelHeight = bmp.PixelHeight
db.AddToZackGraphic(g)
db.SaveChanges()
End If
Catch Ex As Exception
MessageBox.Show("Cannot read file from disk. " & Ex.Message, "Add a New Image", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK)
Finally
If (FileOpenStream IsNot Nothing) Then
FileOpenStream.Close()
End If
End Try
End If
End Sub
This is my value converter used to bind a Byte array to an Image…
Class BinaryImageConverter
Implements IValueConverter
Private Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
If value IsNot Nothing AndAlso TypeOf value Is Byte() Then
Dim ByteArray As Byte() = TryCast(value, Byte())
Dim bmp As New BitmapImage()
bmp.BeginInit()
bmp.StreamSource = New MemoryStream(ByteArray)
bmp.EndInit()
Return bmp
End If
Return Nothing
End Function
Private Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New Exception("The method or operation is not implemented.")
End Function
End Class
This is my XAML that uses the converter display the image…
<Window xmlns:local="clr-namespace:MyProjectName" ... >
<Window.Resources>
<local:BinaryImageConverter x:Key="imgConverter" />
</Window.Resources>
...
<Image Source="{Binding Path=ImageData, Converter={StaticResource imgConverter}}" />
Try using this
Dim imageSource as ImageSource
Dim bitmapDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
imageSource = bitmapDecoder.Frames[0];
imageSource.Freeze();
Return imageSource
I believe this is actually a security permission issue. Try running with administrator privileges, and see if that works, and go from there.
EDIT: I disagree with the downvote and comment. Take a look at this link:
http://social.expression.microsoft.com/Forums/en-US/wpf/thread/617f6711-0373-44cc-b72c-aeae20f0f7a8/
This user had the exact same error, and it was caused by security settings. Therefore, I stand by my answer (which may not be the cause, but it is certainly worth a try)
My guess would be that the bytes are not a legitimate image format. I believe that error code corresponds to WINCODEC_ERR_COMPONENTNOTFOUND, which would be consistent with invalid bytes.
What format is the byte array supposed to be in? Can you save it to disk, and try to open it with another imaging program?

CellTemplateSelector for .NET 3.5 DataGrid is passed DataRowView and not cell contents

I am using the DataGrid that comes in the WpfToolkit for .NET 3.5. I am displaying DataTable. I want negative amounts to be red. The number of columns is variable so AutoGenerateColumns is True.
Following the advice here http://wpftutorial.net/DataGrid.html I have done the following. However whenever TemplateSelector is called item is a DataRowView and not cell contents. How can I reference the cell contents??
Public Class AmountDataGrid
Inherits Microsoft.Windows.Controls.DataGrid
Public Property CellTemplateSelector() As DataTemplateSelector
Get
Return DirectCast(GetValue(CellTemplateSelectorProperty), DataTemplateSelector)
End Get
Set(value As DataTemplateSelector)
SetValue(CellTemplateSelectorProperty, value)
End Set
End Property
Public Shared ReadOnly CellTemplateSelectorProperty As DependencyProperty = DependencyProperty.Register("Selector", GetType(DataTemplateSelector), GetType(AmountDataGrid), New FrameworkPropertyMetadata(Nothing))
Protected Overrides Sub OnAutoGeneratingColumn(e As Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs)
e.Cancel = True
Columns.Add(New Microsoft.Windows.Controls.DataGridTemplateColumn() With {.Header = e.Column.Header, .CellTemplateSelector = CellTemplateSelector})
End Sub
End Class
DataGrid in XAML
<l:AmountDataGrid x:Name="dgMonths" Style="{StaticResource MonthsView}" CellTemplateSelector="{StaticResource TemplateSelector}"/>
Selector
Public Class TemplateSelector
Inherits DataTemplateSelector
Private _defaultTemplate As DataTemplate
Public Property DefaultTemplate() As DataTemplate
Get
Return _defaultTemplate
End Get
Set(value As DataTemplate)
_defaultTemplate = value
End Set
End Property
Public Overrides Function SelectTemplate(item As Object, container As System.Windows.DependencyObject) As DataTemplate
Return Me.DefaultTemplate
End Function
End Class
Code that binds the DataTable
strSelect = "TRANSFORM Format(Sum(Items.amount),'#,##0.00') AS total SELECT Accounts.accCategory, Accounts.ID, Accounts.comment AS Account FROM Accounts INNER JOIN Items ON Accounts.ID = Items.accFrom WHERE (((Year([idate]))=2013) AND ((Items.category)<>3 Or (Items.category) Is Null) AND ((Accounts.accCategory)=6 OR (Accounts.accCategory)=7) AND ((Accounts.curr)=1)) GROUP BY Accounts.accCategory, Accounts.ID, Accounts.comment PIVOT Format(idate,'mmm') IN ('Jan','Feb','Mar','Apr', 'May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')"
dsmcmd = New OleDbDataAdapter(strSelect, cn)
dsmcmd.Fill(dsm, "Totals")
dgMonths.ItemsSource = dsm.Tables("Totals").DefaultView
Thanks
Andy
Figured it out.... rather complicated for something that should really be simple!
XAML
<Window.Resources>
<l:DGAmountConverter x:Key="DGAmountConverter"/>
<l:DGColourConverter x:Key="DGColourConverter"/>
<DataTemplate x:Key="ColourAmount">
<Grid>
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dg:DataGridCell}, Converter={StaticResource DGAmountConverter}}" Foreground="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dg:DataGridCell}, Converter={StaticResource DGColourConverter}}"/>
</Grid>
</DataTemplate>
<l:TemplateSelector x:Key="TemplateSelector" DefaultTemplate="{StaticResource ColourAmount}"/>
</Window.Resources>
....
<l:AmountDataGrid x:Name="dgMonths" Style="{StaticResource MonthsView}" CellTemplateSelector="{StaticResource TemplateSelector}"/>
Code behind
Public Class AmountDataGrid
Inherits Microsoft.Windows.Controls.DataGrid
Public Property CellTemplateSelector() As DataTemplateSelector
Get
Return DirectCast(GetValue(CellTemplateSelectorProperty), DataTemplateSelector)
End Get
Set(value As DataTemplateSelector)
SetValue(CellTemplateSelectorProperty, value)
End Set
End Property
Public Shared ReadOnly CellTemplateSelectorProperty As DependencyProperty = DependencyProperty.Register("Selector", GetType(DataTemplateSelector), GetType(AmountDataGrid), New FrameworkPropertyMetadata(Nothing))
Protected Overrides Sub OnAutoGeneratingColumn(e As Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs)
e.Cancel = True
Columns.Add(New Microsoft.Windows.Controls.DataGridTemplateColumn() With {.Header = e.Column.Header, .CellTemplateSelector = CellTemplateSelector})
End Sub
End Class
Public Class TemplateSelector
Inherits DataTemplateSelector
Private _defaultTemplate As DataTemplate
Public Property DefaultTemplate() As DataTemplate
Get
Return _defaultTemplate
End Get
Set(value As DataTemplate)
_defaultTemplate = value
End Set
End Property
Public Overrides Function SelectTemplate(item As Object, container As System.Windows.DependencyObject) As DataTemplate
Return Me.DefaultTemplate
End Function
End Class
Class DGAmountConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim cell As Microsoft.Windows.Controls.DataGridCell = TryCast(value, Microsoft.Windows.Controls.DataGridCell)
If cell IsNot Nothing Then
Dim colNum As Integer = cell.Column.DisplayIndex
Dim cp As ContentPresenter = TryCast(cell.Content, ContentPresenter)
Dim view As DataRowView = TryCast(cp.Content, DataRowView)
If view IsNot Nothing Then
Return TryCast(view.Row.ItemArray(colNum), Object)
End If
End If
Return DependencyProperty.UnsetValue
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Nothing
End Function
End Class
Class DGColourConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim cell As Microsoft.Windows.Controls.DataGridCell = TryCast(value, Microsoft.Windows.Controls.DataGridCell)
If cell IsNot Nothing Then
Dim colNum As Integer = cell.Column.DisplayIndex
Dim cp As ContentPresenter = TryCast(cell.Content, ContentPresenter)
Dim view As DataRowView = TryCast(cp.Content, DataRowView)
If view IsNot Nothing Then
Dim obj As Object = view.Row.ItemArray(colNum)
If obj Is Nothing OrElse Val(obj) >= 0 Then
Return New SolidColorBrush(Colors.White)
Else
Return New SolidColorBrush(Colors.Red)
End If
End If
End If
Return DependencyProperty.UnsetValue
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return Nothing
End Function
End Class

IMultiValueConverter + MultiBinding - Reverse Arithmatic Calculations on Data-Bound Values?

Apollogies for the weird title. I didnt know how else to put it.
What i have is 3 TextBoxes Data-Bound to some values.
What i need to do is have the value of 1 of the TextBoxes Automatically compute as a result of a calculation of the other 2 textboxes.
After some Google'ing, i found that using the IMultiValueConverter Interface should solve my problem. It does. But only 1 way.
For example:
TextBox 1 * TextBox 2 = TextBox 3
But the reverse is also true:
TextBox 3 / TextBox 2 = TextBox 1
The latter is what i'm having trouble with completing. No matter what i do, the reverse calculation wont stick.
I've Implemented 2 IMultiValueConverters, each for the 2 TextBoxes(since its 2 different calculations).
Converter 1:
Public Class SalaryConverter
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Dim salary As Double = 0
salary = (Math.Round(values(0) * (values(1) * 4)))
Return salary.ToString("C")
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Return Nothing
End Function
End Class
Converter 2:
Public Class RateConverter
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Dim rate As Double = 0
rate = ((values(0) / values(1)) / 4)
Return rate.ToString("C")
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Return Nothing
End Function
End Class
You'll see that the Return values for the ConvertBack methods is set to Nothing. This is because whenever i return the value parameter, i get an exception saying i cant convert from string to 1-dimensional array.
What am i doing wrong?
Any help is much appreciated.
Thanks in Advance.
P.S. I am fairly new to Converters. I only started playing around with it recently.
A multi value convert works as this :
the Convert method requires n input parameters and convert it into 1 result. In your case : TextBox 1 + TextBox 2 => TextBox 3
the ConvertBack is the reverse : it takes 1 input parameter and convert it into n results. In your case : TextBox 3 => TextBox 1 + TextBox 2
So your ConvertBack should return an array containing values of TextBox1 and TextBox2...
If you want to update only TextBox1 for exemple, you can do :
return new object[] { myTextBox1String, Binding.DoNothing };
EDIT : link to MSDN ConvertBack : http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.convertback.aspx

Wpf binding to a function

I've a created a simple scrollviewer (pnlDayScroller) and want to have a separate horizontal scrollbar (associated scroller) to do the horizontal scrolling. All works with the below code accept I need to bind the visibility of the associated scroller.
I can't simply bind this to the visibility property of the horizontal template part of the scroll viewer as I've set this to be always hidden. The only way I can think to do this is to bind the visibility of the associated scroller to a function such that
If associatedScroller.scrollableWidth > 0 then
associatedScroller.visibility = visibility.visible
else
associatedScroller.visibility = visibility.collapsed
end if
Is this possible to do and if so how do I do it?
Private Sub pnlDayScroller_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles pnlDayScroller.Loaded
Dim binViewport, binMax, binMin, binSChange, binLChange As Binding
Dim horizontalScrollBar As Primitives.ScrollBar = CType(pnlDayScroller.Template.FindName("PART_HorizontalScrollBar", pnlDayScroller), Primitives.ScrollBar)
binViewport = New Binding("ViewportSize")
binViewport.Mode = BindingMode.OneWay
binViewport.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.ViewportSizeProperty, binViewport)
binMax = New Binding("Maximum")
binMax.Mode = BindingMode.OneWay
binMax.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.MaximumProperty, binMax)
binMin = New Binding("Minimum")
binMin.Mode = BindingMode.OneWay
binMin.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.MinimumProperty, binMin)
binSChange = New Binding("SmallChange")
binSChange.Mode = BindingMode.OneWay
binSChange.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.SmallChangeProperty, binSChange)
binLChange = New Binding("LargeChange")
binLChange.Mode = BindingMode.OneWay
binLChange.Source = horizontalScrollBar
associatedScroller.SetBinding(Primitives.ScrollBar.LargeChangeProperty, binLChange)
End Sub
Private Sub associatedScroller_Scroll(ByVal sender As System.Object, ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) Handles associatedScroller.ValueChanged
pnlDayScroller.ScrollToHorizontalOffset(e.NewValue)
end sub
FOLLOW UP (thanks to JustABill) :
I've add this code into the pnlDayScroller sub above (I've discovered scrollableWidth is a property of scrollviewer not scrollbar, but the maximum property gives a result I can use instead)
binVisibility = New Binding("Maximum")
binVisibility.Mode = BindingMode.OneWay
binVisibility.Source = horizontalScrollBar
binVisibility.Converter = New ScrollableConverter
associatedScroller.SetBinding(Primitives.ScrollBar.VisibilityProperty, binVisibility)
and I've created this class
Public Class ScrollableConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Dim dblMaximum As Double
If targetType IsNot GetType(Visibility) Then
Throw New InvalidOperationException("The target must be a visibility")
Else
dblMaximum = CType(value, Double)
Debug.WriteLine("Value of double is " & dblMaximum)
If dblMaximum > 0 Then
Return Visibility.Visible
Else
Return Visibility.Collapsed
End If
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
And the problem is resolved.
You need a ValueConverter. Bind to the scrollableWidth property, and add your ValueConverter to the binding's Converter property. That example's in C#, but the concept's pretty simple, and I'm sure there's VB.Net examples around if you look.
The short form of what you need to do is:
Create a new class that implements IValueConverter (I think it's in System.ComponentModel).
Fill in the Convert method with your first code block, except use the "value" parameter instead of scrollableWidth and return the visibility.
Add an appropriate xmlns for your local classes.
Add a StaticResource of your new ValueConverter to your Window/UserControl/whatever.
Bind the Visibility property to the scrollableWidth property using this ValueConverter.

When binding with Wpf is there a way to use System.String funcntions without using converters?

When binding with Wpf is there a way to use System.String funcntions without using converters?
<TextBlock Text="({Binding Path=Text}).Trim()"/>
that's basically my desire.
I would use a converter.
Binding Xaml
<StackPanel>
<StackPanel.Resources>
<local:StringTrimmingConverter x:Key="trimmingConverter" />
<StackPanel.Resources>
<TextBlock Text="{Binding Path=Text, Converter={StaticResource trimmingConverter}}" />
</StackPanel>
StringTrimmingConverter.cs
using System;
using System.Windows.Data;
namespace WpfApplication1
{
[ValueConversion(typeof(string), typeof(string))]
public class StringTrimmingConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString().Trim();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
#endregion
}
}
And if VB StringTrimmingConverter.vb
Imports System.Globalization
Public Class StringTrimmingConverter
Implements IValueConverter
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Return value.ToString().Trim
End Function
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
Return value
End Function
End Class
I created an ultimate converter for all the functions in System.String, needs some improvement would love to hear from you, hope to update it in future, please accept:
VB:
<ValueConversion(GetType(String), GetType(Object))> _
Class StringFunctions : Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse String.IsNullOrEmpty(parameter) Then Return Nothing
Dim parameters As New List(Of String)(parameter.ToString.Split(":"c))
parameter = parameters(0)
parameters.RemoveAt(0)
If String.IsNullOrEmpty(parameter) Then Return value
Dim method = (From m In GetType(String).GetMethods _
Where m.Name = parameter _
AndAlso m.GetParameters.Count = parameters.Count).FirstOrDefault
If method Is Nothing Then Return value
Return method.Invoke(value, parameters.ToArray)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Return value.ToString()
End Function
End Class
C#: -converted by a tool, don't rely!
[ValueConversion(typeof(string), typeof(object))]
public class StringConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return null;
value = value.ToString();
if (String.IsNullOrEmpty(value as string)) return "";
if (parameter == null || !parameter is string || String.IsNullOrEmpty((string)parameter)) return value;
List<string> parameters = new List<string>(((string)parameter).Split(':'));
parameter = parameters[0];
parameters.RemoveAt(0);
var method = (from m in typeof(String).GetMethods()
where m.Name== parameter
&& m.GetParameters().Count()==parameters.Count
select m).FirstOrDefault();
if (method == null) return value;
return method.Invoke(value, parameters.ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
#endregion
}
Xaml:
<TextBox Text="{Binding Path=String, Converter={StaticResource StringConverter}, ConverterParameter=Trim:Argument:AnotherArgument}" />
Then, in the binding, when u use a converter u have an option to pass a parameter to the converter (Binding.ConverterParameter) pass all your parameters seperated with : (colon - you can change it in the String.Split delimiter parameter), while the first parameter is the function name, the function will count the extra parameters and try to pass it.
I still didn't work on the parameters addressing, it's a shallow function.
Would like to see your improvements and notes.
thanks.
Shimmy
You will need to use a converter as you want to transform the data your control is bound to. To avoid writing lots of converters simple transformations, you can use the Dynamic Language Runtime and write expressions in your favourite DLR scripting language (such as Python, Ruby, etc).
See my blog series for an example of how to achieve this. Part 3 talks specifically about ValueConverters.
I created an ultimate converter for all the functions in System.String, needs some improvement would love to hear from you, hope to update it in future, please accept:
<ValueConversion(GetType(String), GetType(String))> _
Class StringFunctions : Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse String.IsNullOrEmpty(parameter) Then Return Nothing
Dim parameters As New List(Of String)(parameter.ToString.Split(":"c))
parameter = parameters(0)
parameters.RemoveAt(0)
If String.IsNullOrEmpty(parameter) Then Return value
Dim method = (From m In GetType(String).GetMethods _
Where m.Name = parameter _
AndAlso m.GetParameters.Count = parameters.Count).FirstOrDefault
If method Is Nothing Then Return value
Return method.Invoke(value, parameters.ToArray)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotSupportedException
End Function
End Class
The in the binding, when u use a converter u have an option to pass a parameter to the converter (Binding.ConverterParameter) pass all your parameters seperated with : (colon - you can change it in the String.Split delimiter parameter), while the first parameter is the function name, the function will count the extra parameters and try to pass it.
I still didn't work on the parameters addressing, it's a shallow function.
Would like to see your improvements and notes.
thanks.
Shimmy
I know this post is old, but it is still the first one showing up when searching "WPF TextBox Binding Trim".
I don't have a VB answer, but please don't use a converter.
Reasons:
A converter means you have to add extra XAML code to all your XAML binding everytime. Having to always add extra code is just as bad in XAML as it in is C#/VB.
This will prevent you from typing a space if you have UpdateSourceTrigger=PropertyChanged set.
Use object oriented programming like it is supposed to be used. If you need a Trimmed TextBox, then create a child of TextBox called TrimmedTextBox and use that. http://www.wpfsharp.com/2014/05/15/a-simple-trimmedtextbox-for-wpf/
C#
using System.Windows.Controls;
namespace WpfSharp.UserControls
{
public class TrimmedTextBox : TextBox
{
public TrimmedTextBox()
{
LostFocus += TrimOnLostFocus;
}
void TrimOnLostFocus(object sender, System.Windows.RoutedEventArgs e)
{
var trimTextBox = sender as TrimmedTextBox;
if (trimTextBox != null)
trimTextBox.Text = trimTextBox.Text.Trim();
}
}
}
VB (I used a converter on my C# code)
Imports System.Windows.Controls
Namespace WpfSharp.UserControls
Public Class TrimmedTextBox
Inherits TextBox
Public Sub New()
AddHandler LostFocus, AddressOf TrimOnLostFocus
End Sub
Private Sub TrimOnLostFocus(sender As Object, e As System.Windows.RoutedEventArgs)
Dim trimTextBox = TryCast(sender, TrimmedTextBox)
If trimTextBox IsNot Nothing Then
trimTextBox.Text = trimTextBox.Text.Trim()
End If
End Sub
End Class
End Namespace
Hope this helps. Please feel free to use this object as if it were public domain.

Resources